Skip to content

Commit d5b72e4

Browse files
authored
Merge pull request #22 from kolesa-team/sync-2025-01
CORE-3324 Синх с основым репозиторием
2 parents 63fc74d + f21dd42 commit d5b72e4

File tree

18 files changed

+242
-78
lines changed

18 files changed

+242
-78
lines changed

.github/workflows/create-docker-release.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,20 @@ jobs:
3636
- name: Build project
3737
run: ./gradlew -Pversion=$GITHUB_REF_NAME bootJar
3838
- name: Set up QEMU
39-
uses: docker/setup-qemu-action@v2
39+
uses: docker/setup-qemu-action@v3
4040
- name: Set up Docker Buildx
41-
uses: docker/setup-buildx-action@v2
41+
uses: docker/setup-buildx-action@v3
4242
- name: Login to DockerHub
4343
uses: docker/login-action@v2
4444
with:
4545
username: ${{ secrets.DOCKERHUB_USERNAME }}
4646
password: ${{ secrets.DOCKERHUB_TOKEN }}
4747
- name: Build and push
48-
uses: docker/build-push-action@v3
48+
uses: docker/build-push-action@v6
4949
with:
5050
push: true
5151
tags: malikzh/ncanode:latest,malikzh/ncanode:${{ github.ref_name }}
5252
build-args: artifact=build/libs/NCANode-${{ github.ref_name }}.jar
5353
context: ./
5454
file: ./Dockerfile
55+
platforms: linux/amd64,linux/arm64

.github/workflows/validate-openapi-spec.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ jobs:
1414
steps:
1515
- uses: actions/checkout@v3
1616
- name: Validate OpenAPI definition
17-
uses: char0n/swagger-editor-validate@v1
17+
uses: swaggerexpert/swagger-editor-validate@v1
1818
with:
1919
definition-file: openapi.yml

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
FROM amazoncorretto:17-alpine
2+
ENV JAVA_OPTS='-Xms128m -Xmx512m'
23
EXPOSE 14579
34
WORKDIR /app
45
ARG artifact=build/libs/NCANode.jar

README.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ https://profit.kz/news/56732/Otkritij-kod-Beeline-Hacktoberfest-v-Kazahstane/
7474

7575
Документацию можно найти на http://ncanode.kz
7676

77+
Swagger: https://v3.ncanode.kz/swagger-ui/
78+
7779
## Contributors
7880

7981
<a href="https://github.com/malikzh/NCANode/graphs/contributors">
@@ -86,33 +88,47 @@ https://profit.kz/news/56732/Otkritij-kod-Beeline-Hacktoberfest-v-Kazahstane/
8688

8789
## Важно!!!
8890

89-
По требованию АО «НИТ» | НУЦ РК. Библиотеки `kalkancrypt-0.6.jar` и `kalkancrypt_xmldsig-0.3.jar`
91+
По требованию АО «НИТ» | НУЦ РК. Библиотеки `kalkancrypt-*.jar`/`knca_provider_jce_kalkan-*.jar` и `kalkancrypt-xmldsig-*.jar`
9092
Были удалены из репозитория, поэтому для компиляции Вам необходимо подставить библиотеки
9193
из комплекта разработчика (SDK) в директорию `/lib`.
9294

9395
### Сборка проекта
9496

97+
Версия gradle: 7.2
98+
Версия java: 17
99+
95100
Для сборки проекта необходимо:
96101

