From 81685a6ffd5446acc965c56d67438f6d83a9d5b2 Mon Sep 17 00:00:00 2001 From: Maic Stohr Date: Wed, 8 Apr 2026 17:51:08 +0200 Subject: [PATCH 1/2] feat(certificate): make key length and validity years configurable --- .../ebics/certificate/CertificateManager.java | 29 ++++++- .../certificate/CertificateManagerTest.java | 84 ++++++++++++++----- 2 files changed, 90 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/kopi/ebics/certificate/CertificateManager.java b/src/main/java/org/kopi/ebics/certificate/CertificateManager.java index ed887e16..e92d4656 100644 --- a/src/main/java/org/kopi/ebics/certificate/CertificateManager.java +++ b/src/main/java/org/kopi/ebics/certificate/CertificateManager.java @@ -42,6 +42,11 @@ */ public class CertificateManager { + private static final String KEY_LENGTH_PROPERTY = "ebics.key.length"; + private static final String CERTIFICATE_VALIDITY_YEARS_PROPERTY = "ebics.cert.validity.years"; + private static final int DEFAULT_KEY_LENGTH = X509Constants.EBICS_KEY_SIZE; + private static final int DEFAULT_CERTIFICATE_VALIDITY_YEARS = X509Constants.DEFAULT_DURATION / 365; + public CertificateManager(EbicsUser user) { this.user = user; generator = new X509Generator(); @@ -54,7 +59,7 @@ public CertificateManager(EbicsUser user) { */ public void create() throws GeneralSecurityException, IOException { Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.DAY_OF_YEAR, X509Constants.DEFAULT_DURATION); + calendar.add(Calendar.YEAR, resolveCertificateValidityYears()); createA005Certificate(new Date(calendar.getTimeInMillis())); createX002Certificate(new Date(calendar.getTimeInMillis())); @@ -82,7 +87,7 @@ private void setUserCertificates() { * @throws IOException */ public void createA005Certificate(Date end) throws GeneralSecurityException, IOException { - KeyPair keypair = KeyUtil.makeKeyPair(X509Constants.EBICS_KEY_SIZE); + KeyPair keypair = KeyUtil.makeKeyPair(resolveKeyLength()); a005Certificate = generator.generateA005Certificate(keypair, user.getDN(), new Date(), end); a005PrivateKey = keypair.getPrivate(); } @@ -100,7 +105,7 @@ X509Certificate getA005Certificate() { public void createX002Certificate(Date end) throws GeneralSecurityException, IOException { KeyPair keypair; - keypair = KeyUtil.makeKeyPair(X509Constants.EBICS_KEY_SIZE); + keypair = KeyUtil.makeKeyPair(resolveKeyLength()); x002Certificate = generator.generateX002Certificate(keypair, user.getDN(), new Date(), @@ -117,7 +122,7 @@ public void createX002Certificate(Date end) throws GeneralSecurityException, IOE public void createE002Certificate(Date end) throws GeneralSecurityException, IOException { KeyPair keypair; - keypair = KeyUtil.makeKeyPair(X509Constants.EBICS_KEY_SIZE); + keypair = KeyUtil.makeKeyPair(resolveKeyLength()); e002Certificate = generator.generateE002Certificate(keypair, user.getDN(), new Date(), @@ -220,4 +225,20 @@ public void writePKCS12Certificate(char[] password, OutputStream fos) private PrivateKey a005PrivateKey; private PrivateKey x002PrivateKey; private PrivateKey e002PrivateKey; + + private int resolveKeyLength() { + Integer configuredKeyLength = Integer.getInteger(KEY_LENGTH_PROPERTY); + if (configuredKeyLength == null || configuredKeyLength <= 0) { + return DEFAULT_KEY_LENGTH; + } + return configuredKeyLength; + } + + private int resolveCertificateValidityYears() { + Integer configuredYears = Integer.getInteger(CERTIFICATE_VALIDITY_YEARS_PROPERTY); + if (configuredYears == null || configuredYears <= 0) { + return DEFAULT_CERTIFICATE_VALIDITY_YEARS; + } + return configuredYears; + } } diff --git a/src/test/java/org/kopi/ebics/certificate/CertificateManagerTest.java b/src/test/java/org/kopi/ebics/certificate/CertificateManagerTest.java index b51d7c85..d2be9c1a 100644 --- a/src/test/java/org/kopi/ebics/certificate/CertificateManagerTest.java +++ b/src/test/java/org/kopi/ebics/certificate/CertificateManagerTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.security.GeneralSecurityException; @@ -9,6 +10,7 @@ import java.security.Security; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; +import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.Date; @@ -30,7 +32,69 @@ class CertificateManagerTest { @Test void createA005Certificate() throws GeneralSecurityException, IOException { - var user = new EbicsUser() { + var user = testUser(); + var manager = new CertificateManager(user); + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.DAY_OF_YEAR, X509Constants.DEFAULT_DURATION); + + manager.createA005Certificate(new Date(calendar.getTimeInMillis())); + + var cert = manager.getA005Certificate(); + + assertNotNull(cert); + + //System.out.println(cert); + + assertEquals(3, cert.getVersion(), "Certificate version must be 3 (V3)."); + String expectedDN = "CN=test-dn"; + assertEquals(expectedDN, cert.getIssuerX500Principal().getName(X500Principal.RFC2253)); + assertEquals(expectedDN, cert.getSubjectX500Principal().getName(X500Principal.RFC2253)); + assertEquals("SHA256WITHRSA", cert.getSigAlgName()); + } + + @Test + void createUsesConfiguredKeyLength() throws Exception { + String previousKeyLength = System.getProperty("ebics.key.length"); + System.setProperty("ebics.key.length", "3072"); + try { + var manager = new CertificateManager(testUser()); + manager.create(); + var cert = manager.getA005Certificate(); + assertNotNull(cert); + assertEquals(3072, ((RSAPublicKey) cert.getPublicKey()).getModulus().bitLength()); + } finally { + if (previousKeyLength == null) { + System.clearProperty("ebics.key.length"); + } else { + System.setProperty("ebics.key.length", previousKeyLength); + } + } + } + + @Test + void createUsesConfiguredCertificateValidityYears() throws Exception { + String previousValidityYears = System.getProperty("ebics.cert.validity.years"); + System.setProperty("ebics.cert.validity.years", "2"); + try { + var manager = new CertificateManager(testUser()); + manager.create(); + var cert = manager.getA005Certificate(); + assertNotNull(cert); + long validDays = ChronoUnit.DAYS.between( + cert.getNotBefore().toInstant(), + cert.getNotAfter().toInstant()); + assertTrue(validDays >= 730 && validDays <= 732); + } finally { + if (previousValidityYears == null) { + System.clearProperty("ebics.cert.validity.years"); + } else { + System.setProperty("ebics.cert.validity.years", previousValidityYears); + } + } + } + + private EbicsUser testUser() { + return new EbicsUser() { @Override public RSAPublicKey getA005PublicKey() { return null; @@ -136,24 +200,6 @@ public byte[] decrypt(byte[] encryptedKey, byte[] transactionKey) throws GeneralSecurityException, IOException, EbicsException { return new byte[0]; } - }; - var manager = new CertificateManager(user); - Calendar calendar = Calendar.getInstance(); - calendar.add(Calendar.DAY_OF_YEAR, X509Constants.DEFAULT_DURATION); - - manager.createA005Certificate(new Date(calendar.getTimeInMillis())); - - var cert = manager.getA005Certificate(); - - assertNotNull(cert); - - //System.out.println(cert); - - assertEquals(3, cert.getVersion(), "Certificate version must be 3 (V3)."); - String expectedDN = "CN=test-dn"; - assertEquals(expectedDN, cert.getIssuerX500Principal().getName(X500Principal.RFC2253)); - assertEquals(expectedDN, cert.getSubjectX500Principal().getName(X500Principal.RFC2253)); - assertEquals("SHA256WITHRSA", cert.getSigAlgName()); } } From 5bebe9df6d5b390a275b6cd5c3033e1d4ae9feec Mon Sep 17 00:00:00 2001 From: Maic Stohr Date: Wed, 8 Apr 2026 22:14:24 +0200 Subject: [PATCH 2/2] ci: skip dependency graph submission for fork PRs --- .github/workflows/maven.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 6031fcd7..fa7dfd95 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -8,7 +8,8 @@ on: jobs: build: - + permissions: + contents: write runs-on: ubuntu-latest steps: @@ -24,4 +25,5 @@ jobs: # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive - name: Update dependency graph + if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6