Skip to content

Commit ae67e21

Browse files
committed
verification fixes
1 parent aa3fdcc commit ae67e21

16 files changed

Lines changed: 338 additions & 192 deletions

.github/workflows/ci-cd.yml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,20 @@ jobs:
212212
cache-to: type=gha,mode=max
213213

214214
- name: Generate CHANGELOG.md
215-
uses: github-changelog-generator/github-changelog-generator-action@v2
216-
with:
217-
token: ${{ secrets.GITHUB_TOKEN }}
218-
output: CHANGELOG.md
215+
# Replaced external action (repo not found) with a local generation step to avoid prepare-time failure.
216+
run: |
217+
echo "# Changelog" > CHANGELOG.md
218+
echo "" >> CHANGELOG.md
219+
# Try to generate changelog since last tag; fall back to full history
220+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
221+
if [ -n "$LAST_TAG" ]; then
222+
RANGE="${LAST_TAG}..HEAD"
223+
else
224+
RANGE="HEAD"
225+
fi
226+
echo "Changelog generated from git commits in range: $RANGE" >> CHANGELOG.md
227+
echo "" >> CHANGELOG.md
228+
git --no-pager log --pretty=format:"* %s (%h) — %an" $RANGE >> CHANGELOG.md || true
219229
220230
- name: Create release notes file
221231
run: |

