diff --git a/build.gradle b/build.gradle index dba0f7ad8..cb7dbebcc 100644 --- a/build.gradle +++ b/build.gradle @@ -45,6 +45,7 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' implementation "org.springdoc:springdoc-openapi-starter-common:${openApiVersion}" implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:${openApiVersion}" + implementation group: 'com.smartsensesolutions', name: 'commons-dao', version: '0.0.2' implementation 'org.liquibase:liquibase-core' implementation 'org.eclipse.tractusx.ssi.lib:cx-ssi-lib:0.0.4' runtimeOnly 'org.postgresql:postgresql' diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java index faf991719..f67053adc 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/ManagedIdentityWalletsApplication.java @@ -24,12 +24,14 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; +import org.springframework.transaction.annotation.EnableTransactionManagement; /** * The type Managed identity wallets application. */ @SpringBootApplication @ConfigurationPropertiesScan +@EnableTransactionManagement public class ManagedIdentityWalletsApplication { /** diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java index edaea9ba7..0201952a9 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java @@ -31,6 +31,7 @@ * The type Miw settings. */ @ConfigurationProperties(prefix = "miw") -public record MIWSettings(String host, String encryptionKey, String authorityWalletBpn, String authorityWalletName, +public record MIWSettings(String host, String encryptionKey, String authorityWalletBpn, String authorityWalletDid, + String authorityWalletName, List vcContexts, @DateTimeFormat(pattern = "dd-MM-yyyy") Date vcExpiryDate) { } 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 0343ad61a..30bbcefaf 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/RestURI.java @@ -67,4 +67,6 @@ private RestURI() { */ public static final String API_CREDENTIALS_ISSUER_FRAMEWORK = "/api/credentials/issuer/framework"; + public static final String API_PRESENTATIONS = "/api/presentations"; + } 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 8bea2720c..f69c7755b 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/CredentialController.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/CredentialController.java @@ -26,13 +26,11 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.Credential; import org.eclipse.tractusx.managedidentitywallets.dto.IssueDismantlerCredentialRequest; import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; import org.eclipse.tractusx.managedidentitywallets.service.CredentialService; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; -import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -61,14 +59,12 @@ public class CredentialController { */ @Operation(description = "Permission: **view_wallets** OR **view_wallet** (The BPN of holderIdentifier must equal BPN of caller)\n\n Search verifiable credentials with filter criteria", summary = "Query Verifiable Credentials") @GetMapping(path = RestURI.CREDENTIALS, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity> getCredentials(@RequestParam(required = false) String holderIdentifier, @RequestParam(required = false) String id, - @RequestParam(required = false) String issuerIdentifier, - @RequestParam(required = false) List type, - @RequestParam(required = false, defaultValue = "0") int pageNumber, - @RequestParam(required = false, defaultValue = Integer.MAX_VALUE + "") int size, - @RequestParam(required = false, defaultValue = "createdAt") String sortColumn, - @RequestParam(required = false, defaultValue = "desc") String sortTpe) { - return ResponseEntity.status(HttpStatus.OK).body(service.getCredentials(holderIdentifier, id, issuerIdentifier, type, pageNumber, size, sortColumn, sortTpe)); + public ResponseEntity> getCredentials(@RequestParam(required = false) String holderIdentifier, @RequestParam(required = false) String id, + @RequestParam(required = false) String issuerIdentifier, + @RequestParam(required = false) List type, + @RequestParam(required = false, defaultValue = "createdAt") String sortColumn, + @RequestParam(required = false, defaultValue = "desc") String sortTpe) { + return ResponseEntity.status(HttpStatus.OK).body(service.getCredentials(holderIdentifier, id, issuerIdentifier, type, sortColumn, sortTpe)); } /** diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java new file mode 100644 index 000000000..25e80e543 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/PresentationController.java @@ -0,0 +1,88 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,2023 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.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 lombok.RequiredArgsConstructor; +import org.eclipse.tractusx.managedidentitywallets.constant.RestURI; +import org.eclipse.tractusx.managedidentitywallets.service.PresentationService; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RestController +@RequiredArgsConstructor +@Tag(name = "VerifiablePresentations") +public class PresentationController { + + private final PresentationService presentationService; + + @Operation(summary = "Create Verifiable Presentation", description = "Permission: **update_wallets** OR **update_wallet** (The BPN of the issuer of the Verifiable Presentation must equal to BPN of caller) \n\n Create a verifiable presentation from a list of verifiable credentials, signed by the holder") + @PostMapping(path = RestURI.API_PRESENTATIONS, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + + @io.swagger.v3.oas.annotations.parameters.RequestBody(content = { + @Content(examples = @ExampleObject(""" + { + "holderIdentifier": "did:example:76e12ec712ebc6f1c221ebfeb1f", + "verifiableCredentials": [ + { + "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, + @RequestParam(name = "asJwt", required = false, defaultValue = "false") boolean asJwt + ) { + return ResponseEntity.status(HttpStatus.CREATED).body(presentationService.createPresentation(data, asJwt, "smartSense")); + } +} 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 feb15ccee..087294d59 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CredentialService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/CredentialService.java @@ -28,64 +28,46 @@ import com.smartsensesolutions.java.commons.sort.Sort; import com.smartsensesolutions.java.commons.sort.SortType; import com.smartsensesolutions.java.commons.specification.SpecificationUtil; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.bouncycastle.util.io.pem.PemReader; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Credential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; import org.eclipse.tractusx.managedidentitywallets.dao.repository.CredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; import org.eclipse.tractusx.managedidentitywallets.dto.IssueDismantlerCredentialRequest; import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateCredentialProblem; -import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; +import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; -import org.eclipse.tractusx.ssi.lib.model.Ed25519Signature2020; 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.springframework.context.annotation.Lazy; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.io.StringReader; -import java.net.URI; import java.time.Instant; +import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.UUID; /** * The type Credential service. */ @Service @Slf4j +@RequiredArgsConstructor public class CredentialService extends BaseService { private final CredentialRepository credentialRepository; private final MIWSettings miwSettings; - private final WalletKeyRepository walletKeyRepository; - private final EncryptionUtils encryptionUtils; private final WalletService walletService; private final SpecificationUtil credentialSpecificationUtil; - public CredentialService(CredentialRepository credentialRepository, MIWSettings miwSettings, WalletKeyRepository walletKeyRepository, EncryptionUtils encryptionUtils, @Lazy WalletService walletService, SpecificationUtil credentialSpecificationUtil) { - this.credentialRepository = credentialRepository; - this.miwSettings = miwSettings; - this.walletKeyRepository = walletKeyRepository; - this.encryptionUtils = encryptionUtils; - this.walletService = walletService; - this.credentialSpecificationUtil = credentialSpecificationUtil; - } + private final WalletKeyService walletKeyService; @Override @@ -98,6 +80,7 @@ protected SpecificationUtil getSpecificationUtil() { return credentialSpecificationUtil; } + /** * Gets credentials. * @@ -105,12 +88,14 @@ protected SpecificationUtil getSpecificationUtil() { * @param id the id * @param issuerIdentifier the issuer identifier * @param type the type + * @param pageNumber the page number + * @param size the size + * @param sortColumn the sort column + * @param sortType the sort type * @return the credentials */ - public Page getCredentials(String holderIdentifier, String id, String issuerIdentifier, List type, int pageNumber, int size, String sortColumn, String sortType) { + public List getCredentials(String holderIdentifier, String id, String issuerIdentifier, List type, String sortColumn, String sortType) { FilterRequest filterRequest = new FilterRequest(); - filterRequest.setPage(pageNumber); - filterRequest.setSize(size); if (StringUtils.hasText(holderIdentifier)) { Wallet holderWallet = walletService.getWalletByIdentifier(holderIdentifier); filterRequest.appendNewCriteria("holderDid", Operator.EQUALS, holderWallet.getDid()); @@ -129,9 +114,17 @@ public Page getCredentials(String holderIdentifier, String id, Strin sort.setColumn(sortColumn); sort.setSortType(SortType.valueOf(sortType.toUpperCase())); filterRequest.setSort(sort); - return filter(filterRequest); + Page filter = filter(filterRequest); + + List list = new ArrayList<>(filter.getContent().size()); + for (Credential credential : filter.getContent()) { + list.add(credential.getData()); + } + + return list; } + /** * Issue framework credential verifiable credential. * @@ -142,16 +135,18 @@ public VerifiableCredential issueFrameworkCredential(IssueFrameworkCredentialReq //Fetch Holder Wallet Wallet holderWallet = walletService.getWalletByIdentifier(request.getBpn()); - // Fetch Issuer Wallet Wallet baseWallet = walletService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); - byte[] privateKeyBytes = getPrivateKeyById(baseWallet.getId()); + + + // get Key + byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifier(baseWallet.getId()); Map subject = Map.of("type", request.getType(), "id", holderWallet.getDid(), "value", request.getValue(), "contract-template", request.getContractTemplate(), "contract-version", request.getContractVersion()); - Credential credential = getCredential(subject, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION_CX, baseWallet, privateKeyBytes, holderWallet); + Credential credential = CommonUtils.getCredential(subject, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION_CX, miwSettings.authorityWalletDid(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credential = create(credential); @@ -171,19 +166,20 @@ public VerifiableCredential issueDismantlerCredential(IssueDismantlerCredentialR //Fetch Holder Wallet Wallet holderWallet = walletService.getWalletByIdentifier(request.getBpn()); + // Fetch Issuer Wallet + Wallet baseWallet = walletService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); + //check duplicate isCredentialExit(holderWallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL_CX); - // Fetch Issuer Wallet - Wallet baseWallet = walletService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); - byte[] privateKeyBytes = getPrivateKeyById(baseWallet.getId()); + byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifier(baseWallet.getId()); Map subject = Map.of("type", MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL, "id", holderWallet.getDid(), "holderIdentifier", holderWallet.getBpn(), "activityType", request.getActivityType(), "allowedVehicleBrands", request.getAllowedVehicleBrands()); - Credential credential = getCredential(subject, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL_CX, baseWallet, privateKeyBytes, holderWallet); + Credential credential = CommonUtils.getCredential(subject, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL_CX, miwSettings.authorityWalletDid(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credential = create(credential); @@ -209,15 +205,15 @@ public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialR // Fetch Issuer Wallet Wallet baseWallet = walletService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); - byte[] privateKeyBytes = getPrivateKeyById(baseWallet.getId()); + byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifier(baseWallet.getId()); //VC Subject - Credential credential = getCredential(Map.of("type", MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL, + Credential credential = CommonUtils.getCredential(Map.of("type", MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL, "id", holderWallet.getDid(), "holderIdentifier", holderWallet.getBpn(), "memberOf", baseWallet.getName(), "status", "Active", - "startTime", Instant.now().toString()), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL_CX, baseWallet, privateKeyBytes, holderWallet); + "startTime", Instant.now().toString()), MIWVerifiableCredentialType.MEMBERSHIP_CREDENTIAL_CX, miwSettings.authorityWalletDid(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credential = create(credential); @@ -226,58 +222,8 @@ public VerifiableCredential issueMembershipCredential(IssueMembershipCredentialR return credential.getData(); } - private VerifiableCredential createVerifiableCredential(String issuerDid, List verifiableCredentialType, VerifiableCredentialSubject verifiableCredentialSubject, byte[] privateKey) { - //VC Builder - VerifiableCredentialBuilder builder = - new VerifiableCredentialBuilder() - .context(miwSettings.vcContexts()) - .id(URI.create(UUID.randomUUID().toString())) - .type(verifiableCredentialType) - .issuer(URI.create(issuerDid)) - .expirationDate(miwSettings.vcExpiryDate().toInstant()) - .issuanceDate(Instant.now()) - .credentialSubject(verifiableCredentialSubject); - - - //Ed25519 Proof Builder - LinkedDataProofGenerator generator = LinkedDataProofGenerator.create(); - Ed25519Signature2020 proof = generator.createEd25519Signature2020(builder.build(), URI.create(issuerDid), privateKey); - - //Adding Proof to VC - builder.proof(proof); - - //Create Credential - return builder.build(); - } - - - public Credential getCredential(Map subject, String type, Wallet baseWallet, byte[] privateKeyBytes, Wallet holderWallet) { - //VC Subject - VerifiableCredentialSubject verifiableCredentialSubject = - new VerifiableCredentialSubject(subject); - - // VC Type - List verifiableCredentialType = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, type); - - // Create VC - VerifiableCredential verifiableCredential = createVerifiableCredential(baseWallet.getDid(), verifiableCredentialType, verifiableCredentialSubject, privateKeyBytes); - - // Create Credential - return Credential.builder() - .holderDid(holderWallet.getDid()) - .issuerDid(baseWallet.getDid()) - .type(type) - .data(verifiableCredential) - .build(); - } - @SneakyThrows - public byte[] getPrivateKeyById(Long id) { - WalletKey baseWalletKey = walletKeyRepository.getByWalletId(id); - String privateKey = encryptionUtils.decrypt(baseWalletKey.getPrivateKey()); - return new PemReader(new StringReader(privateKey)).readPemObject().getContent(); - } private void isCredentialExit(String holderDid, String credentialType) { Validate.isTrue(credentialRepository.existsByHolderDidAndType(holderDid, credentialType)).launch(new DuplicateCredentialProblem("Credential of type " + credentialType + " is already exists ")); diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java new file mode 100644 index 000000000..243f59bc7 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java @@ -0,0 +1,132 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,2023 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.managedidentitywallets.service; + +import com.nimbusds.jwt.SignedJWT; +import com.smartsensesolutions.java.commons.base.repository.BaseRepository; +import com.smartsensesolutions.java.commons.base.service.BaseService; +import com.smartsensesolutions.java.commons.specification.SpecificationUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Credential; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.CredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.utils.Validate; +import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtFactory; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.did.DidParser; +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; +import org.eclipse.tractusx.ssi.lib.model.verifiable.presentation.VerifiablePresentationType; +import org.eclipse.tractusx.ssi.lib.resolver.OctetKeyPairFactory; +import org.eclipse.tractusx.ssi.lib.serialization.jsonLd.JsonLdSerializerImpl; +import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtPresentationFactory; +import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtPresentationFactoryImpl; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +@RequiredArgsConstructor +public class PresentationService extends BaseService { + + private final CredentialRepository credentialRepository; + + + private final SpecificationUtil credentialSpecificationUtil; + + private final WalletService walletService; + + private final WalletKeyService walletKeyService; + + @Override + protected BaseRepository getRepository() { + return credentialRepository; + } + + @Override + protected SpecificationUtil getSpecificationUtil() { + return credentialSpecificationUtil; + } + + public Map createPresentation(Map data, boolean asJwt, String audience) { + Map response = new HashMap<>(); + + String holderIdentifier = data.get("holderIdentifier").toString(); + + //check if holder wallet is in the system + Wallet holderWallet = walletService.getWalletByIdentifier(holderIdentifier); + + List> verifiableCredentialList = (List>) data.get("verifiableCredentials"); + + List verifiableCredentials = new ArrayList<>(verifiableCredentialList.size()); + + verifiableCredentialList.forEach(map -> { + VerifiableCredential verifiableCredential = new VerifiableCredential(map); + validateCredential(verifiableCredential, holderIdentifier); + + verifiableCredentials.add(verifiableCredential); + }); + + + Did issuerDid = DidParser.parse(verifiableCredentials.get(0).getIssuer()); //TODO need to discuss, what if we have credentials with separate issuer? + + if (asJwt) { + + //JWT Factory + SerializedJwtPresentationFactory presentationFactory = new SerializedJwtPresentationFactoryImpl( + new SignedJwtFactory(new OctetKeyPairFactory()), new JsonLdSerializerImpl(), issuerDid); + + //Build JWT + SignedJWT presentation = presentationFactory.createPresentation( + issuerDid, verifiableCredentials, audience, walletKeyService.getEd25519Key(holderWallet.getId())); + response.put("vp", presentation); + } else { + VerifiablePresentationBuilder verifiablePresentationBuilder = + new VerifiablePresentationBuilder(); + + // Build VP + VerifiablePresentation verifiablePresentation = + verifiablePresentationBuilder + .id(issuerDid.toUri()) + .type(List.of(VerifiablePresentationType.VERIFIABLE_PRESENTATION)) + .verifiableCredentials(verifiableCredentials) + .build(); + response.put("vp", verifiablePresentation); + } + return response; + } + + private void validateCredential(VerifiableCredential verifiableCredential, String holderIdentifier) { + + //check holders + Validate.isFalse(verifiableCredential.getCredentialSubject().get(0).get("id").toString().equals(holderIdentifier)).launch(new ForbiddenException("VC " + verifiableCredential.getTypes() + " is not match with holder identifier " + holderIdentifier)); + + //TODO need to validate policies + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java new file mode 100644 index 000000000..64b946e25 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java @@ -0,0 +1,74 @@ +/* + * ******************************************************************************* + * Copyright (c) 2021,2023 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.managedidentitywallets.service; + +import com.smartsensesolutions.java.commons.base.repository.BaseRepository; +import com.smartsensesolutions.java.commons.base.service.BaseService; +import com.smartsensesolutions.java.commons.specification.SpecificationUtil; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.util.io.pem.PemReader; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; +import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; +import org.eclipse.tractusx.ssi.lib.crypt.ed25519.Ed25519Key; +import org.springframework.stereotype.Service; + +import java.io.StringReader; + +@Service +@Slf4j +@RequiredArgsConstructor +public class WalletKeyService extends BaseService { + + private final WalletKeyRepository walletKeyRepository; + + private final SpecificationUtil specificationUtil; + + private final EncryptionUtils encryptionUtils; + + @Override + protected BaseRepository getRepository() { + return walletKeyRepository; + } + + @Override + protected SpecificationUtil getSpecificationUtil() { + return specificationUtil; + } + + + @SneakyThrows + public byte[] getPrivateKeyByWalletIdentifier(long walletId) { + WalletKey wallet = walletKeyRepository.getByWalletId(walletId); + String privateKey = encryptionUtils.decrypt(wallet.getPrivateKey()); + return new PemReader(new StringReader(privateKey)).readPemObject().getContent(); + } + + @SneakyThrows + public Ed25519Key getEd25519Key(long walletId) { + return new Ed25519Key(getPrivateKeyByWalletIdentifier(walletId)); + + } + +} 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 4e72e922a..4b9dba3df 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java @@ -46,7 +46,6 @@ import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; import org.eclipse.tractusx.managedidentitywallets.dao.repository.CredentialRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; @@ -64,6 +63,9 @@ import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; import java.io.StringWriter; import java.net.URI; @@ -78,8 +80,8 @@ * The type Wallet service. */ @Service -@RequiredArgsConstructor @Slf4j +@RequiredArgsConstructor public class WalletService extends BaseService { private final WalletRepository walletRepository; @@ -88,12 +90,10 @@ public class WalletService extends BaseService { private final EncryptionUtils encryptionUtils; - private final WalletKeyRepository walletKeyRepository; + private final WalletKeyService walletKeyService; private final CredentialRepository credentialRepository; - private final CredentialService credentialService; - private final SpecificationUtil walletSpecificationUtil; @Override @@ -195,9 +195,11 @@ public Page getWallets(int pageNumber, int size, String sortColumn, Stri * @return the wallet */ @SneakyThrows + @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) public Wallet createWallet(CreateWalletRequest request) { validateCreateWallet(request); + //create private key pair Ed25519KeySet keyPair = createKeyPair(); @@ -236,7 +238,7 @@ public Wallet createWallet(CreateWalletRequest request) { .build()); //Save key - walletKeyRepository.save(WalletKey.builder() + walletKeyService.getRepository().save(WalletKey.builder() .walletId(wallet.getId()) .referenceKey("dummy ref key") //TODO removed once vault setup is ready .vaultAccessToken("dummy vault access token") ////TODO removed once vault setup is ready @@ -245,27 +247,28 @@ public Wallet createWallet(CreateWalletRequest request) { .build()); log.debug("Wallet created for bpn ->{}", request.getBpn()); - // Fetch Issuer Wallet + //issue BPN credentials`` Wallet baseWallet = getWalletByIdentifier(miwSettings.authorityWalletBpn()); - byte[] privateKeyBytes = credentialService.getPrivateKeyById(baseWallet.getId()); + byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdentifier(baseWallet.getId()); - Credential credential = credentialService.getCredential(Map.of("type", MIWVerifiableCredentialType.BPN_CREDENTIAL, + Credential credential = CommonUtils.getCredential(Map.of("type", MIWVerifiableCredentialType.BPN_CREDENTIAL, "id", wallet.getDid(), - "bpn", wallet.getBpn()), MIWVerifiableCredentialType.BPN_CREDENTIAL_CX, baseWallet, privateKeyBytes, wallet); + "bpn", wallet.getBpn()), MIWVerifiableCredentialType.BPN_CREDENTIAL_CX, miwSettings.authorityWalletDid(), privateKeyBytes, wallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate()); //Store Credential credentialRepository.save(credential); + log.debug("BPN credential issued for bpn -{}", request.getBpn()); return wallet; } /** - * Create authority wallet. + * Create authority wallet on application start up, skip if already created. */ @PostConstruct + @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) public void createAuthorityWallet() { - boolean exist = walletRepository.existsByBpn(miwSettings.authorityWalletBpn()); - if (!exist) { + if (!walletRepository.existsByBpn(miwSettings.authorityWalletBpn())) { CreateWalletRequest request = CreateWalletRequest.builder() .name(miwSettings.authorityWalletName()) .bpn(miwSettings.authorityWalletBpn()) 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 57a58cde3..445df4358 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java @@ -22,6 +22,20 @@ package org.eclipse.tractusx.managedidentitywallets.utils; 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.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 java.net.URI; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.UUID; /** * The type Common utils. @@ -45,4 +59,60 @@ public static String getIdentifierType(String identifier) { return ApplicationConstant.BPN; } } + + + /** + * Gets credential. + * + * @param subject the subject + * @param type the type + * @param issuerDid the issuer did + * @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) { + //VC Subject + VerifiableCredentialSubject verifiableCredentialSubject = + new VerifiableCredentialSubject(subject); + + // VC Type + List verifiableCredentialType = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, type); + + // Create VC + VerifiableCredential verifiableCredential = createVerifiableCredential(issuerDid, verifiableCredentialType, verifiableCredentialSubject, privateKeyBytes, contexts, expiryDate); + + // Create Credential + return Credential.builder() + .holderDid(holderDid) + .issuerDid(issuerDid) + .type(type) + .data(verifiableCredential) + .build(); + } + + + private static VerifiableCredential createVerifiableCredential(String issuerDid, 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)) + .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); + + //Adding Proof to VC + builder.proof(proof); + + //Create Credential + return builder.build(); + } } diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml index ae49b9e55..975cfc05a 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test.yaml @@ -1,5 +1,6 @@ spring: - + main: + allow-circular-references: true miw: host: localhost encryptionKey: ohb7echohNe3zoo1so0eiC2phue4liux