Skip to content

Commit

Permalink
fix: add WalletKey for ES256K algorithm to DidDocument
Browse files Browse the repository at this point in the history
  • Loading branch information
andreibogus committed Mar 20, 2024
1 parent 541a524 commit 9eb3294
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
import com.nimbusds.jose.jwk.gen.ECKeyGenerator;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringEscapeUtils;
import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings;
import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey;
Expand All @@ -44,7 +47,12 @@
import org.eclipse.tractusx.managedidentitywallets.exception.SignatureFailureException;
import org.eclipse.tractusx.managedidentitywallets.exception.UnsupportedAlgorithmException;
import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils;
import org.eclipse.tractusx.ssi.lib.did.web.DidWebFactory;
import org.eclipse.tractusx.ssi.lib.model.did.Did;
import org.eclipse.tractusx.ssi.lib.model.did.DidDocument;
import org.eclipse.tractusx.ssi.lib.model.did.DidDocumentBuilder;
import org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod;
import org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod;
import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential;
import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentation;
import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationBuilder;
Expand All @@ -59,6 +67,7 @@
import java.io.IOException;
import java.net.URI;
import java.security.interfaces.ECPrivateKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
Expand All @@ -71,22 +80,32 @@
import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.REFERENCE_KEY;
import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.VAULT_ACCESS_TOKEN;
import static org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils.getKeyString;
import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.JWK_CURVE;
import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.JWK_KEK_TYPE;
import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.JWK_X;
import static org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethod.PUBLIC_KEY_JWK;
import static org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod.CONTROLLER;
import static org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod.ID;
import static org.eclipse.tractusx.ssi.lib.model.did.VerificationMethod.TYPE;


