Skip to content

Commit

Permalink
Merge pull request #91 from Cofinity-X/feat/vc_asJwt
Browse files Browse the repository at this point in the history
Feat: Create VC as jwt( json web token)
  • Loading branch information
borisrizov-zf authored Apr 17, 2024
2 parents 9727c2d + 9e7d6c3 commit e85a0e9
Show file tree
Hide file tree
Showing 18 changed files with 458 additions and 99 deletions.
4 changes: 0 additions & 4 deletions DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ maven/mavencentral/net.i2p.crypto/eddsa/0.3.0, CC0-1.0, approved, CQ22537
maven/mavencentral/org.apache.commons/commons-lang3/3.14.0, Apache-2.0, approved, #11677
maven/mavencentral/org.bouncycastle/bcprov-jdk18on/1.77, MIT AND CC0-1.0, approved, #11595
maven/mavencentral/org.checkerframework/checker-qual/3.37.0, MIT, approved, clearlydefined
maven/mavencentral/org.apache.commons/commons-lang3/3.12.0, Apache-2.0, approved, clearlydefined
maven/mavencentral/org.apache.httpcomponents/httpclient/4.5.14, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ23527
maven/mavencentral/org.apache.httpcomponents/httpcore/4.4.16, Apache-2.0, approved, CQ23528
maven/mavencentral/org.checkerframework/checker-compat-qual/2.5.5, GPL-2.0-only with Classpath-Exception-2.0, approved, #11598
maven/mavencentral/org.codehaus.woodstox/stax2-api/4.2.1, BSD-2-Clause, approved, #2670
maven/mavencentral/org.eclipse.parsson/parsson/1.1.5, EPL-2.0, approved, ee4j.parsson
maven/mavencentral/org.projectlombok/lombok/1.18.30, MIT AND LicenseRef-Public-Domain, approved, CQ23907
4 changes: 2 additions & 2 deletions src/main/java/org/eclipse/tractusx/ssi/examples/VC.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public static VerifiableCredential createVCWithoutProof() {
* @return the verifiable credential
* @throws UnsupportedSignatureTypeException the unsupported signature type exception
* @throws SsiException the ssi exception
* @throws InvalidePrivateKeyFormat the invalide private key format
* @throws InvalidPrivateKeyFormatException the invalide private key format
*/
public static VerifiableCredential createVCWithED25519Proof(
VerifiableCredential credential, IPrivateKey privateKey, Did issuer)
Expand Down Expand Up @@ -120,7 +120,7 @@ public static VerifiableCredential createVCWithED25519Proof(
* @return the verifiable credential
* @throws UnsupportedSignatureTypeException the unsupported signature type exception
* @throws SsiException the ssi exception
* @throws InvalidePrivateKeyFormat the invalide private key format
* @throws InvalidPrivateKeyFormatException the invalide private key format
*/
public static VerifiableCredential createVCWithJWSProof(
VerifiableCredential credential, IPrivateKey privateKey, Did issuer)
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/org/eclipse/tractusx/ssi/examples/VP.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* ******************************************************************************
* Copyright (c) 2021,2023 Contributors to the Eclipse Foundation
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
Expand Down Expand Up @@ -42,7 +42,6 @@
* JWT format
*/
class VP {

/**
* Create a verifiable presentation.
*
Expand Down Expand Up @@ -71,6 +70,7 @@ public static VerifiablePresentation createVP(
* @param audience the audience
* @param privateKey the private key
* @param publicKey the public key
* @param keyId the key id
* @return the signed jwt
* @throws IOException the io exception
*/
Expand All @@ -79,7 +79,8 @@ public static SignedJWT createVPAsJWT(
List<VerifiableCredential> credentials,
String audience,
IPrivateKey privateKey,
IPublicKey publicKey)
IPublicKey publicKey,
String keyId)
throws IOException {

// Extracting keys
Expand All @@ -90,6 +91,6 @@ public static SignedJWT createVPAsJWT(
new SerializedJwtPresentationFactoryImpl(
new SignedJwtFactory(new OctetKeyPairFactory()), new JsonLdSerializerImpl(), issuer);

return presentationFactory.createPresentation(issuer, credentials, audience, privateKey);
return presentationFactory.createPresentation(issuer, credentials, audience, privateKey, keyId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class x25519PrivateKey implements IPrivateKey {
* Instantiates a new X 25519 private key.
*
* @param privateKey the private key
* @throws InvalidePrivateKeyFormat the invalide private key format
* @throws InvalidPrivateKeyFormatException the invalide private key format
*/
public x25519PrivateKey(byte[] privateKey) throws InvalidPrivateKeyFormatException {
if (this.getKeyLength() != privateKey.length) {
Expand All @@ -60,8 +60,7 @@ public x25519PrivateKey(byte[] privateKey) throws InvalidPrivateKeyFormatExcepti
*
* @param privateKey the private key
* @param pemFormat the pem format
* @throws InvalidePrivateKeyFormat the invalide private key format
* @throws IOException the io exception
* @throws InvalidPrivateKeyFormatException the invalide private key format
*/
public x25519PrivateKey(String privateKey, boolean PEMFormat)
throws InvalidPrivateKeyFormatException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public class x25519PublicKey implements IPublicKey {
* Instantiates a new X 25519 public key.
*
* @param publicKey the public key
* @throws InvalidePublicKeyFormat the invalide public key format
* @throws InvalidPublicKeyFormatException the invalide public key format
*/
public x25519PublicKey(byte[] publicKey) throws InvalidPublicKeyFormatException {
if (this.getKeyLength() != publicKey.length) {
Expand All @@ -60,7 +60,7 @@ public x25519PublicKey(byte[] publicKey) throws InvalidPublicKeyFormatException
*
* @param publicKey the public key
* @param pemFormat the pe mformat
* @throws InvalidePublicKeyFormat the invalide public key format
* @throws InvalidPublicKeyFormatException the invalide public key format
* @throws IOException the io exception
*/
public x25519PublicKey(String publicKey, boolean PEMformat)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

package org.eclipse.tractusx.ssi.lib.exception.proof;

/** NoVerificationKeyFoundExcpetion */
/** NoVerificationKeyFoundException */
public class NoVerificationKeyFoundException extends SignatureVerificationException {
private static final long serialVersionUID = 1L;
/**
Expand Down
148 changes: 114 additions & 34 deletions src/main/java/org/eclipse/tractusx/ssi/lib/jwt/SignedJwtFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/*
* ******************************************************************************
/********************************************************************************
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
Expand All @@ -16,11 +15,11 @@
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
* *******************************************************************************
*/
********************************************************************************/

package org.eclipse.tractusx.ssi.lib.jwt;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
Expand All @@ -30,16 +29,15 @@
import com.nimbusds.jose.jwk.OctetKeyPair;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.net.URI;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
import lombok.SneakyThrows;
import org.eclipse.tractusx.ssi.lib.crypt.IPrivateKey;
import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory;
import org.eclipse.tractusx.ssi.lib.model.did.Did;
import org.eclipse.tractusx.ssi.lib.model.verifiable.Verifiable;
import org.eclipse.tractusx.ssi.lib.serialization.jwt.JwtConfig;
import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedVerifiablePresentation;

/**
Expand All @@ -50,57 +48,138 @@ public class SignedJwtFactory {

private final OctetKeyPairFactory octetKeyPairFactory;

public SignedJwtFactory(OctetKeyPairFactory octetKeyPairFactory) {
this.octetKeyPairFactory = Objects.requireNonNull(octetKeyPairFactory);
}

/**
* Instantiates a new Signed jwt factory.
* Creates a signed JWT {@link SignedJWT} that contains a set of claims and an issuer with a
* default configuration (60 second expiration time) {@link JwtConfig} for VP
*
* @param octetKeyPairFactory the octet key pair factory
* @param didIssuer
* @param audience
* @param serializedPresentation
* @param privateKey
* @param keyId
* @return
*/
public SignedJwtFactory(OctetKeyPairFactory octetKeyPairFactory) {
this.octetKeyPairFactory = Objects.requireNonNull(octetKeyPairFactory);
@SneakyThrows
public SignedJWT create(
Did didIssuer,
String audience,
SerializedVerifiablePresentation serializedPresentation,
IPrivateKey privateKey,
String keyId) {
JwtConfig jwtConfig = JwtConfig.builder().expirationTime(60).build();
return create(didIssuer, audience, serializedPresentation, privateKey, keyId, jwtConfig);
}

/**
* Creates a signed JWT {@link SignedJWT} that contains a set of claims and an issuer. Although
* all private key types are possible, in the context of Distributed Identity using an Elliptic
* Curve key ({@code P-256}) is advisable.
* Creates a signed JWT {@link SignedJWT} that contains a set of claims and an issuer with a
* specific configuration {@link JwtConfig} for VP
*
* @param didIssuer the did issuer
* @param audience the value of the token audience claim, e.g. the IDS Webhook address.
* @param serializedPresentation the serialized presentation
* @param privateKey the private key
* @return a {@code SignedJWT} that is signed with the private key and contains all claims listed.
* @param didIssuer
* @param audience
* @param serializedPresentation
* @param privateKey
* @param keyId
* @param config
* @return
*/
@SneakyThrows
public SignedJWT create(
URI id,
Did didIssuer,
String audience,
SerializedVerifiablePresentation serializedPresentation,
IPrivateKey privateKey) {
IPrivateKey privateKey,
String keyId,
JwtConfig config) {

final String issuer = didIssuer.toString();
final String subject = didIssuer.toString();

TypeReference<HashMap<String, Object>> typeRef =
new TypeReference<HashMap<String, Object>>() {};

// make on object out of it so that it can get serialized again
Map<String, Object> vp =
new ObjectMapper().readValue(serializedPresentation.getJson(), HashMap.class);
new ObjectMapper().readValue(serializedPresentation.getJson(), typeRef);

Date iat = new Date();

var claimsSet =
new JWTClaimsSet.Builder()
.issuer(issuer)
.subject(subject)
.audience(audience)
.claim("vp", vp)
.expirationTime(new Date(new Date().getTime() + 60 * 1000))
.jwtID(id.toString())
.issueTime(iat)
.expirationTime(new Date(iat.getTime() + config.getExpirationTime() * 1000))
.jwtID(UUID.randomUUID().toString())
.build();

final OctetKeyPair octetKeyPair = octetKeyPairFactory.fromPrivateKey(privateKey);
return createSignedES256Jwt(octetKeyPair, claimsSet, issuer);
return createSignedES256Jwt(octetKeyPair, claimsSet, issuer, keyId);
}

private static SignedJWT createSignedES256Jwt(
OctetKeyPair privateKey, JWTClaimsSet claimsSet, String issuer) {
/**
* Creates a signed JWT {@link SignedJWT} from a Verifiable Credential
*
* @param didIssuer
* @param holderIssuer
* @param vc
* @param privateKey
* @param keyId
* @return
*/
@SneakyThrows
public SignedJWT create(
Did didIssuer,
Did holderDid,
LinkedHashMap<String, Object> vc,
IPrivateKey privateKey,
String keyId) {
final String issuer = didIssuer.toString();
final String subject = holderDid.toString();

// check if expirationDate is presented in VC then use it, otherwise null
final Date expireDateAsDate =
vc.containsKey("expirationDate")
? Date.from(Instant.parse((String) vc.get("expirationDate")))
: null;

// check if issuanceDate is presented in VC then use it, otherwise null
final Date issueDate =
vc.containsKey("issuanceDate")
? Date.from(Instant.parse((String) vc.get("issuanceDate")))
: null;

vc.remove(Verifiable.PROOF);

var claimsSet =
new JWTClaimsSet.Builder()
.issuer(issuer)
.subject(subject)
.claim("vc", vc)
.expirationTime(expireDateAsDate)
.issueTime(issueDate)
.build();

final OctetKeyPair octetKeyPair = octetKeyPairFactory.fromPrivateKey(privateKey);
return createSignedES256Jwt(octetKeyPair, claimsSet, issuer, keyId);
}

/**
* Create a signedJwt for ES256 JWT {@link SignedJWT} with a set of claims
*
* @param privateKey
* @param claimsSet
* @param issuer
* @param keyId
* @return
*/
public SignedJWT createSignedES256Jwt(
OctetKeyPair privateKey, JWTClaimsSet claimsSet, String issuer, String keyId) {
JWSSigner signer;
try {

Expand All @@ -116,11 +195,12 @@ private static SignedJWT createSignedES256Jwt(

var algorithm = JWSAlgorithm.EdDSA;
var type = JOSEObjectType.JWT;

JWSHeader.Builder jwsHeaderBuilder =
new JWSHeader.Builder(algorithm).type(type).keyID(issuer).base64URLEncodePayload(true);

var header = jwsHeaderBuilder.build();
var header =
new JWSHeader.Builder(algorithm)
.type(type)
.keyID(issuer + "#" + keyId)
.base64URLEncodePayload(true)
.build();
var vc = new SignedJWT(header, claimsSet);

vc.sign(signer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface ISigner {
* @param privateKey the private key
* @return the byte [ ]
* @throws SsiException the ssi exception
* @throws InvalidePrivateKeyFormat the invalide private key format
* @throws InvalidPrivateKeyFormatException the invalide private key format
*/
public byte[] sign(HashedLinkedData hashedLinkedData, IPrivateKey privateKey)
throws InvalidPrivateKeyFormatException, SignatureGenerateFailedException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public static LinkedDataProofGenerator newInstance(SignatureType type)
* @param privateKey the private key
* @return the proof
* @throws SsiException the ssi exception
* @throws InvalidePrivateKeyFormat the invalide private key format
* @throws InvalidPrivateKeyFormatException the invalide private key format
*/
public Proof createProof(Verifiable verifiable, URI verificationMethodId, IPrivateKey privateKey)
throws InvalidPrivateKeyFormatException, SignatureGenerateFailedException,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/********************************************************************************
* Copyright (c) 2021,2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
********************************************************************************/

package org.eclipse.tractusx.ssi.lib.serialization.jwt;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class JwtConfig {

private long expirationTime;
}
Loading

0 comments on commit e85a0e9

Please sign in to comment.