Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
## Next

### Added
- Support for Ed25519 keys as verification material in create DID logs command
- 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
13 changes: 10 additions & 3 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 @@ -47,6 +50,10 @@ This is the list of all third-party dependencies grouped by their license type.

* **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)
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.9.0-SNAPSHOT</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 @@ -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;
}
}
37 changes: 9 additions & 28 deletions src/main/java/ch/admin/bj/swiyu/didtoolbox/JCommanderRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.nio.file.*;
import java.security.*;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;

import static ch.admin.bj.swiyu.didtoolbox.jcommander.CommandParameterNames.PARAM_NAME_LONG_GENERATE_NEW_VERIFYING_KEY;
Expand Down Expand Up @@ -135,8 +134,7 @@ void runCreateDidLogCommand(CreateDidLogCommand command) throws VerificationMeth
.create(identifierRegistryUrl));
}

@SuppressWarnings({"PMD.CognitiveComplexity", "PMD.NPathComplexity"})
void runUpdateDidLogCommand(UpdateDidLogCommand command) throws CommandException, VerificationMethodException, IOException, DidLogCreatorStrategyException, VcDataIntegrityCryptographicSuiteException, UpdateKeysDidMethodParameterException, NextKeyHashesDidMethodParameterException, DidLogUpdaterStrategyException {
void runUpdateDidLogCommand(UpdateDidLogCommand command) throws CommandException, VerificationMethodException, IOException, VcDataIntegrityCryptographicSuiteException, UpdateKeysDidMethodParameterException, NextKeyHashesDidMethodParameterException, DidLogUpdaterStrategyException {
if (command.help) {
jc.usage(parsedCommandName);
return;
Expand All @@ -148,21 +146,8 @@ void runUpdateDidLogCommand(UpdateDidLogCommand command) throws CommandException

// CAUTION At this point, it should be all in place to update to be able to update the supplied DID log

var assertionMethods = new HashSet<VerificationMethod>();
var updateCommandAssertionMethodKeys = command.assertionMethodKeys;
if (updateCommandAssertionMethodKeys != null && !updateCommandAssertionMethodKeys.isEmpty()) {
for (VerificationMethodParameters param : updateCommandAssertionMethodKeys) {
assertionMethods.add(VerificationMethod.of(param.key, param.jwk));
}
}

var authentications = new HashSet<VerificationMethod>();
var updateCommandAuthenticationKeys = command.authenticationKeys;
if (updateCommandAuthenticationKeys != null && !updateCommandAuthenticationKeys.isEmpty()) {
for (VerificationMethodParameters param : updateCommandAuthenticationKeys) {
authentications.add(VerificationMethod.of(param.key, param.jwk));
}
}
var assertionMethods = command.getAssertionMethods(getOutputDir().toPath());
var authentications = command.getAuthentications(getOutputDir().toPath());

if (authentications.isEmpty() && assertionMethods.isEmpty()) {
throw new CommandException("No update will take place as no verification material is supplied whatsoever");
Expand Down Expand Up @@ -226,7 +211,7 @@ void runDeactivateDidLogCommand(DeactivateDidLogCommand command) throws CommandE
.deactivate(didLogFile));
}

void runPoPCreateCommand(CreateProofOfPossessionCommand command) throws IOException, ProofOfPossessionCreatorException, CommandException, UnrecoverableEntryException, KeyStoreException, NoSuchAlgorithmException, JOSEException, KeyException {
void runPoPCreateCommand(CreateProofOfPossessionCommand command) throws IOException, ProofOfPossessionCreatorException, CommandException, UnrecoverableEntryException, KeyStoreException, NoSuchAlgorithmException, JOSEException, KeyException, VcDataIntegrityCryptographicSuiteException {
if (command.help) {
jc.usage(parsedCommandName);
return;
Expand All @@ -235,27 +220,23 @@ void runPoPCreateCommand(CreateProofOfPossessionCommand command) throws IOExcept
// Duration after which the JWT expires
Duration validDuration = Duration.ofDays(1);

var nonce = command.nonce;
var didLogFile = command.didLog;
var kid = command.kid;

var didLog = Files.readString(didLogFile.toPath());
var didLog = Files.readString(command.didLog.toPath());

ProofOfPossessionJWSSigner signer = null;
if (command.signingKeyPemFile != null) {
signer = new EcP256ProofOfPossessionJWSSigner(command.signingKeyPemFile.toPath(), kid);
signer = ProofOfPossessionJWSSigner.of(command.signingKeyPemFile.toPath(), command.kid);
} else if (command.securosysPrimusKeyStoreLoader != null && command.primusKeyAlias != null) {
signer = HsmProofOfPossessionJWSSigner.newPrimusSigner(command.securosysPrimusKeyStoreLoader, command.primusKeyAlias, command.primusKeyPassword, kid);
signer = ProofOfPossessionJWSSigner.of(command.securosysPrimusKeyStoreLoader, command.primusKeyAlias, command.primusKeyPassword, command.kid);
}

if (signer == null) {
throw new CommandException("No valid source of signing P-256 key supplied. Use one of the relevant options to supply keys");
}

var proof = new ProofOfPossessionCreator(signer).create(nonce, validDuration);
var proof = new ProofOfPossessionCreator(signer).create(command.nonce, validDuration);
try {
var verifier = new ProofOfPossessionVerifier(didLog);
verifier.verify(proof, nonce);
verifier.verify(proof, command.nonce);
} catch (ProofOfPossessionVerifierException e) {
throw new CommandException("Failed to verify generated proof: %s".formatted(e.getLocalizedMessage()), e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ch.admin.bj.swiyu.didtoolbox;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;

import java.time.Duration;
Expand Down Expand Up @@ -70,18 +72,18 @@ public ProofOfPossessionCreator(ProofOfPossessionJWSSigner signer) {
*/
public SignedJWT create(String nonce, Duration expiresIn) throws ProofOfPossessionCreatorException {
// Prepare header and claims set of the JWT
var signedJWT = new com.nimbusds.jwt.SignedJWT(
new com.nimbusds.jose.JWSHeader.Builder(signer.getAlgorithm())
var signedJWT = new SignedJWT(
new JWSHeader.Builder(signer.getAlgorithm())
.keyID(this.signer.getKid())
.build(),
new com.nimbusds.jwt.JWTClaimsSet.Builder()
new JWTClaimsSet.Builder()
.claim("nonce", nonce)
.issuer(signer.getKid().split("#")[0])
.issueTime(new Date()) //NOPMD ReplaceJavaUtilDate: function expects date
.expirationTime(Date.from(ZonedDateTime.now().plus(expiresIn).toInstant()))
.build());

// JOSEException is throw here, if the keys are invalid or don't match
// JOSEException is thrown here, if the keys are invalid or don't match
try {
signedJWT.sign(this.signer);
} catch (JOSEException e) {
Expand Down
Loading
Loading