diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java index e8b8a7f75..fdaec4ddc 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/security/SecurityConfig.java @@ -69,12 +69,14 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers(new AntPathRequestMatcher("/docs/api-docs/**")).permitAll() .requestMatchers(new AntPathRequestMatcher("/ui/swagger-ui/**")).permitAll() .requestMatchers(new AntPathRequestMatcher("/actuator/health/**")).permitAll() + .requestMatchers(new AntPathRequestMatcher(RestURI.DID_RESOLVE, GET.name())).permitAll() //Get did document .requestMatchers(new AntPathRequestMatcher(RestURI.DID_DOCUMENTS, GET.name())).permitAll() //Get did document .requestMatchers(new AntPathRequestMatcher(RestURI.WALLETS, POST.name())).hasRole(ApplicationConstant.ROLE_ADD_WALLETS) //Create wallet .requestMatchers(new AntPathRequestMatcher(RestURI.WALLETS, GET.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLETS) //Get all wallet .requestMatchers(new AntPathRequestMatcher(RestURI.API_WALLETS_IDENTIFIER, GET.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //get wallet by BPN .requestMatchers(new AntPathRequestMatcher(RestURI.API_WALLETS_IDENTIFIER_CREDENTIALS, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //Store credential .requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS, GET.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //get credentials + .requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS_VALIDATION, POST.name())).hasAnyRole(ApplicationConstant.ROLE_VIEW_WALLET, ApplicationConstant.ROLE_VIEW_WALLETS) //validate credentials .requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS_ISSUER_MEMBERSHIP, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //issue Membership Credential .requestMatchers(new AntPathRequestMatcher(RestURI.CREDENTIALS_ISSUER_DISMANTLER, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //issue dismantler Credential .requestMatchers(new AntPathRequestMatcher(RestURI.API_CREDENTIALS_ISSUER_FRAMEWORK, POST.name())).hasAnyRole(ApplicationConstant.ROLE_UPDATE_WALLETS, ApplicationConstant.ROLE_UPDATE_WALLET) //issue dismantler Credential diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java index 30bbcefaf..a90f3ec8b 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java @@ -39,6 +39,7 @@ private RestURI() { * The constant DID_DOCUMENTS. */ public static final String DID_DOCUMENTS = "/api/didDocuments/{identifier}"; + public static final String DID_RESOLVE = "/{bpn}/did.json"; /** * The constant WALLETS_BY_BPN. */ @@ -52,6 +53,7 @@ private RestURI() { * The constant CREDENTIALS. */ public static final String CREDENTIALS = "/api/credentials"; + public static final String CREDENTIALS_VALIDATION = "/api/credentials/validation"; /** * The constant CREDENTIALS_ISSUER_MEMBERSHIP. */ diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/CredentialController.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/CredentialController.java index 89fb4cd79..64a7f3703 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/CredentialController.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/CredentialController.java @@ -22,6 +22,8 @@ package org.eclipse.tractusx.managedidentitywallets.controller; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -38,6 +40,7 @@ import java.security.Principal; import java.util.List; +import java.util.Map; /** * The type Credential controller. @@ -108,4 +111,38 @@ public ResponseEntity issueDismantlerCredential(@Valid @Re public ResponseEntity issueFrameworkCredential(@Valid @RequestBody IssueFrameworkCredentialRequest request, Principal principal) { return ResponseEntity.status(HttpStatus.CREATED).body(service.issueFrameworkCredential(request, getBPNFromToken(principal))); } + + @Operation(summary = "Validate Verifiable Credentials", description = "Permission: **view_wallets** OR **view_wallet** \n\n Validate Verifiable Credentials") + @PostMapping(path = RestURI.CREDENTIALS_VALIDATION, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + + @io.swagger.v3.oas.annotations.parameters.RequestBody(content = { + @Content(examples = @ExampleObject(""" + { + "id": "http://example.edu/credentials/333", + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "type": [ + "University-Degree-Credential, VerifiableCredential" + ], + "issuer": "did:example:76e12ec712ebc6f1c221ebfeb1f", + "issuanceDate": "2019-06-16T18:56:59Z", + "expirationDate": "2019-06-17T18:56:59Z", + "credentialSubject": { + "college": "Test-University" + }, + "proof": { + "type": "Ed25519Signature2018", + "created": "2021-11-17T22:20:27Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:example:76e12ec712ebc6f1c221ebfeb1f#keys-1", + "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..JNerzfrK46Mq4XxYZEnY9xOK80xsEaWCLAHuZsFie1-NTJD17wWWENn_DAlA_OwxGF5dhxUJ05P6Dm8lcmF5Cg" + } + } + """)) + }) + public ResponseEntity> createPresentation(@RequestBody Map data) { + return ResponseEntity.status(HttpStatus.OK).body(service.credentialsValidation(data)); + } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java index 135fef018..5cfdb90af 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/DidDocumentController.java @@ -54,4 +54,10 @@ public class DidDocumentController { public ResponseEntity getDidDocument(@PathVariable(name = "identifier") String identifier) { return ResponseEntity.status(HttpStatus.OK).body(service.getDidDocument(identifier)); } + + @Operation(description = "Resolve the DID document for a given BPN", summary = "Resolve DID Document") + @GetMapping(path = RestURI.DID_RESOLVE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getDidResolve(@PathVariable(name = "bpn") String bpn) { + return ResponseEntity.status(HttpStatus.OK).body(service.getDidDocument(bpn)); + } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CredentialService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CredentialService.java index 879b42f72..fe3470df5 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CredentialService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CredentialService.java @@ -43,15 +43,21 @@ import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; +import org.eclipse.tractusx.ssi.lib.did.web.DidWebDocumentResolver; +import org.eclipse.tractusx.ssi.lib.did.web.util.DidWebParser; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; +import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofValidation; +import org.eclipse.tractusx.ssi.lib.resolver.DidDocumentResolverRegistryImpl; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.net.http.HttpClient; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -150,7 +156,7 @@ public VerifiableCredential issueFrameworkCredential(IssueFrameworkCredentialReq "value", request.getValue(), "contract-template", request.getContractTemplate(), "contract-version", request.getContractVersion()); - Credential credential = CommonUtils.getCredential(subject, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION_CX, miwSettings.authorityWalletDid(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); + Credential credential = CommonUtils.getCredential(subject, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION_CX, baseWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credential = create(credential); @@ -187,7 +193,7 @@ public VerifiableCredential issueDismantlerCredential(IssueDismantlerCredentialR "holderIdentifier", holderWallet.getBpn(), "activityType", request.getActivityType(), "allowedVehicleBrands", request.getAllowedVehicleBrands()); - Credential credential = CommonUtils.getCredential(subject, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL_CX, miwSettings.authorityWalletDid(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); + Credential credential = CommonUtils.getCredential(subject, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL_CX, baseWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credential = create(credential); @@ -226,7 +232,7 @@ public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialR "holderIdentifier", holderWallet.getBpn(), "memberOf", baseWallet.getName(), "status", "Active", - "startTime", Instant.now().toString()), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL_CX, miwSettings.authorityWalletDid(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); + "startTime", Instant.now().toString()), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL_CX, baseWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credential = create(credential); @@ -236,10 +242,27 @@ public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialR } - - private void isCredentialExit(String holderDid, String credentialType) { Validate.isTrue(credentialRepository.existsByHolderDidAndType(holderDid, credentialType)).launch(new DuplicateCredentialProblem("Credential of type " + credentialType + " is already exists ")); } + public Map credentialsValidation(Map data) { + VerifiableCredential verifiableCredential = new VerifiableCredential(data); + // DID Resolver Constracture params + DidWebParser didParser = new DidWebParser(); + var httpClient = HttpClient.newHttpClient(); + var enforceHttps = true; + + var didDocumentResolverRegistry = new DidDocumentResolverRegistryImpl(); + didDocumentResolverRegistry.register( + new DidWebDocumentResolver(httpClient, didParser, enforceHttps)); + + LinkedDataProofValidation proofValidation = LinkedDataProofValidation.newInstance(didDocumentResolverRegistry); + Boolean valid = proofValidation.checkProof(verifiableCredential); // TODO getting InvalidKeyException + Map response = new HashMap<>(); + response.put("valid", valid); + response.put("vp", verifiableCredential); + + return response; + } } 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 e8018959e..667f679c3 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java @@ -264,7 +264,7 @@ public Wallet createWallet(CreateWalletRequest request) { Credential credential = CommonUtils.getCredential(Map.of("type", MIWVerifiableCredentialType.BPN_CREDENTIAL, "id", wallet.getDid(), - "bpn", wallet.getBpn()), MIWVerifiableCredentialType.BPN_CREDENTIAL_CX, miwSettings.authorityWalletDid(), privateKeyBytes, wallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); + "bpn", wallet.getBpn()), MIWVerifiableCredentialType.BPN_CREDENTIAL_CX, baseWallet.getDidDocument(), privateKeyBytes, wallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credentialRepository.save(credential); diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java index 445df4358..e3ca621fe 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java @@ -24,11 +24,15 @@ import org.eclipse.tractusx.managedidentitywallets.constant.ApplicationConstant; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Credential; import org.eclipse.tractusx.ssi.lib.model.Ed25519Signature2020; +import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialBuilder; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofGenerator; +import org.eclipse.tractusx.ssi.lib.proof.hash.LinkedDataHasher; +import org.eclipse.tractusx.ssi.lib.proof.transform.LinkedDataTransformer; +import org.eclipse.tractusx.ssi.lib.proof.verify.LinkedDataSigner; import java.net.URI; import java.time.Instant; @@ -66,12 +70,12 @@ public static String getIdentifierType(String identifier) { * * @param subject the subject * @param type the type - * @param issuerDid the issuer did + * @param issuerDoc the issuer doc * @param privateKeyBytes the private key bytes * @param holderDid the holder did * @return the credential */ - public static Credential getCredential(Map subject, String type, String issuerDid, byte[] privateKeyBytes, String holderDid, List contexts, Date expiryDate) { + public static Credential getCredential(Map subject, String type, DidDocument issuerDoc, byte[] privateKeyBytes, String holderDid, List contexts, Date expiryDate) { //VC Subject VerifiableCredentialSubject verifiableCredentialSubject = new VerifiableCredentialSubject(subject); @@ -80,34 +84,40 @@ public static Credential getCredential(Map subject, String type, List verifiableCredentialType = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, type); // Create VC - VerifiableCredential verifiableCredential = createVerifiableCredential(issuerDid, verifiableCredentialType, verifiableCredentialSubject, privateKeyBytes, contexts, expiryDate); + VerifiableCredential verifiableCredential = createVerifiableCredential(issuerDoc, verifiableCredentialType, + verifiableCredentialSubject, privateKeyBytes, contexts, expiryDate); // Create Credential return Credential.builder() .holderDid(holderDid) - .issuerDid(issuerDid) + .issuerDid(issuerDoc.getId().toString()) .type(type) .data(verifiableCredential) .build(); } - private static VerifiableCredential createVerifiableCredential(String issuerDid, List verifiableCredentialType, VerifiableCredentialSubject verifiableCredentialSubject, byte[] privateKey, List contexts, Date expiryDate) { + private static VerifiableCredential createVerifiableCredential(DidDocument issuerDoc, List verifiableCredentialType, + VerifiableCredentialSubject verifiableCredentialSubject, + byte[] privateKey, List contexts, Date expiryDate) { //VC Builder VerifiableCredentialBuilder builder = new VerifiableCredentialBuilder() .context(contexts) .id(URI.create(UUID.randomUUID().toString())) .type(verifiableCredentialType) - .issuer(URI.create(issuerDid)) + .issuer(issuerDoc.getId()) .expirationDate(expiryDate.toInstant()) .issuanceDate(Instant.now()) .credentialSubject(verifiableCredentialSubject); //Ed25519 Proof Builder - LinkedDataProofGenerator generator = LinkedDataProofGenerator.create(); - Ed25519Signature2020 proof = generator.createEd25519Signature2020(builder.build(), URI.create(issuerDid), privateKey); + LinkedDataProofGenerator generator = new LinkedDataProofGenerator( + new LinkedDataHasher(), new LinkedDataTransformer(), new LinkedDataSigner()); + URI verificationMethod = issuerDoc.getVerificationMethods().get(0).getId(); + Ed25519Signature2020 proof = generator.createEd25519Signature2020(builder.build(), verificationMethod, + privateKey); //Adding Proof to VC builder.proof(proof); diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java index 41c4ba75a..b14706e22 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/did/DidDocumentsTest.java @@ -76,6 +76,35 @@ void getDidDocumentWithBpn200() { Assertions.assertNotNull(response.getBody()); } + @Test + void getDidDocumentWithDid200() { + String bpn = UUID.randomUUID().toString(); + String did = "did:web:localhost:" + bpn; + + createWallet(bpn, did); + ResponseEntity response = restTemplate.getForEntity(RestURI.DID_DOCUMENTS, String.class, did); + Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); + Assertions.assertNotNull(response.getBody()); + } + + @Test + void getDidResolveInvalidBpn404() { + ResponseEntity response = restTemplate.getForEntity(RestURI.DID_RESOLVE, String.class, UUID.randomUUID().toString()); + Assertions.assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode().value()); + } + + @Test + void getDidResolveWithBpn200() { + + String bpn = UUID.randomUUID().toString(); + String did = "did:web:localhost:" + bpn; + + createWallet(bpn, did); + ResponseEntity response = restTemplate.getForEntity(RestURI.DID_RESOLVE, String.class, bpn); + Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); + Assertions.assertNotNull(response.getBody()); + } + private Wallet createWallet(String bpn, String did) { String didDocument = """ { @@ -101,16 +130,4 @@ private Wallet createWallet(String bpn, String did) { .build(); return walletRepository.save(wallet); } - - @Test - void getDidDocumentWithDid200() { - String bpn = UUID.randomUUID().toString(); - String did = "did:web:localhost:" + bpn; - - createWallet(bpn, did); - ResponseEntity response = restTemplate.getForEntity(RestURI.DID_DOCUMENTS, String.class, did); - Assertions.assertEquals(HttpStatus.OK.value(), response.getStatusCode().value()); - Assertions.assertNotNull(response.getBody()); - } - }