backend/src/main/java/knca/signer/controller/VerifierHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ public static class XmlValidationRequest {
111111
private String publicKey; // Optional base64 encoded public key
112112
private String caPem; // Optional base64 encoded CA certificate PEM
113113
@Builder.Default
114-
private boolean checkKalkanProvider = false;
114+
private boolean checkSignature = true; // Enable signature validation by default
115+
@Builder.Default
116+
private boolean checkKncaProvider = false; //old name was checkKalkanProvider
115117
@Builder.Default
116118
private boolean checkData = false;
117119
@Builder.Default

backend/src/main/java/knca/signer/example/Generator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static void main(String[] args) {
2222
// Load and register the KalkanProvider
2323
Provider realProvider = KalkanRegistry.loadRealKalkanProvider();
2424
String providerName = realProvider.getName();
25-
log.info("Registered provider: " + providerName);
25+
log.info("Registered provider: {}", providerName);
2626

2727
// Create configuration (simulating what would be loaded from YAML)
2828
ApplicationConfig.CertificateConfig config = new ApplicationConfig.CertificateConfig(

backend/src/main/java/knca/signer/example/Validator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ public void work(String xmlContent) {
4545
X509Certificate caCertificate = CertificateValidator.loadCACertificate(config.getCaCertPath());
4646

4747
// Create and run XML validator
48-
XmlValidator xmlValidator = new XmlValidator(caCertificate, (java.security.Provider) provider);
48+
XmlValidator xmlValidator = new XmlValidator(caCertificate, (java.security.Provider) provider, false);
4949
boolean valid = xmlValidator.validateXmlSignature(xmlContent);
5050

51-
log.info("Validation result: " + valid);
51+
log.info("Validation result: {}", valid);
5252
} catch (Exception e) {
5353
log.error("Validation failed: %s".formatted(e.getMessage()), e);
5454
}

backend/src/main/java/knca/signer/kalkan/KalkanConstants.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
*/
66
public class KalkanConstants {
77

8-
public static final String ROOT_SUBJECT_DN = "C=KZ, CN=НЕГІЗГІ КУӘЛАНДЫРУШЫ ОРТАЛЫҚ (RSA) TEST 2025";
9-
108
// GeneralName constants
119
public static class GeneralName {
1210
public static final int otherName = 0;

backend/src/main/java/knca/signer/service/CertificateDataPopulator.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ public class CertificateDataPopulator {
4444
private static final String[] EMAIL_DOMAINS = {
4545
"gmail.com", "mail.kz", "yandex.kz", "outlook.com"
4646
};
47+
private static final String[] KAZAKH_CA_NAMES = {
48+
"НЕГІЗГІ КУӘЛАНДЫРУШЫ ОРТАЛЫҚ (RSA) TEST 2025",
49+
"НЕГІЗГІ КУӘЛАНДЫРУШЫ ОРТАЛЫҚ (RSA) TEST 2026",
50+
"НЕГІЗГІ КУӘЛАНДЫРУШЫ ОРТАЛЫҚ (RSA) TEST 2027",
51+
"ҚАЗАҚСТАН РЕСПУБЛИКАСЫНЫҢ КУӘЛАНДЫРУШЫ ОРТАЛЫҒЫ TEST 2028",
52+
"ҰЛТТЫҚ КУӘЛАНДЫРУШЫ ОРТАЛЫҚ (RSA) TEST 2029",
53+
"БІРІККЕН КУӘЛАНДЫРУШЫ ОРТАЛЫҚ ҚАЗАҚСТАН TEST 2030"
54+
};
4755

4856
/**
4957
* Generate a random 12-digit IIN (Individual Identification Number).
@@ -68,7 +76,7 @@ public static String populateBIN() {
6876
* Generate a random Kazakh company name.
6977
*/
7078
public static String populateCompany() {
71-
return KAZAKH_COMPANIES[random.nextInt(KAZAKH_COMPANIES.length)];
79+
return KAZAKH_COMPANIES[random.nextInt(KAZAKH_COMPANIES.length)] + " (TEST)";
7280
}
7381

7482
/**
@@ -92,6 +100,14 @@ public static String populateEmail() {
92100
return "%s@%s".formatted(local, domain);
93101
}
94102

103+
/**
104+
* Generate subject DN for a CA certificate with random selection from available names.
105+
*/
106+
public static String populateCASubjectDN() {
107+
String caName = KAZAKH_CA_NAMES[random.nextInt(KAZAKH_CA_NAMES.length)];
108+
return "C=KZ, CN=%s, L=KNCA-SIGNER".formatted(caName);
109+
}
110+
95111
/**
96112
* Generate subject DN for an individual certificate.
97113
*/
@@ -102,7 +118,7 @@ public static String populateIndividualSubjectDN() {
102118
String fullName = givenName + " " + surname;
103119
String iin = populateIIN();
104120
String email = populateEmail();
105-
return "CN=%s, SURNAME=%s, SN=IIN%s, C=KZ, G=%s, emailAddress=%s".formatted(fullName, surname, iin, patronymic, email);
121+
return "CN=%s, SURNAME=%s, SN=IIN%s, C=KZ, L=KNCA-SIGNER, G=%s, emailAddress=%s".formatted(fullName, surname, iin, patronymic, email);
106122
}
107123

108124
/**
@@ -113,13 +129,13 @@ public static String populateLegalEntitySubjectDN() {
113129
String givenName = KAZAKH_GIVEN_NAMES[random.nextInt(KAZAKH_GIVEN_NAMES.length)];
114130
String patronymic = KAZAKH_PATRONYMICS[random.nextInt(KAZAKH_PATRONYMICS.length)];
115131
String fullName = givenName + " " + surname;
116-
String company = KAZAKH_COMPANIES[random.nextInt(KAZAKH_COMPANIES.length)];
132+
String company = KAZAKH_COMPANIES[random.nextInt(KAZAKH_COMPANIES.length)] + " (TEST)";
117133
String bin = populateBIN();
118134
String iin = populateIIN();
119135
String email = populateEmail();
120136
String businessCategory = BUSINESS_CATEGORY[random.nextInt(BUSINESS_CATEGORY.length)];
121137
String dc = COMPANY_ROLES[random.nextInt(COMPANY_ROLES.length)];
122-
return "CN=%s, SURNAME=%s, SN=IIN%s, C=KZ, O=%s, OU=BIN%s, BusinessCategory=%s, G=%s, DC=%s, emailAddress=%s".formatted(
138+
return "CN=%s, SURNAME=%s, SN=IIN%s, C=KZ, O=%s, L=KNCA-SIGNER, OU=BIN%s, BusinessCategory=%s, G=%s, DC=%s, emailAddress=%s".formatted(
123139
fullName, surname, iin, company, bin, businessCategory, patronymic, dc, email);
124140
}
125141

@@ -161,7 +177,7 @@ public static String populateLegalEntitySubjectDN(String company, String bin) {
161177
String email = populateEmail();
162178
String businessCategory = BUSINESS_CATEGORY[random.nextInt(BUSINESS_CATEGORY.length)];
163179
String dc = COMPANY_ROLES[random.nextInt(COMPANY_ROLES.length)];
164-
return "CN=%s, SURNAME=%s, SN=IIN%s, C=KZ, O=%s, OU=BIN%s, BusinessCategory=%s, G=%s, DC=%s, emailAddress=%s".formatted(
180+
return "CN=%s, SURNAME=%s, SN=IIN%s, C=KZ, O=%s, L=KNCA-SIGNER, OU=BIN%s, BusinessCategory=%s, G=%s, DC=%s, emailAddress=%s".formatted(
165181
fullName, surname, iin, company, bin, businessCategory, patronymic, dc, email);
166182
}
167183

backend/src/main/java/knca/signer/service/CertificateGenerator.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import java.util.*;
2323
import java.util.stream.Stream;
2424

25-
import static knca.signer.kalkan.KalkanConstants.ROOT_SUBJECT_DN;
25+
2626

2727
/**
2828
* Instance-based certificate generator that uses dependency injection.
@@ -34,7 +34,7 @@ public class CertificateGenerator {
3434

3535
private static final String DEFAULT_CA_ALIAS = "ca";
3636

37-
private final java.security.Provider provider;
37+
private final Provider provider;
3838
private final ApplicationConfig.CertificateConfig config;
3939
private final CertificateStorage registry;
4040

@@ -92,11 +92,11 @@ private CertificateResult generateCACertificateInternal(String alias) throws Exc
9292
X509Certificate rootCert = generateRootCA(caKeyPair);
9393

9494
// Always save CA certificate files
95-
saveCertificate(rootCert, config.getCertsPath() + alias + ".crt");
96-
saveCertificate(rootCert, config.getCertsPath() + alias + ".pem");
95+
saveCertificate(rootCert, "%s%s.crt".formatted(config.getCertsPath(), alias));
96+
saveCertificate(rootCert, "%s%s.pem".formatted(config.getCertsPath(), alias));
9797

9898
// Save CA private key in PKCS8 format
99-
savePrivateKey(caKeyPair.getPrivate(), config.getCertsPath() + alias + ".key");
99+
savePrivateKey(caKeyPair.getPrivate(), "%s%s.key".formatted(config.getCertsPath(), alias));
100100

101101
return new CertificateResult(caKeyPair, rootCert);
102102
}
@@ -131,11 +131,11 @@ public void generateUserCertificate(KeyPair caKeyPair, X509Certificate rootCert)
131131
rootCert, userSubjectDN, email, iin, bin);
132132

133133
// Save user certificate
134-
saveCertificate(userCert, config.getCertsPath() + "user.crt");
135-
saveCertificate(userCert, config.getCertsPath() + "user.pem");
134+
saveCertificate(userCert, "%suser.crt".formatted(config.getCertsPath()));
135+
saveCertificate(userCert, "%suser.pem".formatted(config.getCertsPath()));
136136

137137
// Save user private key in PKCS8 format
138-
savePrivateKey(userKeyPair.getPrivate(), config.getCertsPath() + "user.key");
138+
savePrivateKey(userKeyPair.getPrivate(), "%suser.key".formatted(config.getCertsPath()));
139139

140140
// Create keystores
141141
KeyStoreManager.createPKCS12Keystore(userKeyPair.getPrivate(), userCert, rootCert,
@@ -594,8 +594,9 @@ private X509Certificate generateRootCA(KeyPair keyPair) throws Exception {
594594
}
595595
tbsManager.setSerialNumber(serNum);
596596
tbsManager.setSignature(config.getSignatureAlgorithm());
597-
tbsManager.setIssuer(ROOT_SUBJECT_DN);
598-
tbsManager.setSubject(ROOT_SUBJECT_DN);
597+
String caSubjectDN = CertificateDataPopulator.populateCASubjectDN();
598+
tbsManager.setIssuer(caSubjectDN);
599+
tbsManager.setSubject(caSubjectDN);
599600
tbsManager.setSubjectPublicKeyInfo(keyPair.getPublic());
600601

601602
// Validity period

backend/src/main/java/knca/signer/service/CertificateService.java

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
import lombok.*;
77
import lombok.extern.slf4j.Slf4j;
88

9+
import javax.xml.crypto.MarshalException;
10+
import javax.xml.crypto.dsig.XMLSignatureException;
911
import java.nio.charset.StandardCharsets;
12+
import java.security.InvalidAlgorithmParameterException;
13+
import java.security.NoSuchAlgorithmException;
1014
import java.security.PrivateKey;
1115
import java.security.Signature;
1216
import java.security.cert.X509Certificate;
@@ -78,22 +82,39 @@ public ValidationResult validateXmlSignature(XmlValidationRequest request) throw
7882
public String signXml(String xmlData, String certAlias) throws Exception {
7983
if ("file".equals(config.getStorageMode())) {
8084
// File mode: use PEM files
81-
String certPemPath = config.getCertsPath() + certAlias + ".crt";
82-
String keyPemPath = config.getCertsPath() + certAlias + ".key";
85+
String certPemPath = "%s%s.crt".formatted(config.getCertsPath(), certAlias);
86+
String keyPemPath = "%s%s.key".formatted(config.getCertsPath(), certAlias);
8387
return signXmlWithPemFiles(xmlData, certPemPath, keyPemPath);
8488
} else {
8589
// In-memory mode: use keystore lookup
8690
X509Certificate certificate = getCertificate(certAlias);
8791
PrivateKey privateKey = getPrivateKey(certAlias);
8892

89-
// Create certificate chain (in real implementation, this might include intermediate CAs)
90-
List<X509Certificate> certificateChain = List.of(certificate);
93+
// Create certificate chain including CA certificate
94+
List<X509Certificate> certificateChain;
95+
if (storage.hasCACertificate(certAlias)) {
96+
// CA certificate - self-signed
97+
certificateChain = List.of(certificate);
98+
} else {
99+
// User or legal certificate - include CA certificate
100+
CertificateData certData = storage.getUserCertificate(certAlias).orElse(null);
101+
if (certData == null) {
102+
certData = storage.getLegalCertificate(certAlias).orElse(null);
103+
}
104+
if (certData != null) {
105+
String caId = certData.getCaId();
106+
X509Certificate caCert = storage.getCACertificate(caId).orElseThrow().getCertificate();
107+
certificateChain = List.of(certificate, caCert);
108+
} else {
109+
certificateChain = List.of(certificate);
110+
}
111+
}
91112

92113
SigningEntity signingEntity = new SigningEntity(privateKey, certificateChain);
93114
try {
94115
return XmlSigningUtil.createXmlSignature(signingEntity, xmlData);
95-
} catch (javax.xml.crypto.MarshalException | javax.xml.crypto.dsig.XMLSignatureException |
96-
java.security.NoSuchAlgorithmException | java.security.InvalidAlgorithmParameterException e) {
116+
} catch (MarshalException | XMLSignatureException |
117+
NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
97118
throw new Exception("XML signing failed: " + e.getMessage(), e);
98119
}
99120
}
@@ -116,8 +137,8 @@ public String signXmlWithPemFiles(String xmlData, String certPemPath, String key
116137
SigningEntity signingEntity = new SigningEntity(pemData.privateKey, certificateChain);
117138
try {
118139
return XmlSigningUtil.createXmlSignature(signingEntity, xmlData);
119-
} catch (javax.xml.crypto.MarshalException | javax.xml.crypto.dsig.XMLSignatureException |
120-
java.security.NoSuchAlgorithmException | java.security.InvalidAlgorithmParameterException e) {
140+
} catch (MarshalException | XMLSignatureException |
141+
NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
121142
throw new Exception("XML signing failed: " + e.getMessage(), e);
122143
}
123144
}
@@ -154,7 +175,7 @@ public CertificateDownloadData downloadCertificate(String alias, String format)
154175
var caResult = storage.getCACertificate(alias).orElseThrow();
155176
caCert = cert; // Self-signed CA
156177
privateKey = caResult.getKeyPair().getPrivate();
157-
filename = alias + "." + format;
178+
filename = "%s.%s".formatted(alias, format);
158179
} else {
159180
// User or legal certificate - need to find CA cert and private key
160181
CertificateData certData = storage.getUserCertificate(alias).orElse(null);
@@ -167,7 +188,7 @@ public CertificateDownloadData downloadCertificate(String alias, String format)
167188
privateKey = storage.getUserKey(alias)
168189
.orElseGet(() -> storage.getLegalKey(alias).orElse(null))
169190
.getPrivate();
170-
filename = alias + "." + format;
191+
filename = "%s.%s".formatted(alias, format);
171192
} else {
172193
log.warn("Certificate not found: {}", alias);
173194
return null;

backend/src/main/java/knca/signer/service/CertificateStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public Map<String, CertificateData> getCertificates() {
5252
Map<String, CertificateData> certs = new HashMap<>();
5353
// Add all CA certificates
5454
storage.getCaCertificates().forEach((alias, result) ->
55-
certs.put("ca-" + alias, new CertificateData(null, null, null, alias, result.getCertificate())));
55+
certs.put("ca-%s".formatted(alias), new CertificateData(null, null, null, alias, result.getCertificate())));
5656
// Add all user certificates
5757
storage.getUserCertificates().forEach((alias, data) -> certs.put("user-" + alias, data));
5858
// Add all legal certificates

0 commit comments

Comments
 (0)