Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## Next

### Added
- Support for Ed25519 keys as verification material in create and update DID logs command.
- Support for Ed25519 for signing verifying proof of possession JWTs.

### Changed

Expand Down
47 changes: 28 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,21 @@ Usage: didtoolbox [options] [command] [command options]

Options:
--assert, -a
One or more assertion method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing EC P-256
public/verifying key
One or more assertion method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing an P-256 or
Ed25519 public/verifying key
--auth, -t
One or more authentication method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing EC
P-256 public/verifying key
--force-overwrite, -f
One or more authentication method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing P-256
or Ed25519 public/verifying key
--cryptographic-algorithm, -c
Specify which type of keys to generate as assertion and authorization keys when none are provided through other means. Available are
P-256 and Ed25519
Default: P-256
Possible Values: [Ed25519, P-256]
--force, -f
Overwrite existing PEM key files, if any
Default: false
--generate-next-verifying-key, -gw
Generates a new ed25519 key pair to be used as the next signing key. The generated key pair is stored in the `.didtoolbox` directory.
Generates a new Ed25519 key pair to be used as the next signing key. The generated key pair is stored in the `.didtoolbox` directory.
CAUTION: using `-f` can override existing keys in the directory, make sure to back them up. Cannot be used together with the generate
verifying key next flag ('-gv')
Default: false
Expand Down Expand Up @@ -126,10 +131,10 @@ Usage: didtoolbox [options] [command] [command options]
The ed25519 private key file required for signing a DID log entry or a PoP JWT. In PEM Format. This CLI parameter cannot be used in
conjunction with any of --jks-* or --primus-* CLI parameters
--verifying-key-files, -v
One or more ed25519 public key file(s) for the DID Document’s verification method. In PEM format.
One or more Ed25519 public key file(s) for the DID Document’s verification method. In PEM format.
Default: []
--verifying-key-files-next, -w
One or more ed25519 public key file(s) to be used as 'pre-rotation' keys. In PEM format. Using the CLI option activates 'key
One or more Ed25519 public key file(s) to be used as 'pre-rotation' keys. In PEM format. Using the CLI option activates 'key
pre-rotation'. Analogously, deactivating 'key pre-rotation' goes simply by omitting this option altogether
Default: []

Expand All @@ -143,14 +148,19 @@ Usage: didtoolbox [options] [command] [command options]

