From f9956ac2a12dd259754ff262fba20c45f2f3cdf4 Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Sat, 18 Jun 2022 11:07:18 +0300 Subject: [PATCH] Added method for setting serial number --- README.md | 4 +- gradle.properties | 1 + lib/build.gradle | 39 +++++++++++++++++-- .../java/fi/protonode/certy/Credential.java | 23 +++++++++-- .../fi/protonode/certy/TestCredential.java | 15 +++++++ 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 gradle.properties diff --git a/README.md b/README.md index cdb4941..28d76fb 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ## Description -Certy gives you a simple Java API for creating X509 certificates inside your unit tests. -No more committing test certificates and keys into the repository! +Certy is a simple to use Java API for creating X509 certificates on demand in unit tests. +No more committing test certificates and keys in the repository! Certy is Java version of similar tool for command line and Golang: [certyaml](https://github.com/tsaarni/certyaml). diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..de55ab6 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +version=0.1.0 diff --git a/lib/build.gradle b/lib/build.gradle index ec24253..b7d6107 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -1,7 +1,8 @@ plugins { id 'java-library' - id 'maven-publish' id 'jacoco' // Adds jacocoTestReport task for coverage. + id 'maven-publish' + id 'signing' } repositories { @@ -22,19 +23,35 @@ tasks.named('test') { } java { + withJavadocJar() withSourcesJar() } publishing { publications { maven(MavenPublication) { - groupId = 'fi.protonode.certy' + groupId = 'fi.protonode' artifactId = 'certy' - version = '0.1' from components.java pom { + name = 'certy' + description = 'Java library for generating certificates for tests.' + url = 'https://github.com/tsaarni/certy' + developers { + developer { + id = 'tsaarni' + name = 'Tero Saarni' + email = 'tero.saarni@gmail.com' + organizationUrl = 'https://github.com/tsaarni/' + } + } + scm { + connection = 'scm:git:git://github.com:tsaarni/certy.git' + developerConnection = 'scm:git:ssh://github.com:tsaarni/certy.git' + url = 'https://github.com/tsaarni/certy' + } licenses { license { name = 'The Apache License, Version 2.0' @@ -44,4 +61,20 @@ publishing { } } } + repositories { + maven { + def releasesRepoUrl = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/' + def snapshotsRepoUrl = 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username = ossrhUsername + password = ossrhPassword + } + } + } +} + +signing { + useGpgCmd() + sign publishing.publications.maven } diff --git a/lib/src/main/java/fi/protonode/certy/Credential.java b/lib/src/main/java/fi/protonode/certy/Credential.java index 799087f..13e6e0b 100644 --- a/lib/src/main/java/fi/protonode/certy/Credential.java +++ b/lib/src/main/java/fi/protonode/certy/Credential.java @@ -129,6 +129,7 @@ public KeyPurposeId getValue() { private List extKeyUsages; private Credential issuer; private Boolean isCa; + private BigInteger serial; // Generated attributes. private KeyPair keyPair; @@ -297,6 +298,18 @@ public Credential isCa(Boolean val) { return this; } + /** + * Defines serial number. + * Default value is current time in milliseconds. + * + * @param val Value for serial number. + * @return The Credential itself. + */ + public Credential serial(BigInteger val) { + this.serial = val; + return this; + } + /** * (Re)generate certificate and private key with currently set values. * @@ -349,10 +362,9 @@ public Credential generate() .build(issuer.keyPair.getPrivate()); } - Instant now = Instant.now(); JcaX509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder( effectiveIssuer, - BigInteger.valueOf(now.toEpochMilli()), // Current time as serial number. + serial, effectiveNotBefore, effectiveNotAfter, subject, @@ -542,11 +554,16 @@ private void setDefaults() { keyUsages = Arrays.asList(KeyUsage.KEY_CERT_SIGN, KeyUsage.CRL_SIGN); } else if (keyType == KeyType.EC) { // https://github.com/openjdk/jdk/blob/0530f4e517be5d5b3ff10be8a0764e564f068c06/src/java.base/share/classes/sun/security/ssl/X509KeyManagerImpl.java#L604-L618 - keyUsages = Arrays.asList(KeyUsage.KEY_ENCIPHERMENT, KeyUsage.DIGITAL_SIGNATURE, KeyUsage.KEY_AGREEMENT); + keyUsages = Arrays.asList(KeyUsage.KEY_ENCIPHERMENT, KeyUsage.DIGITAL_SIGNATURE, + KeyUsage.KEY_AGREEMENT); } else { keyUsages = Arrays.asList(KeyUsage.KEY_ENCIPHERMENT, KeyUsage.DIGITAL_SIGNATURE); } } + + if (serial == null) { + serial = BigInteger.valueOf(Instant.now().toEpochMilli()); // Current time in milliseconds. + } } // Returns new key pair. diff --git a/lib/src/test/java/fi/protonode/certy/TestCredential.java b/lib/src/test/java/fi/protonode/certy/TestCredential.java index 1f9e130..e8955ec 100644 --- a/lib/src/test/java/fi/protonode/certy/TestCredential.java +++ b/lib/src/test/java/fi/protonode/certy/TestCredential.java @@ -32,6 +32,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; +import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyFactory; @@ -307,6 +308,20 @@ void testIntermediateCa() throws Exception { assertEquals(-1, endEntity.getX509Certificate().getBasicConstraints()); // CA:false } + @Test + void testSerialNumber() throws Exception { + BigInteger serial = BigInteger.valueOf(1234); + X509Certificate cert = new Credential().subject("CN=joe").serial(serial).getX509Certificate(); + assertEquals(serial, cert.getSerialNumber()); + + // Serial number should have unique value, even if not set explicitly. + X509Certificate certNoExplicitSerial1 = new Credential().subject("CN=joe").getX509Certificate(); + X509Certificate certNoExplicitSerial2 = new Credential().subject("CN=jen").getX509Certificate(); + assertNotEquals(BigInteger.valueOf(0), certNoExplicitSerial1.getSerialNumber()); + assertNotEquals(BigInteger.valueOf(0), certNoExplicitSerial2.getSerialNumber()); + assertNotEquals(certNoExplicitSerial1.getSerialNumber(), certNoExplicitSerial2.getSerialNumber()); + } + // Helper methods.