97-
1. Подставить бибилиотеки kalkancrypt (Их можно запросить [тут](https://pki.gov.kz/developers/))
102+
1. Подставить библиотеки kalkancrypt (`knca_provider_jce_kalkan-*.jar` и `kalkancrypt-xmldsig-*.jar`) в директорию lib (Их можно запросить [тут](https://pki.gov.kz/developers/))
98103
2. `./gradlew bootJar` (для jar файла) или `./gradlew bootWar` (для war файла)
99104

100105

101106
Собранный проект будет лежать: `build/libs/NCANode.jar` или `build/libs/NCANode.war`
102107

103-
### Запуск в Docker
108+
### Запуск проекта без сборки
109+
110+
Проект запустить можно командой:
111+
112+
```bash
113+
$ ./gradlew bootRun
114+
```
115+
116+
### Запуск в Docker из готового образа
104117

105118
```bash
106119
docker volume create ncanode_cache
107120
docker run -p 14579:14579 -v ncanode_cache:/app/cache -d malikzh/ncanode
108121
```
109122

110-
### Запуск проекта без сборки
123+
### Запуск через Docker Compose
111124

112-
Проект запустить можно командой:
125+
Предварительно нужно собрать проект через gradle и сгенерировать jar файлы
113126

114127
```bash
115-
$ ./gradlew bootRun
128+
docker compose build // сборка образа
129+
docker compose up -d // запуск контейнера
130+
docker compose ps // проверка статуса контейнера
131+
docker compose stop // остановка контейнера
116132
```
117133

118134
### После запуска

build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ repositories {
2121
flatDir {
2222
dirs 'lib'
2323
}
24-
24+
mavenLocal()
2525
mavenCentral()
2626
}
2727

@@ -51,9 +51,9 @@ dependencies {
5151
annotationProcessor 'org.projectlombok:lombok:1.18.24'
5252

5353
// KalkanCrypt
54-
implementation name: 'kalkancrypt-0.7.2'
55-
implementation name: 'kalkancrypt_xmldsig-0.4'
56-
implementation 'org.apache.santuario:xmlsec:2.1.7'
54+
implementation name: 'knca_provider_jce_kalkan-0.7.5'
55+
implementation name: 'kalkancrypt-xmldsig-0.5'
56+
implementation 'org.apache.santuario:xmlsec:3.0.3'
5757

5858
// SOAP/WSSE
5959
implementation 'org.apache.ws.security:wss4j:1.6.19'

docker-compose.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
version: '3.7'
2+
3+
services:
4+
ncanode:
5+
image: ncanode
6+
restart: unless-stopped
7+
build:
8+
context: .
9+
volumes:
10+
- ncanode_cache:/app/cache
11+
ports:
12+
- "14579:14579"
13+
14+
volumes:
15+
ncanode_cache:

lib/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
kalkancrypt-*.jar
22
kalkancrypt_xmldsig-*.jar
3+
knca_provider_jce_kalkan*.jar

lib/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# README
22

3-
По требованию АО «НИТ» | НУЦ РК. Библиотеки `kalkancrypt-0.7.jar` и `kalkancrypt_xmldsig-0.4.jar`
3+
По требованию АО «НИТ» | НУЦ РК. Библиотеки `kalkancrypt-*.jar`/`knca_provider_jce_kalkan-*.jar` и `kalkancrypt-xmldsig-*.jar`
44
Были удалены из репозитория, поэтому для компиляции Вам необходимо сюда подставить библиотеки
55
из комплекта разработчика (SDK).

src/main/java/kz/ncanode/NCANode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public static void main(String[] args) {
1717
System.out.println(banner());
1818
SpringApplication.run(NCANode.class, args);
1919
}
20-
private static String banner() {
20+
public static String banner() {
2121
return """
2222
____ _____ ______ _ ____ _____ __ ______ \s
2323
|_ \\|_ _|.' ___ | / \\ |_ \\|_ _| | ] / ____ `.\s
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package kz.ncanode.configuration;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.web.servlet.config.annotation.CorsRegistry;
5+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6+
7+
@Configuration
8+
public class CorsConfiguration implements WebMvcConfigurer {
9+
@Override
10+
public void addCorsMappings(CorsRegistry registry) {
11+
registry.addMapping("/**").allowedMethods("*");
12+
}
13+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package kz.ncanode.controller;
2+
3+
import kz.ncanode.NCANode;
4+
import kz.ncanode.service.MaintenanceService;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.core.io.Resource;
8+
import org.springframework.http.MediaType;
9+
import org.springframework.stereotype.Controller;
10+
import org.springframework.util.FileCopyUtils;
11+
import org.springframework.web.bind.annotation.RequestMapping;
12+
import org.springframework.web.bind.annotation.ResponseBody;
13+
14+
import java.io.IOException;
15+
import java.io.InputStreamReader;
16+
import java.io.Reader;
17+
import java.io.UncheckedIOException;
18+
import java.nio.charset.StandardCharsets;
19+
20+
@Controller
21+
@RequiredArgsConstructor
22+
public class HomePageController {
23+
private final MaintenanceService maintenanceService;
24+
25+
@Value("classpath:home.html")
26+
private Resource homePage;
27+
@RequestMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
28+
@ResponseBody
29+
public String homePage() {
30+
return loadHtml()
31+
.replace(variable("VERSION"), maintenanceService.getNCANodeVersion())
32+
.replace(variable("BANNER"), NCANode.banner());
33+
}
34+
35+
private String loadHtml() {
36+
try (Reader reader = new InputStreamReader(homePage.getInputStream(), StandardCharsets.UTF_8)) {
37+
return FileCopyUtils.copyToString(reader);
38+
} catch (IOException e) {
39+
throw new UncheckedIOException(e);
40+
}
41+
}
42+
43+
private static String variable(String name) {
44+
return String.format("#{%s}", name);
45+
}
46+
}

src/main/java/kz/ncanode/service/CrlService.java

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -84,31 +84,22 @@ private void initializeDeltaScheduler() {
8484
* @return Статус проверки
8585
*/
8686
public CrlStatus verify(CertificateWrapper cert) {
87-
for (final String cacheDirectory : List.of(CRL_CACHE_DELTA_DIR_NAME, CRL_CACHE_FULL_DIR_NAME)) {
88-
// Догружаем CRL из сертификата
89-
for (URL crlUrl : cert.getCrlList()) {
90-
Util.findAllUrls(crlUrl.toString()).forEach(url -> {
91-
try {
92-
URL u = new URL(url);
93-
File crlFile = getCrlCacheFilePathFor(cacheDirectory, u);
94-
95-
if (!crlFile.exists()) {
96-
downloadCrl(cacheDirectory, u);
97-
}
98-
} catch (MalformedURLException e) {
99-
log.warn("Invalid CRL url: {}. Certificate: {}", crlUrl, cert.getSubjectX500Principal().toString());
100-
}
101-
});
102-
}
87+
if (!crlConfiguration.isEnabled()) {
88+
return CrlStatus.builder()
89+
.result(CrlResult.ACTIVE)
90+
.build();
91+
}
10392

93+
for (final String cacheDirectory : List.of(CRL_CACHE_DELTA_DIR_NAME, CRL_CACHE_FULL_DIR_NAME)) {
10494
// Проверяем в CRL
105-
for (var crlEntry : getLoadedCrlEntries(cacheDirectory).entrySet()) {
106-
if (crlEntry.getValue().isRevoked(cert.getX509Certificate())) {
95+
for (File crlFile : getCrlFiles(cacheDirectory)) {
96+
X509CRL crl = loadCrl(crlFile);
10797

108-
return Optional.ofNullable(crlEntry.getValue().getRevokedCertificate(cert.getX509Certificate()))
98+
if (crl.isRevoked(cert.getX509Certificate())) {
99+
return Optional.ofNullable(crl.getRevokedCertificate(cert.getX509Certificate()))
109100
.map( entry -> CrlStatus.builder()
110101
.result(CrlResult.REVOKED)
111-
.file(crlEntry.getKey())
102+
.file(crlFile.getName())
112103
.revocationDate(entry.getRevocationDate())
113104
.reason(Optional.ofNullable(entry.getRevocationReason()).map(CRLReason::toString).orElse(""))
114105
.build()
@@ -125,16 +116,11 @@ public CrlStatus verify(CertificateWrapper cert) {
125116
.build();
126117
}
127118

128-
public Map<String, X509CRL> getLoadedCrlEntries(String cacheDirName) {
129-
return getCrlFiles(cacheDirName).stream().collect(Collectors.toMap(File::getName, this::loadCrl));
130-
}
131-
132119
/**
133120
* Обновляет кэш CRL
134121
*
135122
* @param force Если true, то кэш будет обновлен в любом случае
136123
*/
137-
@CacheEvict("crls")
138124
public synchronized void updateCache(boolean force, CrlConfiguration crlConfiguration, String cacheDirectory) {
139125
synchronized (directoryService) {
140126
if (!crlConfiguration.isEnabled() || crlConfiguration.getTtl() <= 0) {
@@ -185,7 +171,6 @@ public synchronized void updateCache(boolean force, CrlConfiguration crlConfigur
185171
* @param file
186172
* @return
187173
*/
188-
@Cacheable(value = "crls", key = "#file.absolutePath")
189174
public X509CRL loadCrl(File file) {
190175
try (FileInputStream in = new FileInputStream(file)) {
191176
return (X509CRL) CertificateFactory.getInstance("X.509").generateCRL(in);
@@ -215,13 +200,15 @@ public void downloadCrl(String cacheDirName, URL url) {
215200
}
216201

217202
/**
218-
* Возвращает файл кэша для URL
203+
* Возвращает список CRL файлов в указанной директории
204+
*
219205
* @param cacheDirName
220-
* @param url
221206
* @return
222207
*/
223-
public File getCrlCacheFilePathFor(String cacheDirName, URL url) {
224-
return getCrlCacheFilePathFor(cacheDirName, Util.sha1(url.toString()) + CRL_FILE_EXTENSION);
208+
public List<File> getCrlFiles(String cacheDirName) {
209+
return Arrays.stream(Objects.requireNonNull(directoryService.getCachePathFor(cacheDirName).orElseThrow().listFiles()))
210+
.filter(file -> file.isFile() && file.canRead() && file.getName().endsWith(CRL_FILE_EXTENSION))
211+
.toList();
225212
}
226213

227214
private File download(String url, Path path) throws CrlException {
@@ -253,10 +240,4 @@ private File download(String url, Path path) throws CrlException {
253240
private File getCrlCacheFilePathFor(String cacheDirName, String fileName) {
254241
return new File(directoryService.getCachePathFor(cacheDirName).orElseThrow(), fileName);
255242
}
256-
257-
private List<File> getCrlFiles(String cacheDirName) {
258-
return Arrays.stream(Objects.requireNonNull(directoryService.getCachePathFor(cacheDirName).orElseThrow().listFiles()))
259-
.filter(file -> file.isFile() && file.canRead() && file.getName().endsWith(CRL_FILE_EXTENSION))
260-
.collect(Collectors.toList());
261-
}
262243
}

src/main/java/kz/ncanode/service/OcspService.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ public class OcspService {
4949
public List<OcspStatus> verify(CertificateWrapper cert, CertificateWrapper issuer) {
5050
List<OcspStatus> statuses = new ArrayList<>();
5151

52+
if (issuer == null) {
53+
statuses.add(OcspStatus.builder()
54+
.result(OcspResult.UNKOWN)
55+
.message("Cannot find root certificate in NCANode. Try add it using NCANODE_CA_URL variable.")
56+
.build()
57+
);
58+
59+
return statuses;
60+
}
61+
5262
for (Map.Entry<String, URL> entry : ocspConfiguration.getUrlList().entrySet()) {
5363
try {
5464
byte[] nonce = generateOcspNonce();

src/main/java/kz/ncanode/wrapper/CertificateWrapper.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,23 @@ public PublicKey getPublicKey() {
193193
return x509Certificate.getPublicKey();
194194
}
195195

196-
private Set<CertificateKeyUser> getKeyUser() {
196+
public List<String> getExtendedKeyUsage() {
197197
try {
198-
return getX509Certificate().getExtendedKeyUsage().stream()
199-
.map(CertificateKeyUser::fromOID)
200-
.filter(Optional::isPresent)
201-
.map(Optional::get)
202-
.collect(Collectors.toSet());
198+
return getX509Certificate().getExtendedKeyUsage();
203199
} catch (CertificateParsingException e) {
204200
log.error("Certificate key user extracting error", e);
205-
return Collections.emptySet();
201+
return Collections.emptyList();
206202
}
207203
}
208204

205+
private Set<CertificateKeyUser> getKeyUser() {
206+
return getExtendedKeyUsage().stream()
207+
.map(CertificateKeyUser::fromOID)
208+
.filter(Optional::isPresent)
209+
.map(Optional::get)
210+
.collect(Collectors.toSet());
211+
}
212+
209213
public static Optional<CertificateWrapper> fromBase64(final String encodedCert) {
210214
return fromBytes(Base64.getDecoder().decode(encodedCert.replaceAll("\\s", "")));
211215
}
@@ -259,7 +263,7 @@ private static Optional<CertificateSubject> createCertificateSubjectFromDn(Strin
259263
subjectBuilder.locality((String)rdn.getValue());
260264
} else if (rdn.getType().equalsIgnoreCase("S")) {
261265
subjectBuilder.state((String)rdn.getValue());
262-
} else if (rdn.getType().equalsIgnoreCase("E")) {
266+
} else if ((rdn.getType().equalsIgnoreCase("E")) || (rdn.getType().equalsIgnoreCase("EMAILADDRESS"))) {
263267
subjectBuilder.email((String)rdn.getValue());
264268
} else if (rdn.getType().equalsIgnoreCase("O")) {
265269
subjectBuilder.organization((String)rdn.getValue());
@@ -276,7 +280,7 @@ private static Optional<CertificateSubject> createCertificateSubjectFromDn(Strin
276280

277281
return Optional.of(subjectBuilder.build());
278282
} catch (InvalidNameException e) {
279-
log.warn("Distinguished name parseing error", e);
283+
log.warn("Distinguished name parsing error", e);
280284
return Optional.empty();
281285
}
282286
}

src/main/resources/application.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ ncanode:
1010
crl:
1111
enabled: ${NCANODE_CRL_ENABLED:true}
1212
ttl: ${NCANODE_CRL_TTL:1440}
13-
url: ${NCANODE_CRL_URL:https://crl.pki.gov.kz/nca_gost.crl https://crl.pki.gov.kz/nca_rsa.crl}
13+
url: ${NCANODE_CRL_URL:https://crl.pki.gov.kz/nca_gost.crl https://crl.pki.gov.kz/nca_rsa.crl https://crl.pki.gov.kz/nca_rsa_2022.crl https://crl.pki.gov.kz/nca_gost_2022.crl}
1414
delta:
15-
url: ${NCANODE_CRL_DELTA_URL:https://crl.pki.gov.kz/nca_d_gost.crl https://crl.pki.gov.kz/nca_d_rsa.crl}
15+
url: ${NCANODE_CRL_DELTA_URL:https://crl.pki.gov.kz/nca_d_gost.crl https://crl.pki.gov.kz/nca_d_rsa.crl https://crl.pki.gov.kz/nca_d_rsa_2022.crl https://crl.pki.gov.kz/nca_d_gost_2022.crl}
1616
ttl: ${NCANODE_CRL_DELTA_TTL:60}
1717
http-client:
1818
connectionTtl: ${NCANODE_HTTP_CLIENT_CONNECTION_TTL:10}

0 commit comments

Comments
 (0)