@Service
@Slf4j
public class JwtPresentationES256KService {

private JsonLdSerializer jsonLdSerializer;
private Did agentDid;
private WalletRepository walletRepository;
private EncryptionUtils encryptionUtils;
private WalletKeyService walletKeyService;
private MIWSettings miwSettings;

@Autowired
public JwtPresentationES256KService(WalletRepository walletRepository, EncryptionUtils encryptionUtils, WalletKeyService walletKeyService) {
public JwtPresentationES256KService(WalletRepository walletRepository, EncryptionUtils encryptionUtils, WalletKeyService walletKeyService, MIWSettings miwSettings) {
this.walletRepository = walletRepository;
this.encryptionUtils = encryptionUtils;
this.walletKeyService = walletKeyService;
this.miwSettings = miwSettings;
}

public JwtPresentationES256KService(Did agentDid, JsonLdSerializer jsonLdSerializer) {
Expand All @@ -108,33 +127,70 @@ public SignedJWT createPresentation(Did issuer, List<VerifiableCredential> crede
}

@Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRES_NEW)
public void storeWalletKeyES256K(Wallet wallet) {
WalletKey walletKeyES256K;
public Wallet storeWalletKeyES256K(Wallet wallet, String keyId) {
try {
String keyId = UUID.randomUUID().toString();
// create additional key pair ES256K
ECKey ecJwk = new ECKeyGenerator(Curve.SECP256K1)
ECKey ecKey = new ECKeyGenerator(Curve.SECP256K1)
.keyUse(KeyUse.SIGNATURE)
.keyID(keyId)
.provider(BouncyCastleProviderSingleton.getInstance())
.generate();

Wallet walletFromDB = walletRepository.getByDid(wallet.getDid());
walletKeyES256K = WalletKey.builder()
.wallet(walletFromDB)
Did did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), wallet.getBpn());
JWKVerificationMethod jwkVerificationMethod = getJwkVerificationMethod(ecKey, did);
DidDocument didDocument = wallet.getDidDocument();
List<VerificationMethod> verificationMethods = didDocument.getVerificationMethods();
verificationMethods.add(jwkVerificationMethod);
DidDocument updatedDidDocument = buildDidDocument(wallet.getBpn(), did, verificationMethods);

wallet = walletRepository.getByDid(wallet.getDid());
wallet.setDidDocument(updatedDidDocument);
walletRepository.save(wallet);

WalletKey walletKeyES256K = WalletKey.builder()
.wallet(wallet)
.keyId(keyId)
.referenceKey(REFERENCE_KEY)
.vaultAccessToken(VAULT_ACCESS_TOKEN)
.privateKey(encryptionUtils.encrypt(getKeyString(ecJwk.toECPrivateKey().getEncoded(), PRIVATE_KEY)))
.publicKey(encryptionUtils.encrypt(getKeyString(ecJwk.toECPublicKey().getEncoded(), PUBLIC_KEY)))
.privateKey(encryptionUtils.encrypt(getKeyString(ecKey.toECPrivateKey().getEncoded(), PRIVATE_KEY)))
.publicKey(encryptionUtils.encrypt(getKeyString(ecKey.toECPublicKey().getEncoded(), PUBLIC_KEY)))
.algorithm(SupportedAlgorithms.ES256K.toString())
.build();
//Save key ES256K
walletKeyService.getRepository().save(walletKeyES256K);
} catch (JOSEException e) {
throw new BadDataException("Could not generate EC Jwk", e);
}
return wallet;
}

//Save key ES256K
walletKeyService.getRepository().save(walletKeyES256K);
private JWKVerificationMethod getJwkVerificationMethod(ECKey ecKey, Did did) {
Map<String, Object> verificationMethodJson = new HashMap<>();
Map<String, String> publicKeyJwk = Map.of(JWK_KEK_TYPE, ecKey.getKeyType().toString(), JWK_CURVE,
ecKey.getCurve().getName(), JWK_X, ecKey.getX().toString());
verificationMethodJson.put(ID, URI.create(did + "#" + ecKey.getKeyID()));
verificationMethodJson.put(TYPE, JWKVerificationMethod.DEFAULT_TYPE);
verificationMethodJson.put(CONTROLLER, did.toUri());
verificationMethodJson.put(PUBLIC_KEY_JWK, publicKeyJwk);
return new JWKVerificationMethod(verificationMethodJson);
}

public DidDocument buildDidDocument(String bpn, Did did, List<VerificationMethod> jwkVerificationMethods) {
DidDocumentBuilder didDocumentBuilder = new DidDocumentBuilder();
didDocumentBuilder.id(did.toUri());
didDocumentBuilder.verificationMethods(jwkVerificationMethods);
DidDocument didDocument = didDocumentBuilder.build();
//modify context URLs
List<URI> context = didDocument.getContext();
List<URI> mutableContext = new ArrayList<>(context);
miwSettings.didDocumentContextUrls().forEach(uri -> {
if (!mutableContext.contains(uri)) {
mutableContext.add(uri);
}
});
didDocument.put("@context", mutableContext);
didDocument = DidDocument.fromJson(didDocument.toJson());
log.debug("did document created for bpn ->{}", StringEscapeUtils.escapeJava(bpn));
return didDocument;
}

public SignedJWT createSignedJwt(URI id, Did didIssuer, String audience, SerializedVerifiablePresentation serializedPresentation, ECPrivateKey ecPrivateKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,19 +216,13 @@ public Page<Wallet> getWallets(int pageNumber, int size, String sortColumn, Stri
public Wallet createWallet(CreateWalletRequest request, String callerBpn) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
final Wallet[] wallets = new Wallet[1];
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
wallets[0] = createWallet(request, false, callerBpn);
}
});
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
jwtPresentationES256KService.storeWalletKeyES256K(wallets[0]);
}
});

transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
wallets[0] = createWallet(request, false, callerBpn);
}
});
wallets[0] = updateWalletWithWalletKeyES256K(transactionTemplate, wallets);
return wallets[0];
}