Options:
--assert, -a
One or more assertion method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing EC P-256
public/verifying key
One or more assertion method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing an P-256 or
Ed25519 public/verifying key
--auth, -t
One or more authentication method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing EC
P-256 public/verifying key
One or more authentication method parameter(s) - each parameter consists of a (comma-separated) key name and a PEM file containing P-256
or Ed25519 public/verifying key
--cryptographic-algorithm, -c
Specify which type of keys to generate as assertion and authorization keys when none are provided through other means. Available are
P-256 and Ed25519
Default: P-256
Possible Values: [Ed25519, P-256]
* --did-log-file, -d
The file containing a valid DID log to update
--force-overwrite, -f
--force, -f
Overwrite existing PEM key files, if any
Default: false
--generate-new-verifying-key, -gv
Expand All @@ -159,7 +169,7 @@ Usage: didtoolbox [options] [command] [command options]
be used together with the generate update key next flag ('-gw)
Default: false
--generate-next-verifying-key, -gw
Generates a new ed25519 key pair to be used as the next signing key. The generated key pair is stored in the `.didtoolbox` directory.
Generates a new Ed25519 key pair to be used as the next signing key. The generated key pair is stored in the `.didtoolbox` directory.
CAUTION: using `-f` can override existing keys in the directory, make sure to back them up. Cannot be used together with the generate
verifying key next flag ('-gv')
Default: false
Expand Down Expand Up @@ -190,10 +200,10 @@ Usage: didtoolbox [options] [command] [command options]
The ed25519 private key file required for signing a DID log entry or a PoP JWT. In PEM Format. This CLI parameter cannot be used in
conjunction with any of --jks-* or --primus-* CLI parameters
--verifying-key-files, -v
One or more ed25519 public key file(s) for the DID Document’s verification method. In PEM format.
One or more Ed25519 public key file(s) for the DID Document’s verification method. In PEM format.
Default: []
--verifying-key-files-next, -w
One or more ed25519 public key file(s) to be used as 'pre-rotation' keys. In PEM format. Using the CLI option activates 'key
One or more Ed25519 public key file(s) to be used as 'pre-rotation' keys. In PEM format. Using the CLI option activates 'key
pre-rotation'. Analogously, deactivating 'key pre-rotation' goes simply by omitting this option altogether
Default: []

Expand Down Expand Up @@ -260,7 +270,7 @@ Usage: didtoolbox [options] [command] [command options]
An optional password required for recovering the key pair (stored in Securosys Primus (HSM) Keystore). This CLI parameter should always
be used exclusively alongside all the other --primus-* CLI parameters, related to Securosys Primus (HSM)
--signing-key-file, -s
An EC P-256 private key file matching the specified key within the DID log
A P-256 private key file matching the specified key within the DID log

verify-pop Verifies the validity of the provided proof of possession JWT.
Usage: verify-pop [options]
Expand All @@ -274,9 +284,8 @@ Usage: didtoolbox [options] [command] [command options]
JWT to be verified
* --nonce, -n
Text representation of the possession to be included in the proof

$ java -jar didtoolbox.jar -V

$ java -jar didtoolbox.jar -V
didtoolbox 2.1.0
```

Expand Down
25 changes: 16 additions & 9 deletions THIRD-PARTY-LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ This is the list of all third-party dependencies grouped by their license type.
## Apache License, Version 2.0:

* **Jackson-annotations** (com.fasterxml.jackson.core:jackson-annotations:2.22 - https://github.com/FasterXML/jackson)
* **Jackson-core** (com.fasterxml.jackson.core:jackson-core:2.22.0 - https://github.com/FasterXML/jackson-core)
* **jackson-databind** (com.fasterxml.jackson.core:jackson-databind:2.22.0 - https://github.com/FasterXML/jackson)
* **Jackson-core** (com.fasterxml.jackson.core:jackson-core:2.22.1 - https://github.com/FasterXML/jackson-core)
* **jackson-databind** (com.fasterxml.jackson.core:jackson-databind:2.22.1 - https://github.com/FasterXML/jackson)
* **FindBugs-jsr305** (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)
* **Gson** (com.google.code.gson:gson:2.14.0 - https://github.com/google/gson)
* **Tink Cryptography API** (com.google.crypto.tink:tink:1.22.0 - http://github.com/tink-crypto/tink-java)
* **error-prone annotations** (com.google.errorprone:error_prone_annotations:2.48.0 - https://errorprone.info/error_prone_annotations)
* **Nimbus JOSE+JWT** (com.nimbusds:nimbus-jose-jwt:10.9.1 - https://bitbucket.org/connect2id/nimbus-jose-jwt)
* **Byte Buddy (without dependencies)** (net.bytebuddy:byte-buddy:1.17.7 - https://bytebuddy.net/byte-buddy)
* **Byte Buddy agent** (net.bytebuddy:byte-buddy-agent:1.17.7 - https://bytebuddy.net/byte-buddy-agent)
* **Java Native Access** (net.java.dev.jna:jna:5.19.1 - https://github.com/java-native-access/jna)
* **org.apiguardian:apiguardian-api** (org.apiguardian:apiguardian-api:1.1.2 - https://github.com/apiguardian-team/apiguardian)
* **jcommander** (org.jcommander:jcommander:3.0 - https://jcommander.org)
* **IntelliJ IDEA Annotations** (org.jetbrains:annotations:13.0 - http://www.jetbrains.org)
* **Kotlin Stdlib** (org.jetbrains.kotlin:kotlin-stdlib:1.9.23 - https://kotlinlang.org/)
* **Kotlin Stdlib** (org.jetbrains.kotlin:kotlin-stdlib:2.4.10 - https://kotlinlang.org/)
* **JSpecify annotations** (org.jspecify:jspecify:1.0.0 - http://jspecify.org/)
* **Objenesis** (org.objenesis:objenesis:3.3 - http://objenesis.org/objenesis)
* **org.opentest4j:opentest4j** (org.opentest4j:opentest4j:1.3.0 - https://github.com/ota4j-team/opentest4j)

## BSD-3-Clause:
Expand All @@ -36,17 +39,21 @@ This is the list of all third-party dependencies grouped by their license type.
## Eclipse Public License v2.0:

* **junit-pioneer** (org.junit-pioneer:junit-pioneer:2.3.0 - https://junit-pioneer.org/)
* **JUnit Jupiter (Aggregator)** (org.junit.jupiter:junit-jupiter:6.1.0 - https://junit.org/)
* **JUnit Jupiter API** (org.junit.jupiter:junit-jupiter-api:6.1.0 - https://junit.org/)
* **JUnit Jupiter Engine** (org.junit.jupiter:junit-jupiter-engine:6.1.0 - https://junit.org/)
* **JUnit Jupiter Params** (org.junit.jupiter:junit-jupiter-params:6.1.0 - https://junit.org/)
* **JUnit Platform Commons** (org.junit.platform:junit-platform-commons:6.1.0 - https://junit.org/)
* **JUnit Platform Engine API** (org.junit.platform:junit-platform-engine:6.1.0 - https://junit.org/)
* **JUnit Jupiter (Aggregator)** (org.junit.jupiter:junit-jupiter:6.1.1 - https://junit.org/)
* **JUnit Jupiter API** (org.junit.jupiter:junit-jupiter-api:6.1.1 - https://junit.org/)
* **JUnit Jupiter Engine** (org.junit.jupiter:junit-jupiter-engine:6.1.1 - https://junit.org/)
* **JUnit Jupiter Params** (org.junit.jupiter:junit-jupiter-params:6.1.1 - https://junit.org/)
* **JUnit Platform Commons** (org.junit.platform:junit-platform-commons:6.1.1 - https://junit.org/)
* **JUnit Platform Engine API** (org.junit.platform:junit-platform-engine:6.1.1 - https://junit.org/)

## LGPL-2.1-or-later:

* **Java Native Access** (net.java.dev.jna:jna:5.19.1 - https://github.com/java-native-access/jna)

## MIT:

* **mockito-core** (org.mockito:mockito-core:5.23.0 - https://github.com/mockito/mockito)

## MIT License:

* **Project Lombok** (org.projectlombok:lombok:1.18.46 - https://projectlombok.org)
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
// Update the DID log by adding as many entries as there are keys in the store.
// Keep "rotating" (pre-rotation) keys while updating
var i = 0;
while (i++ < RandomEd25519KeyStore.getCapacity()) {

Check warning on line 70 in examples/using-pre-rotation-keys/src/main/java/org/examples/Main.java

View workflow job for this annotation

GitHub Actions / code-quality-check

Avoid assignment to i in operand

Avoid assignments in operands; this can make code more complicated and harder to read. AssignmentInOperand (Priority: 3, Ruleset: Error Prone) https://docs.pmd-code.org/snapshot/pmd_rules_java_errorprone.html#assignmentinoperand

didLog.append(
// next DID log entry
Expand Down Expand Up @@ -101,7 +101,7 @@
* <strong>CAUTION</strong> The sole purpose of this helper class is to assist developers while writing unit tests.
* It is NOT intended to be used in production code.
*/
static class RandomEd25519KeyStore {

Check failure on line 104 in examples/using-pre-rotation-keys/src/main/java/org/examples/Main.java

View workflow job for this annotation

GitHub Actions / code-quality-check

This class has only private constructors and may be final

Reports classes that may be made final because they cannot be extended from outside their compilation unit anyway. This is because all their constructors are private, so a subclass could not call the super constructor. ClassWithOnlyPrivateConstructorsShouldBeFinal (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#classwithonlyprivateconstructorsshouldbefinal

private static RandomEd25519KeyStore instance = new RandomEd25519KeyStore(5);
private final VcDataIntegrityCryptographicSuite[] suites;
Expand All @@ -119,27 +119,27 @@
var index = 0;
do {
// the Ed25519VerificationMethodKeyProviderImpl() would also work, but is deprecated
var suite = new EdDsaJcs2022VcDataIntegrityCryptographicSuite();

Check warning on line 122 in examples/using-pre-rotation-keys/src/main/java/org/examples/Main.java

View workflow job for this annotation

GitHub Actions / code-quality-check

Avoid instantiating new objects inside loops

New objects created within loops should be checked to see if they can created outside them and reused. AvoidInstantiatingObjectsInLoops (Priority: 3, Ruleset: Performance) https://docs.pmd-code.org/snapshot/pmd_rules_java_performance.html#avoidinstantiatingobjectsinloops
suites[index] = suite;
Path publicPEM = null;
try {
publicPEM = Files.createTempFile("mypublic", "");
suite.writePublicKeyPemFile(publicPEM);
final PEMParser parser = new PEMParser(Files.newBufferedReader(publicPEM));

Check warning on line 128 in examples/using-pre-rotation-keys/src/main/java/org/examples/Main.java

View workflow job for this annotation

GitHub Actions / code-quality-check

Avoid instantiating new objects inside loops

New objects created within loops should be checked to see if they can created outside them and reused. AvoidInstantiatingObjectsInLoops (Priority: 3, Ruleset: Performance) https://docs.pmd-code.org/snapshot/pmd_rules_java_performance.html#avoidinstantiatingobjectsinloops
var pemObj = parser.readObject();
if (pemObj instanceof SubjectPublicKeyInfo) {
keys[index] = new JcaPEMKeyConverter().getPublicKey((SubjectPublicKeyInfo) pemObj);
if (pemObj instanceof SubjectPublicKeyInfo subjectPublicKeyInfo) {
keys[index] = new JcaPEMKeyConverter().getPublicKey(subjectPublicKeyInfo);
} else {
throw new IllegalArgumentException("The supplied reader features no PEM-encoded public key");
}

} catch (VcDataIntegrityCryptographicSuiteException | IOException e) {
throw new RuntimeException(e);

Check failure on line 137 in examples/using-pre-rotation-keys/src/main/java/org/examples/Main.java

View workflow job for this annotation

GitHub Actions / code-quality-check

Avoid throwing raw exception type RuntimeException.

Avoid throwing certain exception types. Rather than throw a raw RuntimeException, Throwable, Exception, or Error, use a subclassed exception or error instead. AvoidThrowingRawExceptionTypes (Priority: 1, Ruleset: Design) https://docs.pmd-code.org/snapshot/pmd_rules_java_design.html#avoidthrowingrawexceptiontypes
} finally {
if (publicPEM != null) publicPEM.toFile().deleteOnExit();
}

} while (++index < suites.length);

Check warning on line 142 in examples/using-pre-rotation-keys/src/main/java/org/examples/Main.java

View workflow job for this annotation

GitHub Actions / code-quality-check

Avoid assignment to index in operand

Avoid assignments in operands; this can make code more complicated and harder to read. AssignmentInOperand (Priority: 3, Ruleset: Error Prone) https://docs.pmd-code.org/snapshot/pmd_rules_java_errorprone.html#assignmentinoperand
}

static void init(int capacity) {
Expand Down
11 changes: 8 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
<!--project.dependencies.directory>lib/</project.dependencies.directory-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<didresolver.version>2.8.2</didresolver.version>
<didresolver.version>2.9.0</didresolver.version>
<jna.version>5.19.1</jna.version>
<gson.version>2.14.0</gson.version>
<lombok.version>1.18.46</lombok.version>
Expand Down Expand Up @@ -584,7 +584,6 @@
<groupId>com.google.crypto.tink</groupId>
<artifactId>tink</artifactId>
<version>${tink.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
Expand All @@ -594,6 +593,12 @@
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit-pioneer/junit-pioneer -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.23.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
Expand All @@ -610,7 +615,7 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.22.0</version>
<version>2.22.1</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ protected static String buildSCID(JsonElement didLogEntryWithoutProofAndSignatur
}

/**
* Setup the class members w.r.t. outcome of the supplied DID log resolution process.
* Set up the class members w.r.t. outcome of the supplied DID log resolution process.
*
* @param didLog to "peek" into
* @throws DidLogMetaPeekerException if "peeking" failed for whatever reason
Expand Down Expand Up @@ -149,13 +149,13 @@ Generate the authorization key pair(s) Authorized keys are authorized to control
an entry replaces the previously active list. If an entry does not have the updateKeys item,
the currently active list continues to apply.
*/
var updatkeKeys = new HashSet<String>(); // must be a distinct list
updatkeKeys.add(verificationMethodKeyProvider.getVerificationKeyMultibase()); // first and foremost...
var updateKeys = new HashSet<String>(); // must be a distinct list
updateKeys.add(verificationMethodKeyProvider.getVerificationKeyMultibase()); // first and foremost...
if (updateKeysParameter != null) {
updateKeysParameter.forEach(key -> updatkeKeys.add(key.getUpdateKey()));
updateKeysParameter.forEach(key -> updateKeys.add(key.getUpdateKey()));
}
var updateKeysJsonArray = new JsonArray();
updatkeKeys.forEach(updateKeysJsonArray::add);
updateKeys.forEach(updateKeysJsonArray::add);

didMethodParameters.add(NamedDidMethodParameters.UPDATE_KEYS, updateKeysJsonArray);

Expand Down Expand Up @@ -299,7 +299,7 @@ protected JsonObject createDidDoc(URL identifierRegistryUrl,

protected boolean isVerificationMethodKeyProviderLegal(VerificationMethodKeyProvider verificationMethodKeyProvider) {
// Update keys must only match during key pre-rotation.
// Otherwise will take effect in the next update.
// Otherwise, will take effect in the next update.
if (this.didLogMeta.isKeyPreRotationActivated()) {
return this.didLogMeta.isPreRotatedUpdateKey(verificationMethodKeyProvider.getVerificationKeyMultibase());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,32 @@
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.ECDSASigner;
import com.nimbusds.jose.jca.JCAContext;
import com.nimbusds.jose.jwk.Curve;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
import com.nimbusds.jose.util.Base64URL;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.util.Date;
import java.util.Set;

public class EcP256ProofOfPossessionJWSSigner implements ProofOfPossessionJWSSigner {
protected ECKey signingKey;
private final JWSSigner signer;

public EcP256ProofOfPossessionJWSSigner(String keyId) throws JOSEException {
this.signingKey = new ECKeyGenerator(Curve.P_256).keyUse(KeyUse.SIGNATURE).keyID(keyId).issueTime(new Date()).generate(); //NOPMD ReplaceJavaUtilDate function expects date class
public EcP256ProofOfPossessionJWSSigner(Path path, String kid) throws IOException, JOSEException {
this(PemUtils.parsePemKeyPair(Files.newBufferedReader(path)), kid);
}

public EcP256ProofOfPossessionJWSSigner(Path path, String keyId) throws IOException {
var keyPair = PemUtils.parsePemKeyPair(Files.newBufferedReader(path));
this.signingKey = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic()).keyID(keyId).privateKey((ECPrivateKey) keyPair.getPrivate()).build();
public EcP256ProofOfPossessionJWSSigner(KeyPair keyPair, String kid) throws JOSEException {
this.signingKey = new ECKey.Builder(Curve.P_256, (ECPublicKey) keyPair.getPublic()).keyID(kid).privateKey((ECPrivateKey) keyPair.getPrivate()).build();
this.signer = new ECDSASigner(this.signingKey.toECPrivateKey());
}

@Override
Expand All @@ -47,10 +48,11 @@ public Set<JWSAlgorithm> supportedJWSAlgorithms() {

@Override
public Base64URL sign(JWSHeader jwsHeader, byte[] bytes) throws JOSEException {
try {
return new ECDSASigner(signingKey.toECPrivateKey()).sign(new JWSHeader(JWSAlgorithm.ES256), bytes);
} catch (JOSEException e) {
throw new RuntimeException(e); //NOPMD AvoidThrowingRawExceptionTypes should not be thrown
}
return this.signer.sign(jwsHeader, bytes);
}

@Override
public JCAContext getJCAContext() {
return signer.getJCAContext();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* @since 1.8.0
*/
public class EdDsaJcs2022JWSSigner extends EdDsaJcs2022VcDataIntegrityCryptographicSuite implements JWSSigner {
private final JCAContext jcaContext = new JCAContext();

/**
* @see EdDsaJcs2022VcDataIntegrityCryptographicSuite#EdDsaJcs2022VcDataIntegrityCryptographicSuite(Path)
Expand Down Expand Up @@ -50,6 +51,6 @@ public Base64URL sign(JWSHeader jwsHeader, byte[] bytes) throws JOSEException {

@Override
public JCAContext getJCAContext() {
return null;
return this.jcaContext;
}
}
Loading
Loading