From 9eb3294a9016befe7bcc469552a9266829bcba4b Mon Sep 17 00:00:00 2001 From: andreibogus Date: Wed, 20 Mar 2024 11:36:52 +0100 Subject: [PATCH] fix: add WalletKey for ES256K algorithm to DidDocument --- .../service/JwtPresentationES256KService.java | 82 ++++++++++++++++--- .../service/WalletService.java | 54 +++++------- .../wallet/WalletTest.java | 12 +++ 3 files changed, 101 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java index 5127528a8..4be744093 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java @@ -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; @@ -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; @@ -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; @@ -71,9 +80,17 @@ 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; @@ -81,12 +98,14 @@ public class JwtPresentationES256KService { 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) { @@ -108,33 +127,70 @@ public SignedJWT createPresentation(Did issuer, List 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 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 verificationMethodJson = new HashMap<>(); + Map 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 jwkVerificationMethods) { + DidDocumentBuilder didDocumentBuilder = new DidDocumentBuilder(); + didDocumentBuilder.id(did.toUri()); + didDocumentBuilder.verificationMethods(jwkVerificationMethods); + DidDocument didDocument = didDocumentBuilder.build(); + //modify context URLs + List context = didDocument.getContext(); + List 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) { diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java index d02c681fa..5eacbe6cd 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java @@ -216,19 +216,13 @@ public Page 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]; } @@ -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 context = didDocument.getContext(); - List 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() @@ -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) { @@ -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) { diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java index 587f710f7..3396339db 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java @@ -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; @@ -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 context = wallet.getDidDocument().getContext(); miwSettings.didDocumentContextUrls().forEach(uri -> { Assertions.assertTrue(context.contains(uri)); @@ -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); @@ -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);