Expand All @@ -255,21 +249,7 @@ private Wallet createWallet(CreateWalletRequest request, boolean authority, Stri
JWKVerificationMethod jwkVerificationMethod =
new JWKVerificationMethodBuilder().did(did).jwk(jwk).build();

DidDocumentBuilder didDocumentBuilder = new DidDocumentBuilder();
didDocumentBuilder.id(did.toUri());
didDocumentBuilder.verificationMethods(List.of(jwkVerificationMethod));
DidDocument didDocument = didDocumentBuilder.build();
//modify context URLs
List<URI> context = didDocument.getContext();
List<URI> mutableContext = new ArrayList<>(context);
miwSettings.didDocumentContextUrls().forEach(uri -> {
if (!mutableContext.contains(uri)) {
mutableContext.add(uri);
}
});
didDocument.put("@context", mutableContext);
didDocument = DidDocument.fromJson(didDocument.toJson());
log.debug("did document created for bpn ->{}", StringEscapeUtils.escapeJava(request.getBpn()));
DidDocument didDocument = jwtPresentationES256KService.buildDidDocument(request.getBpn(), did, List.of(jwkVerificationMethod));

//Save wallet
Wallet wallet = create(Wallet.builder()
Expand Down Expand Up @@ -308,7 +288,7 @@ private Wallet createWallet(CreateWalletRequest request, boolean authority, Stri
@PostConstruct
public void createAuthorityWallet() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
final Wallet[] wallet = new Wallet[1];
final Wallet[] wallets = new Wallet[1];
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
Expand All @@ -317,22 +297,28 @@ protected void doInTransactionWithoutResult(TransactionStatus status) {
.name(miwSettings.authorityWalletName())
.bpn(miwSettings.authorityWalletBpn())
.build();
wallet[0] = createWallet(request, true, miwSettings.authorityWalletBpn());
wallets[0] = createWallet(request, true, miwSettings.authorityWalletBpn());
log.info("Authority wallet created with bpn {}", StringEscapeUtils.escapeJava(miwSettings.authorityWalletBpn()));
} else {
log.info("Authority wallet exists with bpn {}", StringEscapeUtils.escapeJava(miwSettings.authorityWalletBpn()));
}
}
});
updateWalletWithWalletKeyES256K(transactionTemplate, wallets);
}

private Wallet updateWalletWithWalletKeyES256K(TransactionTemplate transactionTemplate, Wallet[] wallets) {
String keyId = UUID.randomUUID().toString();
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@SneakyThrows
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
if (wallet[0] != null) {
jwtPresentationES256KService.storeWalletKeyES256K(wallet[0]);
}
// create additional key pair ES256K
if (wallets[0] != null){
wallets[0] = jwtPresentationES256KService.storeWalletKeyES256K(wallets[0], keyId);
}
}
});
return wallets[0];
}

private void validateCreateWallet(CreateWalletRequest request, String callerBpn) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType;
import org.eclipse.tractusx.managedidentitywallets.constant.RestURI;
import org.eclipse.tractusx.managedidentitywallets.constant.StringPool;
import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.HoldersCredential;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet;
import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey;
Expand Down Expand Up @@ -163,6 +164,7 @@ void createWalletTest201() throws JsonProcessingException, JSONException {

Assertions.assertNotNull(response.getBody());
Assertions.assertNotNull(wallet.getDidDocument());
Assertions.assertEquals(2, wallet.getDidDocument().getVerificationMethods().size());
List<URI> context = wallet.getDidDocument().getContext();
miwSettings.didDocumentContextUrls().forEach(uri -> {
Assertions.assertTrue(context.contains(uri));
Expand All @@ -174,6 +176,7 @@ void createWalletTest201() throws JsonProcessingException, JSONException {
Assertions.assertEquals(walletFromDB.getBpn(), bpn);
Assertions.assertEquals(walletFromDB.getName(), name);
Assertions.assertNotNull(walletFromDB);
// walletKey1
WalletKey walletKey = walletKeyRepository.getByWalletIdAndAlgorithm(walletFromDB.getId(), walletFromDB.getAlgorithm());
Assertions.assertNotNull(walletKey);
Assertions.assertEquals(walletFromDB.getBpn(), bpn);
Expand All @@ -183,6 +186,15 @@ void createWalletTest201() throws JsonProcessingException, JSONException {
Assertions.assertNotNull(walletKey.getKeyId());
Assertions.assertEquals(walletKey.getKeyId(), keyId);

//walletKey2
WalletKey walletKey2 = walletKeyRepository.getByWalletIdAndAlgorithm(walletFromDB.getId(), SupportedAlgorithms.ES256K.name());
Assertions.assertNotNull(walletKey2);

//validate keyId2
String keyId2 = wallet.getDidDocument().getVerificationMethods().get(1).getId().toString().split("#")[1];
Assertions.assertNotNull(walletKey2.getKeyId());
Assertions.assertEquals(walletKey2.getKeyId(), keyId2);

//check if BPN and Summary credentials is issued
HttpHeaders headers = AuthenticationUtils.getValidUserHttpHeaders(bpn);

Expand Down

0 comments on commit 9eb3294

Please sign in to comment.