Skip to content

Commit

Permalink
Added method for setting serial number
Browse files Browse the repository at this point in the history
  • Loading branch information
tsaarni committed Jun 18, 2022
1 parent 65c3079 commit f9956ac
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 8 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
version=0.1.0
39 changes: 36 additions & 3 deletions lib/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
plugins {
id 'java-library'
id 'maven-publish'
id 'jacoco' // Adds jacocoTestReport task for coverage.
id 'maven-publish'
id 'signing'
}

repositories {
Expand All @@ -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 = '[email protected]'
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'
Expand All @@ -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
}
23 changes: 20 additions & 3 deletions lib/src/main/java/fi/protonode/certy/Credential.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public KeyPurposeId getValue() {
private List<ExtKeyUsage> extKeyUsages;
private Credential issuer;
private Boolean isCa;
private BigInteger serial;

// Generated attributes.
private KeyPair keyPair;
Expand Down Expand Up @@ -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.
*
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions lib/src/test/java/fi/protonode/certy/TestCredential.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.

Expand Down

0 comments on commit f9956ac

Please sign in to comment.