diff --git a/CHANGELOG.md b/CHANGELOG.md index 205fb14b3..ab94157d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # [0.5.0-develop.17](https://github.com/eclipse-tractusx/managed-identity-wallet/compare/v0.5.0-develop.16...v0.5.0-develop.17) (2024-05-29) + ### Bug Fixes * api doc folder structure ([ebd691a](https://github.com/eclipse-tractusx/managed-identity-wallet/commit/ebd691a8a5c05f26a6aa10b778d4c8be6189a4af)) diff --git a/README.md b/README.md index 131d6e078..25f6e815e 100644 --- a/README.md +++ b/README.md @@ -313,6 +313,8 @@ This process ensures that any issues with the database schema are resolved by re | ENFORCE_HTTPS_IN_DID_RESOLUTION | Enforce https during web did resolution | true | | CONTRACT_TEMPLATES_URL | Contract templates URL used in summary VC | https://public.catena-x.org/contracts/ | | APP_LOG_LEVEL | Log level of application | INFO | +| AUTHORITY_SIGNING_SERVICE_TYPE | Base wallet signing type, Currency only LOCAL is supported | Local | +| LOCAL_SIGNING_KEY_STORAGE_TYPE | Key storage type, currently only DB is supported | DB | | | | | # Technical Debts and Known issue diff --git a/dev-assets/env-files/env.docker.dist b/dev-assets/env-files/env.docker.dist index d6c8204c8..aeedf42a1 100644 --- a/dev-assets/env-files/env.docker.dist +++ b/dev-assets/env-files/env.docker.dist @@ -26,6 +26,8 @@ ENCRYPTION_KEY= AUTHORITY_WALLET_BPN=BPNL000000000000 AUTHORITY_WALLET_DID=did:web:localhost:BPNL000000000000 AUTHORITY_WALLET_NAME=Catena-X +AUTHORITY_SIGNING_SERVICE_TYPE=LOCAL +LOCAL_SIGNING_KEY_STORAGE_TYPE=DB KEYCLOAK_REALM=miw_test VC_SCHEMA_LINK="https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json" VC_EXPIRY_DATE=01-01-2025 diff --git a/dev-assets/env-files/env.local.dist b/dev-assets/env-files/env.local.dist index 68ffa0547..30c20135a 100644 --- a/dev-assets/env-files/env.local.dist +++ b/dev-assets/env-files/env.local.dist @@ -26,6 +26,8 @@ ENCRYPTION_KEY= AUTHORITY_WALLET_BPN=BPNL000000000000 AUTHORITY_WALLET_DID=did:web:localhost:BPNL000000000000 AUTHORITY_WALLET_NAME=Catena-X +AUTHORITY_SIGNING_SERVICE_TYPE=LOCAL +LOCAL_SIGNING_KEY_STORAGE_TYPE=DB KEYCLOAK_REALM=miw_test VC_SCHEMA_LINK="https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json" VC_EXPIRY_DATE=01-01-2025 diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java index a8cb28daf..9cefda803 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/ApplicationConfig.java @@ -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. @@ -28,6 +28,10 @@ import com.smartsensesolutions.java.commons.specification.SpecificationUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.text.StringEscapeUtils; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.signing.KeyProvider; +import org.eclipse.tractusx.managedidentitywallets.signing.LocalSigningService; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.springdoc.core.properties.SwaggerUiConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -40,6 +44,9 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; /** * The type Application config. @@ -50,11 +57,13 @@ public class ApplicationConfig implements WebMvcConfigurer { private final SwaggerUiConfigProperties properties; private final String resourceBundlePath; + private final MIWSettings miwSettings; @Autowired - public ApplicationConfig(@Value("${resource.bundle.path:classpath:i18n/language}") String resourceBundlePath, SwaggerUiConfigProperties properties) { + public ApplicationConfig(@Value("${resource.bundle.path:classpath:i18n/language}") String resourceBundlePath, SwaggerUiConfigProperties properties, MIWSettings miwSettings) { this.resourceBundlePath = resourceBundlePath; this.properties = properties; + this.miwSettings = miwSettings; } /** @@ -98,4 +107,23 @@ public LocalValidatorFactoryBean validator() { beanValidatorFactory.setValidationMessageSource(messageSource()); return beanValidatorFactory; } + + @Bean + public Map availableKeyStorages(List storages, List keyProviders) { + KeyProvider localSigningKeyProvider = keyProviders.stream().filter(s -> s.getKeyStorageType().equals(miwSettings.localSigningKeyStorageType())) + .findFirst() + .orElseThrow(() -> new IllegalStateException("no key provider with type %s found".formatted(miwSettings.localSigningKeyStorageType()))); + + Map available = new EnumMap<>(SigningServiceType.class); + storages.forEach( + s -> { + if(s instanceof LocalSigningService local){ + local.setKeyProvider(localSigningKeyProvider); + } + available.put(s.getSupportedServiceType(), s); + } + ); + + return available; + } } 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 2a4401651..6a57603ba 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/config/MIWSettings.java @@ -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. @@ -21,6 +21,8 @@ package org.eclipse.tractusx.managedidentitywallets.config; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyStorageType; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.format.annotation.DateTimeFormat; @@ -39,5 +41,8 @@ public record MIWSettings(String host, String encryptionKey, String authorityWal @DateTimeFormat(pattern = "dd-MM-yyyy") Date vcExpiryDate, Set supportedFrameworkVCTypes, boolean enforceHttps, String contractTemplatesUrl, - List didDocumentContextUrls) { + List didDocumentContextUrls, + KeyStorageType localSigningKeyStorageType, + SigningServiceType authoritySigningServiceType) { } + diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/MIWVerifiableCredentialType.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/MIWVerifiableCredentialType.java index 62597d2b8..e7f166b44 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/MIWVerifiableCredentialType.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/MIWVerifiableCredentialType.java @@ -21,9 +21,12 @@ package org.eclipse.tractusx.managedidentitywallets.constant; +import lombok.experimental.UtilityClass; + /** * The type Miw verifiable credential type. */ +@UtilityClass public class MIWVerifiableCredentialType { public static final String VERIFIABLE_CREDENTIAL = "VerifiableCredential"; diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java index e9328f539..11644fee8 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/constant/StringPool.java @@ -93,6 +93,8 @@ private StringPool() { public static final String BPN_NUMBER_REGEX = "^(BPN)(L|S|A)[0-9A-Z]{12}"; + public static final String W3_ID_JWS_2020_V1_CONTEXT_URL = "https://w3id.org/security/suites/jws-2020/v1"; + public static final String COMA_SEPARATOR = ", "; public static final String BLANK_SEPARATOR = " "; public static final String COLON_SEPARATOR = ":"; diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenController.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenController.java index 42ca8b94e..3cfd78657 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenController.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/controller/SecureTokenController.java @@ -31,10 +31,12 @@ import org.apache.commons.lang3.StringUtils; import org.eclipse.tractusx.managedidentitywallets.apidocs.SecureTokenControllerApiDoc; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; import org.eclipse.tractusx.managedidentitywallets.domain.DID; import org.eclipse.tractusx.managedidentitywallets.domain.IdpTokenResponse; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.domain.StsTokenErrorResponse; import org.eclipse.tractusx.managedidentitywallets.domain.StsTokenResponse; import org.eclipse.tractusx.managedidentitywallets.dto.SecureTokenRequest; @@ -42,8 +44,8 @@ import org.eclipse.tractusx.managedidentitywallets.exception.InvalidSecureTokenRequestException; import org.eclipse.tractusx.managedidentitywallets.exception.UnknownBusinessPartnerNumberException; import org.eclipse.tractusx.managedidentitywallets.exception.UnsupportedGrantTypeException; -import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; import org.eclipse.tractusx.managedidentitywallets.service.IdpAuthorization; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.eclipse.tractusx.managedidentitywallets.validator.SecureTokenRequestValidator; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -57,6 +59,7 @@ import org.springframework.web.bind.annotation.RestController; import java.text.ParseException; +import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -69,17 +72,19 @@ @Tag(name = "STS") public class SecureTokenController { - private final SecureTokenService tokenService; private final IdpAuthorization idpAuthorization; private final WalletRepository walletRepo; + private final Map availableSigningServices; + @InitBinder void initBinder(WebDataBinder webDataBinder) { webDataBinder.addValidators(new SecureTokenRequestValidator()); } + @SneakyThrows @PostMapping(path = "/api/token", consumes = { MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE }) @SecureTokenControllerApiDoc.PostSecureTokenDocJson @@ -103,7 +108,8 @@ private ResponseEntity processTokenRequest(SecureTokenRequest // handle idp authorization IdpTokenResponse idpResponse = idpAuthorization.fromSecureTokenRequest(secureTokenRequest); BusinessPartnerNumber bpn = idpResponse.bpn(); - DID selfDid = new DID(walletRepo.getByBpn(bpn.toString()).getDid()); + Wallet selfWallet = walletRepo.getByBpn(bpn.toString()); + DID selfDid = new DID(selfWallet.getDid()); DID partnerDid; if (Pattern.compile(StringPool.BPN_NUMBER_REGEX).matcher(secureTokenRequest.getAudience()).matches()) { partnerDid = new DID(walletRepo.getByBpn(secureTokenRequest.getAudience()).getDid()); @@ -113,18 +119,21 @@ private ResponseEntity processTokenRequest(SecureTokenRequest throw new InvalidSecureTokenRequestException("You must provide an audience either as a BPN or DID."); } + SigningServiceType signingServiceType = selfWallet.getSigningServiceType(); + SigningService signingService = availableSigningServices.get(signingServiceType); + // create the SI token and put/create the access_token inside JWT responseJwt; if (secureTokenRequest.assertValidWithAccessToken()) { log.debug("Signing si token."); - responseJwt = tokenService.issueToken( + responseJwt = signingService.issueToken( selfDid, partnerDid, JWTParser.parse(secureTokenRequest.getAccessToken()) ); } else if (secureTokenRequest.assertValidWithScopes()) { log.debug("Creating access token and signing si token."); - responseJwt = tokenService.issueToken( + responseJwt = signingService.issueToken( selfDid, partnerDid, Set.of(secureTokenRequest.getBearerAccessScope()) diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java index 247bf5fcc..b3db2d121 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/Wallet.java @@ -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. @@ -27,6 +27,8 @@ import jakarta.persistence.Column; import jakarta.persistence.Convert; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -37,6 +39,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.utils.StringToDidDocumentConverter; import org.eclipse.tractusx.ssi.lib.model.did.DidDocument; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; @@ -72,6 +75,10 @@ public class Wallet extends MIWBaseEntity { @Column(nullable = false) private String algorithm; + @Enumerated(EnumType.STRING) + @Column(name = "signing_service_type", nullable = false) + private SigningServiceType signingServiceType; + @Column(nullable = false) @Convert(converter = StringToDidDocumentConverter.class) private DidDocument didDocument; diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java index 49aabf946..d53a38698 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/entity/WalletKey.java @@ -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. @@ -30,7 +30,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import jakarta.persistence.MapsId; +import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -47,13 +47,14 @@ @AllArgsConstructor @NoArgsConstructor @Builder +@Table(name="wallet_key") public class WalletKey extends MIWBaseEntity { @Id @JsonIgnore @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", columnDefinition = "serial", nullable = false, unique = true) - private Long id; + private long id; @Column(nullable = false) private String vaultAccessToken; @@ -68,8 +69,7 @@ public class WalletKey extends MIWBaseEntity { private String publicKey; @ManyToOne - @MapsId - @JoinColumn(name = "walletId", columnDefinition = "bigint") + @JoinColumn(name = "wallet_id", columnDefinition = "bigint") @JsonBackReference private Wallet wallet; diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java index 00312c9f3..54196d73b 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dao/repository/WalletKeyRepository.java @@ -33,20 +33,43 @@ public interface WalletKeyRepository extends BaseRepository { /** * Gets by wallet id and algorithm. * - * @param id the id - * param algorithm the algorithm + * @param id the wallet id + * @param algorithm the algorithm * @return the by wallet id */ WalletKey getByWalletIdAndAlgorithm(Long id, String algorithm); /** - * Gets by wallet id. - * @param id - * @return WalletKey + * Find first by wallet bpn wallet key. + * + * @param bpn the bpn + * @return the wallet key */ - WalletKey getByWalletId(Long id); - WalletKey findFirstByWallet_Bpn(String bpn); + /** + * Find first by wallet did wallet key. + * + * @param did the did + * @return the wallet key + */ WalletKey findFirstByWallet_Did(String did); + + /** + * Gets by key id and algorithm. + * + * @param keyId the key id + * @param algorithm the algorithm + * @return the by key id and algorithm + */ + WalletKey getByKeyIdAndAlgorithm(String keyId, String algorithm); + + /** + * Gets by algorithm and wallet bpn. + * + * @param name the name + * @param keyName the key name + * @return the by algorithm and wallet bpn + */ + WalletKey getByAlgorithmAndWallet_Bpn(String name, String keyName); } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfig.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfig.java new file mode 100644 index 000000000..f75ae464b --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfig.java @@ -0,0 +1,96 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +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.VerifiableCredentialStatus; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; + +import java.net.URI; +import java.util.Date; +import java.util.List; + +@Builder +@Getter +public class CredentialCreationConfig { + + @NonNull + private VerifiableCredentialSubject subject; + + private VerifiableCredentialStatus verifiableCredentialStatus; + + @NonNull + private DidDocument issuerDoc; + + @NonNull + private String holderDid; + + @NonNull + private List types; + + @NonNull + private List contexts; + + @NonNull + private SupportedAlgorithms algorithm; + + private URI vcId; + + private Date expiryDate; + + private boolean selfIssued; + + // this will be used by the DB-Impl of storage to retrieve privateKey + @NonNull + private String keyName; + + @NonNull + @Setter + private VerifiableEncoding encoding; + + @Setter + //This is used when we issue VC as JWT + private VerifiableCredential verifiableCredential; + + public static class CredentialCreationConfigBuilder { + public CredentialCreationConfigBuilder vcId(Object object) { + if (!(object instanceof URI) && !(object instanceof String)) { + throw new IllegalArgumentException("vcId must be of type String or URI, argument has type%s".formatted(object.getClass().getName())); + } + + if (object instanceof URI uri) { + this.vcId = uri; + } else { + this.vcId = URI.create((String) object); + } + + return this; + + } + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyCreationConfig.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyCreationConfig.java new file mode 100644 index 000000000..59d9b1ba7 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyCreationConfig.java @@ -0,0 +1,43 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.domain; + +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.KeyType; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; + +import java.util.List; + +@Builder +@Getter +public class KeyCreationConfig { + + @NonNull + private String keyName; + + private Curve curve; + + @NonNull + private List keyTypes; +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyStorageType.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyStorageType.java new file mode 100644 index 000000000..1fd489de4 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/KeyStorageType.java @@ -0,0 +1,27 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.domain; + +public enum KeyStorageType { + DB, + REMOTE +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/PresentationCreationConfig.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/PresentationCreationConfig.java new file mode 100644 index 000000000..0ff581e0a --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/PresentationCreationConfig.java @@ -0,0 +1,59 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.domain; + +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.ssi.lib.model.did.Did; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; + +import java.net.URI; +import java.util.List; + +@Builder +@Getter +public class PresentationCreationConfig { + + @NonNull + private VerifiableEncoding encoding; + + @NonNull + private String keyName; + + @NonNull + private List verifiableCredentials; + + @NonNull + private Did vpIssuerDid; + + // all for JWT + private String audience; + + // all for JsonLD + URI verificationMethod; + + @NonNull + private SupportedAlgorithms algorithm; + +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/SigningServiceType.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/SigningServiceType.java new file mode 100644 index 000000000..1b31137a5 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/SigningServiceType.java @@ -0,0 +1,28 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.domain; + +public enum SigningServiceType { + LOCAL, + AZURE, + REMOTE +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/VerifiableEncoding.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/VerifiableEncoding.java new file mode 100644 index 000000000..64a1e23cf --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/domain/VerifiableEncoding.java @@ -0,0 +1,27 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.domain; + +public enum VerifiableEncoding { + JWT, + JSON_LD +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java index 42614d0a6..4d5b253d6 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/dto/CreateWalletRequest.java @@ -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. @@ -24,8 +24,13 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; -import lombok.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; /** @@ -49,4 +54,6 @@ public class CreateWalletRequest { @NotBlank(message = "Please provide url") @Size(min = 1, max = 2000, message = "Please provide url") private String didUrl; + + private SigningServiceType signingServiceType = SigningServiceType.LOCAL; } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenService.java index 22708870a..af35ed92c 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/interfaces/SecureTokenService.java @@ -25,15 +25,16 @@ import org.eclipse.tractusx.managedidentitywallets.domain.DID; import com.nimbusds.jwt.JWT; +import org.eclipse.tractusx.managedidentitywallets.signing.KeyProvider; import java.util.Set; public interface SecureTokenService { - JWT issueToken(DID self, DID partner, Set scopes); + JWT issueToken(DID self, DID partner, Set scopes, KeyProvider keyProvider); - JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes); + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes, KeyProvider keyProvider); - JWT issueToken(DID self, DID partner, JWT accessToken); + JWT issueToken(DID self, DID partner, JWT accessToken, KeyProvider keyProvider); - JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken); + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken, KeyProvider keyProvider); } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java index c84af9347..a69ad4659 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/HoldersCredentialService.java @@ -34,12 +34,18 @@ import org.apache.commons.text.StringEscapeUtils; import org.eclipse.tractusx.managedidentitywallets.command.GetCredentialsCommand; 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.repository.HoldersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; import org.eclipse.tractusx.managedidentitywallets.exception.CredentialNotFoundProblem; import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.signing.SignerResult; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredential; @@ -73,7 +79,8 @@ public class HoldersCredentialService extends BaseService credentialSpecificationUtil; - private final WalletKeyService walletKeyService; + private final Map availableSigningServices; + @Override protected BaseRepository getRepository() { @@ -127,10 +134,27 @@ public PageImpl getCredentials(GetCredentialsCommand comman List list = new ArrayList<>(filter.getContent().size()); + Wallet issuerWallet = command.getIdentifier() != null ? commonService.getWalletByIdentifier(command.getIdentifier()) : holderWallet; + for (HoldersCredential credential : filter.getContent()) { CredentialsResponse cr = new CredentialsResponse(); if (command.isAsJwt()) { - cr.setJwt(CommonUtils.vcAsJwt(command.getIdentifier() != null ? commonService.getWalletByIdentifier(command.getIdentifier()) : holderWallet, holderWallet, credential.getData(), walletKeyService)); + + CredentialCreationConfig config = CredentialCreationConfig.builder() + .algorithm(SupportedAlgorithms.ED25519) + .issuerDoc(issuerWallet.getDidDocument()) + .holderDid(holderWallet.getDid()) + .keyName(issuerWallet.getBpn()) + .verifiableCredential(credential.getData()) + .subject(credential.getData().getCredentialSubject().get(0)) + .contexts(credential.getData().getContext()) + .vcId(credential.getData().getId()) + .types(credential.getData().getTypes()) + .encoding(VerifiableEncoding.JWT) + .build(); + + SignerResult signerResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(config); + cr.setJwt(signerResult.getJwt()); } else { cr.setVc(credential.getData()); } @@ -155,19 +179,31 @@ public CredentialsResponse issueCredential(Map data, String call //validate BPN access, Holder must be caller of API Validate.isFalse(callerBpn.equals(issuerWallet.getBpn())).launch(new ForbiddenException(BASE_WALLET_BPN_IS_NOT_MATCHING_WITH_REQUEST_BPN_FROM_TOKEN)); - // get Key - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdAsBytes(issuerWallet.getId(), issuerWallet.getAlgorithm()); - // check if the expiryDate is set Date expiryDate = null; if (verifiableCredential.getExpirationDate() != null) { expiryDate = Date.from(verifiableCredential.getExpirationDate()); } + + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(verifiableCredential.getCredentialSubject().get(0)) + .types(verifiableCredential.getTypes()) + .issuerDoc(issuerWallet.getDidDocument()) + .holderDid(issuerWallet.getDid()) + .contexts(verifiableCredential.getContext()) + .expiryDate(expiryDate) + .selfIssued(true) + .keyName(issuerWallet.getBpn()) + .algorithm(SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())) + .build(); + // Create Credential - HoldersCredential credential = CommonUtils.getHoldersCredential(verifiableCredential.getCredentialSubject().get(0), - verifiableCredential.getTypes(), issuerWallet.getDidDocument(), - privateKeyBytes, issuerWallet.getDid(), - verifiableCredential.getContext(), expiryDate, true); + + SignerResult signerResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) signerResult.getJsonLd(); + HoldersCredential credential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); + //Store Credential in holder table credential = create(credential); @@ -176,7 +212,10 @@ public CredentialsResponse issueCredential(Map data, String call // Return VC if (asJwt) { - cr.setJwt(CommonUtils.vcAsJwt(issuerWallet, commonService.getWalletByIdentifier(callerBpn), credential.getData(), walletKeyService)); + holdersCredentialCreationConfig.setVerifiableCredential(credential.getData()); + holdersCredentialCreationConfig.setEncoding(VerifiableEncoding.JWT); + SignerResult signerJwtResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + cr.setJwt(signerJwtResult.getJwt()); } else { cr.setVc(credential.getData()); } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java index 1d26952a6..5cb44c63f 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialService.java @@ -39,11 +39,15 @@ import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.constant.MIWVerifiableCredentialType; 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.IssuersCredential; import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; import org.eclipse.tractusx.managedidentitywallets.dto.CredentialVerificationRequest; import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; import org.eclipse.tractusx.managedidentitywallets.dto.IssueDismantlerCredentialRequest; @@ -52,6 +56,8 @@ import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateCredentialProblem; import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.signing.SignerResult; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; @@ -66,6 +72,7 @@ import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialType; import org.eclipse.tractusx.ssi.lib.proof.LinkedDataProofValidation; import org.eclipse.tractusx.ssi.lib.serialization.SerializeUtil; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.stereotype.Service; @@ -104,14 +111,14 @@ public class IssuersCredentialService extends BaseService credentialSpecificationUtil; - private final WalletKeyService walletKeyService; - private final HoldersCredentialRepository holdersCredentialRepository; private final CommonService commonService; private final ObjectMapper objectMapper; + private Map availableSigningServices; + @Override protected BaseRepository getRepository() { @@ -164,10 +171,27 @@ public PageImpl getCredentials(GetCredentialsCommand comman Page filter = filter(filterRequest, request, CriteriaOperator.AND); List list = new ArrayList<>(filter.getContent().size()); + + Wallet holderWallet = command.getIdentifier() != null ? commonService.getWalletByIdentifier(command.getIdentifier()) : issuerWallet; + for (IssuersCredential credential : filter.getContent()) { CredentialsResponse cr = new CredentialsResponse(); if (command.isAsJwt()) { - cr.setJwt(CommonUtils.vcAsJwt(issuerWallet, command.getIdentifier() != null ? commonService.getWalletByIdentifier(command.getIdentifier()) : issuerWallet, credential.getData(), walletKeyService)); + CredentialCreationConfig config = CredentialCreationConfig.builder() + .algorithm(SupportedAlgorithms.ED25519) + .issuerDoc(issuerWallet.getDidDocument()) + .holderDid(holderWallet.getDid()) + .keyName(issuerWallet.getBpn()) + .verifiableCredential(credential.getData()) + .subject(credential.getData().getCredentialSubject().get(0)) + .contexts(credential.getData().getContext()) + .vcId(credential.getData().getId()) + .types(credential.getData().getTypes()) + .encoding(VerifiableEncoding.JWT) + .build(); + + SignerResult signerResult = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(config); + cr.setJwt(signerResult.getJwt()); } else { cr.setVc(credential.getData()); } @@ -187,13 +211,29 @@ public PageImpl getCredentials(GetCredentialsCommand comman */ @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRED) public VerifiableCredential issueBpnCredential(Wallet baseWallet, Wallet holderWallet, boolean authority) { - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdAsBytes(baseWallet.getId(), baseWallet.getAlgorithm()); + List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.BPN_CREDENTIAL); VerifiableCredentialSubject verifiableCredentialSubject = new VerifiableCredentialSubject(Map.of(StringPool.TYPE, MIWVerifiableCredentialType.BPN_CREDENTIAL, StringPool.ID, holderWallet.getDid(), StringPool.BPN, holderWallet.getBpn())); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(verifiableCredentialSubject, - types, baseWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), authority); + + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(verifiableCredentialSubject) + .types(types) + .issuerDoc(baseWallet.getDidDocument()) + .holderDid(holderWallet.getDid()) + .contexts(miwSettings.vcContexts()) + .expiryDate(miwSettings.vcExpiryDate()) + .selfIssued(authority) + .keyName(miwSettings.authorityWalletBpn()) + .algorithm(SupportedAlgorithms.valueOf(baseWallet.getAlgorithm())) + .build(); + + + SignerResult result = availableSigningServices.get(baseWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) result.getJsonLd(); + HoldersCredential holdersCredential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); //Store Credential in holder wallet holdersCredential = holdersCredentialRepository.save(holdersCredential); @@ -203,7 +243,7 @@ public VerifiableCredential issueBpnCredential(Wallet baseWallet, Wallet holderW issuersCredentialRepository.save(issuersCredential); //update summery VC - updateSummeryCredentials(baseWallet.getDidDocument(), privateKeyBytes, baseWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), MIWVerifiableCredentialType.BPN_CREDENTIAL); + updateSummeryCredentials(baseWallet.getDidDocument(), baseWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), MIWVerifiableCredentialType.BPN_CREDENTIAL, baseWallet.getSigningServiceType(),SupportedAlgorithms.valueOf(baseWallet.getAlgorithm())); log.debug("BPN credential issued for bpn -{}", StringEscapeUtils.escapeJava(holderWallet.getBpn())); @@ -230,8 +270,6 @@ public CredentialsResponse issueFrameworkCredential(IssueFrameworkCredentialRequ Wallet baseWallet = commonService.getWalletByIdentifier(miwSettings.authorityWalletBpn()); validateAccess(callerBPN, baseWallet); - // get Key - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdAsBytes(baseWallet.getId(), baseWallet.getAlgorithm()); //if base wallet issue credentials to itself boolean isSelfIssued = isSelfIssued(holderWallet.getBpn()); @@ -243,7 +281,23 @@ public CredentialsResponse issueFrameworkCredential(IssueFrameworkCredentialRequ StringPool.CONTRACT_TEMPLATE, request.getContractTemplate(), StringPool.CONTRACT_VERSION, request.getContractVersion())); List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(subject, types, baseWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); + + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(subject) + .types(types) + .issuerDoc(baseWallet.getDidDocument()) + .keyName(miwSettings.authorityWalletBpn()) + .holderDid(holderWallet.getDid()) + .contexts(miwSettings.vcContexts()) + .expiryDate(miwSettings.vcExpiryDate()) + .selfIssued(isSelfIssued) + .algorithm(SupportedAlgorithms.valueOf(baseWallet.getAlgorithm())) + .build(); + + SignerResult result = availableSigningServices.get(baseWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) result.getJsonLd(); + HoldersCredential holdersCredential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); //save in holder wallet holdersCredential = holdersCredentialRepository.save(holdersCredential); @@ -253,14 +307,17 @@ public CredentialsResponse issueFrameworkCredential(IssueFrameworkCredentialRequ issuersCredential = create(issuersCredential); //update summery cred - updateSummeryCredentials(baseWallet.getDidDocument(), privateKeyBytes, baseWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), request.getType()); + updateSummeryCredentials(baseWallet.getDidDocument(), baseWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), request.getType(), baseWallet.getSigningServiceType(),SupportedAlgorithms.valueOf(baseWallet.getAlgorithm())); final CredentialsResponse cr = new CredentialsResponse(); // Return VC if (asJwt) { - cr.setJwt(CommonUtils.vcAsJwt(baseWallet, holderWallet, issuersCredential.getData(), walletKeyService)); + holdersCredentialCreationConfig.setVerifiableCredential(issuersCredential.getData()); + holdersCredentialCreationConfig.setEncoding(VerifiableEncoding.JWT); + SignerResult credential = availableSigningServices.get(baseWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + cr.setJwt(credential.getJwt()); } else { cr.setVc(issuersCredential.getData()); } @@ -292,8 +349,6 @@ public CredentialsResponse issueDismantlerCredential(IssueDismantlerCredentialRe //check duplicate isCredentialExit(holderWallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdAsBytes(issuerWallet.getId(), issuerWallet.getAlgorithm()); - //if base wallet issue credentials to itself boolean isSelfIssued = isSelfIssued(request.getBpn()); @@ -303,7 +358,24 @@ public CredentialsResponse issueDismantlerCredential(IssueDismantlerCredentialRe StringPool.ACTIVITY_TYPE, request.getActivityType(), StringPool.ALLOWED_VEHICLE_BRANDS, request.getAllowedVehicleBrands() == null ? Collections.emptySet() : request.getAllowedVehicleBrands())); List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(subject, types, issuerWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); + + + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(subject) + .types(types) + .issuerDoc(issuerWallet.getDidDocument()) + .keyName(miwSettings.authorityWalletBpn()) + .holderDid(holderWallet.getDid()) + .contexts(miwSettings.vcContexts()) + .expiryDate(miwSettings.vcExpiryDate()) + .selfIssued(isSelfIssued) + .algorithm(SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())) + .build(); + + SignerResult result = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) result.getJsonLd(); + HoldersCredential holdersCredential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); //save in holder wallet @@ -314,13 +386,16 @@ public CredentialsResponse issueDismantlerCredential(IssueDismantlerCredentialRe issuersCredential = create(issuersCredential); //update summery VC - updateSummeryCredentials(issuerWallet.getDidDocument(), privateKeyBytes, issuerWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL); + updateSummeryCredentials(issuerWallet.getDidDocument(), issuerWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), MIWVerifiableCredentialType.DISMANTLER_CREDENTIAL, issuerWallet.getSigningServiceType(),SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())); final CredentialsResponse cr = new CredentialsResponse(); // Return VC if (asJwt) { - cr.setJwt(CommonUtils.vcAsJwt(issuerWallet, holderWallet, issuersCredential.getData(), walletKeyService)); + holdersCredentialCreationConfig.setVerifiableCredential(issuersCredential.getData()); + holdersCredentialCreationConfig.setEncoding(VerifiableEncoding.JWT); + SignerResult credential = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + cr.setJwt(credential.getJwt()); } else { cr.setVc(issuersCredential.getData()); } @@ -352,7 +427,6 @@ public CredentialsResponse issueMembershipCredential(IssueMembershipCredentialRe validateAccess(callerBPN, issuerWallet); - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdAsBytes(issuerWallet.getId(), issuerWallet.getAlgorithm()); List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, VerifiableCredentialType.MEMBERSHIP_CREDENTIAL); //if base wallet issue credentials to itself @@ -365,7 +439,24 @@ public CredentialsResponse issueMembershipCredential(IssueMembershipCredentialRe StringPool.MEMBER_OF, issuerWallet.getName(), StringPool.STATUS, "Active", StringPool.START_TIME, Instant.now().toString())); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(verifiableCredentialSubject, types, issuerWallet.getDidDocument(), privateKeyBytes, holderWallet.getDid(), miwSettings.vcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); + + + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(verifiableCredentialSubject) + .types(types) + .issuerDoc(issuerWallet.getDidDocument()) + .keyName(miwSettings.authorityWalletBpn()) + .holderDid(holderWallet.getDid()) + .contexts(miwSettings.vcContexts()) + .expiryDate(miwSettings.vcExpiryDate()) + .selfIssued(isSelfIssued) + .algorithm(SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())) + .build(); + + SignerResult result = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) result.getJsonLd(); + HoldersCredential holdersCredential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); //save in holder wallet @@ -377,13 +468,16 @@ public CredentialsResponse issueMembershipCredential(IssueMembershipCredentialRe issuersCredential = create(issuersCredential); //update summery VC - updateSummeryCredentials(issuerWallet.getDidDocument(), privateKeyBytes, issuerWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), VerifiableCredentialType.MEMBERSHIP_CREDENTIAL); + updateSummeryCredentials(issuerWallet.getDidDocument(), issuerWallet.getDid(), holderWallet.getBpn(), holderWallet.getDid(), VerifiableCredentialType.MEMBERSHIP_CREDENTIAL, issuerWallet.getSigningServiceType(), SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())); final CredentialsResponse cr = new CredentialsResponse(); // Return VC if (asJwt) { - cr.setJwt(CommonUtils.vcAsJwt(issuerWallet, holderWallet, issuersCredential.getData(), walletKeyService)); + holdersCredentialCreationConfig.setVerifiableCredential(issuersCredential.getData()); + holdersCredentialCreationConfig.setEncoding(VerifiableEncoding.JWT); + SignerResult credential = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + cr.setJwt(credential.getJwt()); } else { cr.setVc(issuersCredential.getData()); } @@ -417,17 +511,26 @@ public CredentialsResponse issueCredentialUsingBaseWallet(String holderDid, Map< validateAccess(callerBpn, issuerWallet); - // get issuer Key - byte[] privateKeyBytes = walletKeyService.getPrivateKeyByWalletIdAsBytes(issuerWallet.getId(), issuerWallet.getAlgorithm()); - boolean isSelfIssued = isSelfIssued(holderWallet.getBpn()); + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(verifiableCredential.getCredentialSubject().get(0)) + .types(verifiableCredential.getTypes()) + .issuerDoc(issuerWallet.getDidDocument()) + .keyName(miwSettings.authorityWalletBpn()) + .holderDid(holderWallet.getDid()) + .contexts(verifiableCredential.getContext()) + .expiryDate(Date.from(verifiableCredential.getExpirationDate())) + .selfIssued(isSelfIssued) + .algorithm(SupportedAlgorithms.valueOf(issuerWallet.getAlgorithm())) + .build(); + + // Create Credential - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(verifiableCredential.getCredentialSubject().get(0), - verifiableCredential.getTypes(), issuerWallet.getDidDocument(), - privateKeyBytes, - holderWallet.getDid(), - verifiableCredential.getContext(), Date.from(verifiableCredential.getExpirationDate()), isSelfIssued); + SignerResult result = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) result.getJsonLd(); + HoldersCredential holdersCredential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); //save in holder wallet @@ -441,7 +544,10 @@ public CredentialsResponse issueCredentialUsingBaseWallet(String holderDid, Map< // Return VC if (asJwt) { - cr.setJwt(CommonUtils.vcAsJwt(issuerWallet, holderWallet, issuersCredential.getData(), walletKeyService)); + holdersCredentialCreationConfig.setVerifiableCredential(issuersCredential.getData()); + holdersCredentialCreationConfig.setEncoding(VerifiableEncoding.JWT); + SignerResult credential = availableSigningServices.get(issuerWallet.getSigningServiceType()).createCredential(holdersCredentialCreationConfig); + cr.setJwt(credential.getJwt()); } else { cr.setVc(issuersCredential.getData()); } @@ -502,6 +608,7 @@ private boolean validateJWTExpiryDate(boolean withExpiryDate, SignedJWT signedJW /** * Credentials validation map. * + * @param verificationRequest the verification request * @param withCredentialExpiryDate the with credential expiry date * @return the map */ @@ -523,6 +630,7 @@ public Map credentialsValidation(CredentialVerificationRequest v .followRedirects(HttpClient.Redirect.ALWAYS) .build(); + DidResolver didResolver = new DidWebResolver(httpClient, new DidWebParser(), miwSettings.enforceHttps()); Map response = new TreeMap<>(); boolean valid; @@ -531,7 +639,6 @@ public Map credentialsValidation(CredentialVerificationRequest v if (verificationRequest.containsKey(StringPool.VC_JWT_KEY)) { JWTVerificationResult result = verifyVCAsJWT((String) verificationRequest.get(StringPool.VC_JWT_KEY), didResolver, withCredentialsValidation, withCredentialExpiryDate); - verifiableCredential = result.verifiableCredential; valid = result.valid; } else { @@ -574,16 +681,8 @@ private boolean isSelfIssued(String holderBpn) { } - /** - * Update summery credentials. - * - * @param issuerDidDocument the issuer did document - * @param issuerPrivateKey the issuer private key - * @param holderBpn the holder bpn - * @param holderDid the holder did - * @param type the type - */ - private void updateSummeryCredentials(DidDocument issuerDidDocument, byte[] issuerPrivateKey, String issuerDid, String holderBpn, String holderDid, String type) { + + private void updateSummeryCredentials(DidDocument issuerDidDocument, String issuerDid, String holderBpn, String holderDid, String type, SigningServiceType signingServiceType, SupportedAlgorithms algorithm) { //get last issued summary vc to holder to update items Page filter = getLastIssuedSummaryCredential(issuerDid, holderDid); @@ -632,11 +731,23 @@ private void updateSummeryCredentials(DidDocument issuerDidDocument, byte[] issu StringPool.CONTRACT_TEMPLATE, miwSettings.contractTemplatesUrl())); List types = List.of(VerifiableCredentialType.VERIFIABLE_CREDENTIAL, MIWVerifiableCredentialType.SUMMARY_CREDENTIAL); - HoldersCredential holdersCredential = CommonUtils.getHoldersCredential(subject, types, - issuerDidDocument, - issuerPrivateKey, - holderDid, miwSettings.summaryVcContexts(), miwSettings.vcExpiryDate(), isSelfIssued); + CredentialCreationConfig holdersCredentialCreationConfig = CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .subject(subject) + .types(types) + .issuerDoc(issuerDidDocument) + .keyName(miwSettings.authorityWalletBpn()) + .holderDid(holderDid) + .contexts(miwSettings.summaryVcContexts()) + .expiryDate(miwSettings.vcExpiryDate()) + .selfIssued(isSelfIssued) + .algorithm(algorithm) + .build(); + + SignerResult result = availableSigningServices.get(signingServiceType).createCredential(holdersCredentialCreationConfig); + VerifiableCredential vc = (VerifiableCredential) result.getJsonLd(); + HoldersCredential holdersCredential = CommonUtils.convertVerifiableCredential(vc, holdersCredentialCreationConfig); //save in holder wallet holdersCredentialRepository.save(holdersCredential); @@ -664,4 +775,15 @@ private Page getLastIssuedSummaryCredential(String issuerDid, return filter(filterRequest); } + + /** + * Sets key service. + * + * @param availableKeyStorage the available key storage + */ + @Autowired + public void setKeyService(Map availableKeyStorage) { + this.availableSigningServices = availableKeyStorage; + } + } 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 65c5c327c..1d057d74b 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/JwtPresentationES256KService.java @@ -30,29 +30,18 @@ import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.crypto.ECDSASigner; import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton; -import com.nimbusds.jose.jwk.Curve; import com.nimbusds.jose.jwk.ECKey; -import com.nimbusds.jose.jwk.KeyUse; -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.lang3.StringUtils; 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; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; 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.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.DidMethod; -import org.eclipse.tractusx.ssi.lib.model.did.DidMethodIdentifier; 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; @@ -62,9 +51,6 @@ import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedVerifiablePresentation; import org.springframework.beans.factory.annotation.Autowired; 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.IOException; import java.net.URI; @@ -77,12 +63,6 @@ import java.util.UUID; import java.util.stream.Collectors; -import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; -import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.PRIVATE_KEY; -import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.PUBLIC_KEY; -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; @@ -100,16 +80,10 @@ 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, MIWSettings miwSettings) { - this.walletRepository = walletRepository; - this.encryptionUtils = encryptionUtils; - this.walletKeyService = walletKeyService; + public JwtPresentationES256KService(MIWSettings miwSettings) { this.miwSettings = miwSettings; } @@ -131,53 +105,8 @@ public SignedJWT createPresentation(Did issuer, List crede return createSignedJwt(verifiablePresentation.getId(), issuer, audience, serializedVerifiablePresentation, ecPrivateKey); } - @Transactional(isolation = Isolation.READ_UNCOMMITTED, propagation = Propagation.REQUIRES_NEW) - public Wallet storeWalletKeyES256K(Wallet wallet, String keyId) { - try { - ECKey ecKey = new ECKeyGenerator(Curve.SECP256K1) - .keyUse(KeyUse.SIGNATURE) - .keyID(keyId) - .provider(BouncyCastleProviderSingleton.getInstance()) - .generate(); - - Did did = getDidFromDidString(wallet.getDid()); - - 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(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; - } - - private Did getDidFromDidString(String didString) { - int index = StringUtils.ordinalIndexOf(didString, COLON_SEPARATOR, 2); - String identifier = didString.substring(index + 1); - DidMethod didMethod = new DidMethod("web"); - DidMethodIdentifier methodIdentifier = new DidMethodIdentifier(identifier); - return new Did(didMethod, methodIdentifier, null); - } - private JWKVerificationMethod getJwkVerificationMethod(ECKey ecKey, Did did) { + public 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(), JWK_Y, ecKey.getY().toString()); diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/SecureTokenServiceImpl.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/LocalSecureTokenService.java similarity index 70% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/service/SecureTokenServiceImpl.java rename to src/main/java/org/eclipse/tractusx/managedidentitywallets/service/LocalSecureTokenService.java index 92ad63e90..d6fccfb60 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/SecureTokenServiceImpl.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/LocalSecureTokenService.java @@ -25,9 +25,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.managedidentitywallets.dao.entity.JtiRecord; -import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.JtiRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; import org.eclipse.tractusx.managedidentitywallets.domain.DID; @@ -35,6 +34,7 @@ import org.eclipse.tractusx.managedidentitywallets.exception.UnknownBusinessPartnerNumberException; import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenIssuer; import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; +import org.eclipse.tractusx.managedidentitywallets.signing.KeyProvider; import org.eclipse.tractusx.managedidentitywallets.sts.SecureTokenConfigurationProperties; import java.time.Instant; @@ -47,37 +47,37 @@ @Slf4j @RequiredArgsConstructor -public class SecureTokenServiceImpl implements SecureTokenService { - - private final WalletKeyRepository walletKeyRepository; +public class LocalSecureTokenService implements SecureTokenService { private final WalletRepository walletRepository; - private final SecureTokenIssuer tokenIssuer; + // Autowired by name!!! + private final SecureTokenIssuer localSecureTokenIssuer; private final SecureTokenConfigurationProperties properties; private final JtiRepository jtiRepository; + @Override - public JWT issueToken(final DID self, final DID partner, final Set scopes) { + public JWT issueToken(final DID self, final DID partner, final Set scopes, KeyProvider keyProvider) { log.debug("'issueToken' using scopes and DID."); - KeyPair keyPair = walletKeyRepository.findFirstByWallet_Did(self.toString()).toDto(); + KeyPair keyPair = keyProvider.getKeyPair(self); // IMPORTANT: we re-use the expiration time intentionally to mitigate any kind of timing attacks, // as we're signing two tokens. Instant expirationTime = Instant.now().plus(properties.tokenDuration()); - JWT accessToken = this.tokenIssuer.createAccessToken(keyPair, self, partner, expirationTime, scopes); + JWT accessToken = this.localSecureTokenIssuer.createAccessToken(keyPair, self, partner, expirationTime, scopes); checkAndStoreJti(accessToken); - return this.tokenIssuer.createIdToken(keyPair, self, partner, expirationTime, accessToken); + return this.localSecureTokenIssuer.createIdToken(keyPair, self, partner, expirationTime, accessToken); } @Override - public JWT issueToken(DID self, DID partner, JWT accessToken) { + public JWT issueToken(DID self, DID partner, JWT accessToken, KeyProvider keyProvider) { log.debug("'issueToken' using an access_token and DID."); - KeyPair keyPair = walletKeyRepository.findFirstByWallet_Did(self.toString()).toDto(); + KeyPair keyPair = keyProvider.getKeyPair(self); Instant expirationTime = Instant.now().plus(properties.tokenDuration()); checkAndStoreJti(accessToken); - return this.tokenIssuer.createIdToken(keyPair, self, partner, expirationTime, accessToken); + return this.localSecureTokenIssuer.createIdToken(keyPair, self, partner, expirationTime, accessToken); } private void checkAndStoreJti(JWT accessToken) { @@ -90,33 +90,31 @@ private void checkAndStoreJti(JWT accessToken) { } @Override - public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes) { + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes, KeyProvider keyProvider) { log.debug("'issueToken' using scopes and BPN."); - WalletKey walletKey = Optional.of(walletKeyRepository.findFirstByWallet_Bpn(self.toString())) - .orElseThrow(() -> new UnknownBusinessPartnerNumberException(String.format("The provided BPN '%s' is unknown", self))); - KeyPair keyPair = walletKey.toDto(); - DID selfDid = new DID(walletKey.getWallet().getDid()); + KeyPair keyPair = keyProvider.getKeyPair(self.toString()); + Wallet wallet = walletRepository.getByBpn(self.toString()); + DID selfDid = new DID(wallet.getDid()); DID partnerDid = new DID(Optional.ofNullable(walletRepository.getByBpn(partner.toString())) .orElseThrow(() -> new UnknownBusinessPartnerNumberException(String.format("The provided BPN '%s' is unknown", partner))) .getDid()); // IMPORTANT: we re-use the expiration time intentionally to mitigate any kind of timing attacks, // as we're signing two tokens. Instant expirationTime = Instant.now().plus(properties.tokenDuration()); - JWT accessToken = this.tokenIssuer.createAccessToken(keyPair, selfDid, partnerDid, expirationTime, scopes); - return this.tokenIssuer.createIdToken(keyPair, selfDid, partnerDid, expirationTime, accessToken); + JWT accessToken = this.localSecureTokenIssuer.createAccessToken(keyPair, selfDid, partnerDid, expirationTime, scopes); + return this.localSecureTokenIssuer.createIdToken(keyPair, selfDid, partnerDid, expirationTime, accessToken); } @Override - public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken) { + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken, KeyProvider keyProvider) { log.debug("'issueToken' using an access_token and BPN."); - WalletKey walletKey = Optional.ofNullable(walletKeyRepository.findFirstByWallet_Bpn(self.toString())) - .orElseThrow(() -> new UnknownBusinessPartnerNumberException(String.format("The provided BPN '%s' is unknown", self))); - KeyPair keyPair = walletKey.toDto(); - DID selfDid = new DID(walletKey.getWallet().getDid()); + KeyPair keyPair = keyProvider.getKeyPair(self.toString()); + Wallet wallet = walletRepository.getByBpn(self.toString()); + DID selfDid = new DID(wallet.getDid()); DID partnerDid = new DID(Optional.of(walletRepository.getByBpn(partner.toString())) .orElseThrow(() -> new UnknownBusinessPartnerNumberException(String.format("The provided BPN '%s' is unknown", partner))) .getDid()); Instant expirationTime = Instant.now().plus(properties.tokenDuration()); - return this.tokenIssuer.createIdToken(keyPair, selfDid, partnerDid, expirationTime, accessToken); + return this.localSecureTokenIssuer.createIdToken(keyPair, selfDid, partnerDid, expirationTime, accessToken); } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java index 0a1b783e8..a121b84a3 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/PresentationService.java @@ -30,7 +30,6 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.tuple.Pair; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; @@ -39,37 +38,31 @@ import org.eclipse.tractusx.managedidentitywallets.dao.entity.Wallet; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.JtiRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.PresentationCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; import org.eclipse.tractusx.managedidentitywallets.exception.MissingVcTypesException; import org.eclipse.tractusx.managedidentitywallets.exception.PermissionViolationException; +import org.eclipse.tractusx.managedidentitywallets.signing.SignerResult; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; -import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; -import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; -import org.eclipse.tractusx.ssi.lib.exception.did.DidParseException; import org.eclipse.tractusx.ssi.lib.exception.json.InvalidJsonLdException; -import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; import org.eclipse.tractusx.ssi.lib.exception.proof.JwtExpiredException; -import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtFactory; import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtValidator; import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtVerifier; 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.proof.LinkedDataProofValidation; 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.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedVerifiablePresentation; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.net.URI; -import java.security.interfaces.ECPrivateKey; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -96,20 +89,21 @@ public class PresentationService extends BaseService { private final HoldersCredentialRepository holdersCredentialRepository; - private final WalletKeyRepository walletKeyRepository; - private final SpecificationUtil credentialSpecificationUtil; private final CommonService commonService; - private final WalletKeyService walletKeyService; private final MIWSettings miwSettings; private final DidDocumentResolverService didDocumentResolverService; + private final Map availableSigningServices; + + private final JtiRepository jtiRepository; + @Override protected BaseRepository getRepository() { return holdersCredentialRepository; @@ -142,72 +136,38 @@ public Map createPresentation(Map data, boolean verifiableCredentials.add(verifiableCredential); }); - return buildVP(asJwt, audience, callerBpn, callerWallet, verifiableCredentials, SupportedAlgorithms.ED25519); - } + SigningService keyStorageService = availableSigningServices.get(callerWallet.getSigningServiceType()); - private Map buildVP(boolean asJwt, String audience, String callerBpn, Wallet callerWallet, List verifiableCredentials, SupportedAlgorithms algorithm) { Map response = new HashMap<>(); - if (asJwt && algorithm.equals(SupportedAlgorithms.ES256K)) { - buildVPJwtES256K(audience, callerBpn, callerWallet, verifiableCredentials, algorithm, response); - } else if (asJwt && algorithm.equals(SupportedAlgorithms.ED25519)) { - buildVPJwtEdDSA(audience, callerBpn, callerWallet, verifiableCredentials, algorithm, response); - } else { - buildVPJsonLd(callerBpn, verifiableCredentials, response); - } - return response; - } - - private void buildVPJsonLd(String callerBpn, List verifiableCredentials, Map response) { - log.debug("Creating VP as JSON-LD for bpn ->{}", callerBpn); - VerifiablePresentationBuilder verifiablePresentationBuilder = - new VerifiablePresentationBuilder(); - - VerifiablePresentation verifiablePresentation = - verifiablePresentationBuilder - .id(URI.create(miwSettings.authorityWalletDid() + "#" + UUID.randomUUID())) - .type(List.of(VerifiablePresentationType.VERIFIABLE_PRESENTATION)) - .verifiableCredentials(verifiableCredentials) - .build(); - response.put(StringPool.VP, verifiablePresentation); - } - - @SneakyThrows({ InvalidPrivateKeyFormatException.class }) - private void buildVPJwtEdDSA(String audience, String callerBpn, Wallet callerWallet, List verifiableCredentials, SupportedAlgorithms algorithm, Map response) { - Pair result = getPrivateKey(callerWallet, algorithm, audience, callerBpn); - String keyId = walletKeyService.getWalletKeyIdByWalletId(callerWallet.getId()); - - SerializedJwtPresentationFactory presentationFactory = new SerializedJwtPresentationFactoryImpl( - new SignedJwtFactory(new OctetKeyPairFactory()), new JsonLdSerializerImpl(), result.getKey()); - - X25519PrivateKey ed25519Key = (X25519PrivateKey) result.getRight(); - X25519PrivateKey privateKey = new X25519PrivateKey(ed25519Key.asByte()); - SignedJWT presentation = presentationFactory.createPresentation(result.getLeft(), verifiableCredentials, audience, privateKey, keyId); - response.put(StringPool.VP, presentation.serialize()); - } + Did vpIssuerDid = DidParser.parse(callerWallet.getDid()); - private void buildVPJwtES256K(String audience, String callerBpn, Wallet callerWallet, List verifiableCredentials, SupportedAlgorithms algorithm, Map response) { - Pair result = getPrivateKey(callerWallet, algorithm, audience, callerBpn); - ECPrivateKey ecPrivateKey = (ECPrivateKey) result.getRight(); - JwtPresentationES256KService presentationFactory = new JwtPresentationES256KService(result.getLeft(), new JsonLdSerializerImpl()); - SignedJWT presentation = presentationFactory.createPresentation(result.getLeft(), verifiableCredentials, audience, ecPrivateKey); + PresentationCreationConfig.PresentationCreationConfigBuilder builder = PresentationCreationConfig.builder() + .verifiableCredentials(verifiableCredentials) + .keyName(callerWallet.getBpn()) + .vpIssuerDid(vpIssuerDid) + .algorithm(SupportedAlgorithms.ED25519); - response.put(StringPool.VP, presentation.serialize()); - } + if (asJwt) { + log.debug("Creating VP as JWT for bpn ->{}", callerBpn); + Validate.isFalse(StringUtils.hasText(audience)).launch(new BadDataException("Audience needed to create VP as JWT")); + //Issuer of VP is holder of VC + builder.encoding(VerifiableEncoding.JWT) + .audience(audience); + } else { + log.debug("Creating VP as JSON-LD for bpn ->{}", callerBpn); + builder.encoding(VerifiableEncoding.JSON_LD) + .verificationMethod(URI.create(miwSettings.authorityWalletDid() + "#" + UUID.randomUUID())); + } - @SneakyThrows({ DidParseException.class }) - private Pair getPrivateKey(Wallet callerWallet, SupportedAlgorithms algorithm, String audience, String callerBpn) { - log.debug("Creating VP as JWT for bpn ->{}", callerBpn); - Validate.isFalse(StringUtils.hasText(audience)).launch(new BadDataException("Audience needed to create VP as JWT")); + PresentationCreationConfig presentationConfig = builder.build(); + SignerResult signerResult = keyStorageService.createPresentation(presentationConfig); - //Issuer of VP is holder of VC - Did vpIssuerDid = DidParser.parse(callerWallet.getDid()); + response.put(StringPool.VP, asJwt ? signerResult.getJwt() : signerResult.getJsonLd().toJson()); - //Build JWT - return Pair.of(vpIssuerDid, walletKeyService.getPrivateKeyByWalletIdAndAlgorithm(callerWallet.getId(), algorithm)); + return response; } - /** * Validate presentation map. * @@ -327,6 +287,7 @@ private boolean validateCredential(VerifiableCredential credential) { return isValid; } + @SneakyThrows public Map createVpWithRequiredScopes(SignedJWT innerJWT, boolean asJwt) { JWTClaimsSet jwtClaimsSet = getClaimsSet(innerJWT); @@ -360,11 +321,31 @@ public Map createVpWithRequiredScopes(SignedJWT innerJWT, boolea holdersCredentials.forEach(c -> verifiableCredentials.add(c.getData())); - // if as JWT true -> get key ES256K and sign with it - Map vp = buildVP(asJwt, jwtClaimsSet.getAudience().get(0), callerWallet.getBpn(), - callerWallet, verifiableCredentials, SupportedAlgorithms.ES256K); + PresentationCreationConfig.PresentationCreationConfigBuilder builder = PresentationCreationConfig.builder() + .verifiableCredentials(verifiableCredentials) + .keyName(callerWallet.getBpn()) + .vpIssuerDid(DidParser.parse(callerWallet.getDid())); + + + if (asJwt) { + + //Issuer of VP is holder of VC + builder.encoding(VerifiableEncoding.JWT) + .audience(jwtClaimsSet.getAudience().get(0)) + .algorithm(SupportedAlgorithms.ES256K); + } else { + builder.encoding(VerifiableEncoding.JSON_LD) + .verificationMethod(URI.create(miwSettings.authorityWalletDid() + "#" + UUID.randomUUID())) + .algorithm(SupportedAlgorithms.valueOf(callerWallet.getAlgorithm())); + } + + PresentationCreationConfig presentationConfig = builder.build(); + SigningService keyStorageService = availableSigningServices.get(callerWallet.getSigningServiceType()); + SignerResult signerResult = keyStorageService.createPresentation(presentationConfig); + changeJtiStatus(jtiRecord); - return vp; + + return Map.of(StringPool.VP, asJwt ? signerResult.getJwt() : signerResult.getJsonLd()); } private void checkReadPermission(String permission) { diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java index c013baf9e..ab7a636a6 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletKeyService.java @@ -33,11 +33,15 @@ import org.eclipse.tractusx.managedidentitywallets.exception.UnsupportedAlgorithmException; import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; +import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; import org.springframework.stereotype.Service; +import java.io.IOException; import java.io.StringReader; import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; import java.security.interfaces.ECPrivateKey; +import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; /** @@ -75,13 +79,24 @@ protected SpecificationUtil getSpecificationUtil() { @SneakyThrows public byte[] getPrivateKeyByWalletIdAsBytes(long walletId, String algorithm) { Object privateKey = getPrivateKeyByWalletIdAndAlgorithm(walletId, SupportedAlgorithms.valueOf(algorithm)); - if (privateKey instanceof X25519PrivateKey X25519PrivateKey) { - return X25519PrivateKey.asByte(); + if (privateKey instanceof X25519PrivateKey x25519PrivateKey) { + return x25519PrivateKey.asByte(); } else { return ((ECPrivateKey) privateKey).getEncoded(); } } + + @SneakyThrows + public Object getPrivateKeyByKeyId(String keyId, SupportedAlgorithms supportedAlgorithms) { + WalletKey walletKey = walletKeyRepository.getByKeyIdAndAlgorithm(keyId, supportedAlgorithms.name()); + Object privateKey = getKeyObject(SupportedAlgorithms.valueOf(walletKey.getAlgorithm()), encryptionUtils.decrypt(walletKey.getPrivateKey())); + if (privateKey instanceof X25519PrivateKey x25519PrivateKey) { + return x25519PrivateKey; + } else { + return privateKey; + } + } /** * Gets private key by wallet identifier. * @@ -93,6 +108,10 @@ public byte[] getPrivateKeyByWalletIdAsBytes(long walletId, String algorithm) { public Object getPrivateKeyByWalletIdAndAlgorithm(long walletId, SupportedAlgorithms algorithm) { WalletKey wallet = walletKeyRepository.getByWalletIdAndAlgorithm(walletId, algorithm.toString()); String privateKey = encryptionUtils.decrypt(wallet.getPrivateKey()); + return getKeyObject(algorithm, privateKey); + } + + private static Object getKeyObject(SupportedAlgorithms algorithm, String privateKey) throws IOException, InvalidPrivateKeyFormatException, NoSuchAlgorithmException, InvalidKeySpecException { byte[] content = new PemReader(new StringReader(privateKey)).readPemObject().getContent(); if (SupportedAlgorithms.ED25519.equals(algorithm)) { return new X25519PrivateKey(content); @@ -108,10 +127,12 @@ public Object getPrivateKeyByWalletIdAndAlgorithm(long walletId, SupportedAlgori * Gets wallet key by wallet id. * * @param walletId the wallet id + * @param supportedAlgorithms the algorithm of private key * @return the wallet key by wallet identifier */ @SneakyThrows - public String getWalletKeyIdByWalletId(long walletId) { - return walletKeyRepository.getByWalletId(walletId).getKeyId(); + public String getWalletKeyIdByWalletId(long walletId, SupportedAlgorithms supportedAlgorithms) { + return walletKeyRepository.getByWalletIdAndAlgorithm(walletId, supportedAlgorithms.name()).getKeyId(); } + } 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 7fafee29b..0ca388ac2 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/service/WalletService.java @@ -21,6 +21,10 @@ package org.eclipse.tractusx.managedidentitywallets.service; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jose.jwk.KeyUse; import com.smartsensesolutions.java.commons.FilterRequest; import com.smartsensesolutions.java.commons.base.repository.BaseRepository; import com.smartsensesolutions.java.commons.base.service.BaseService; @@ -28,6 +32,7 @@ import com.smartsensesolutions.java.commons.sort.SortType; import com.smartsensesolutions.java.commons.specification.SpecificationUtil; import jakarta.annotation.PostConstruct; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -40,21 +45,24 @@ import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; import org.eclipse.tractusx.managedidentitywallets.exception.DuplicateWalletProblem; import org.eclipse.tractusx.managedidentitywallets.exception.ForbiddenException; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; +import org.eclipse.tractusx.managedidentitywallets.utils.CommonUtils; import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; import org.eclipse.tractusx.managedidentitywallets.utils.Validate; -import org.eclipse.tractusx.ssi.lib.crypt.IKeyGenerator; import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; import org.eclipse.tractusx.ssi.lib.crypt.jwk.JsonWebKey; -import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519Generator; 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.JWKVerificationMethod; import org.eclipse.tractusx.ssi.lib.model.did.JWKVerificationMethodBuilder; +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.credential.VerifiableCredentialType; import org.springframework.beans.factory.annotation.Qualifier; @@ -71,10 +79,8 @@ import java.util.UUID; import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.COLON_SEPARATOR; -import static org.eclipse.tractusx.managedidentitywallets.constant.StringPool.ED_25519; 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; /** * The type Wallet service. @@ -95,8 +101,6 @@ public class WalletService extends BaseService { private final EncryptionUtils encryptionUtils; - private final WalletKeyService walletKeyService; - private final HoldersCredentialRepository holdersCredentialRepository; private final SpecificationUtil walletSpecificationUtil; @@ -105,6 +109,8 @@ public class WalletService extends BaseService { private final CommonService commonService; + private final Map availableSigningServices; + @Qualifier("transactionManager") private final PlatformTransactionManager transactionManager; @@ -212,6 +218,7 @@ public Page getWallets(int pageNumber, int size, String sortColumn, Stri * @return the wallet */ @SneakyThrows + @Transactional public Wallet createWallet(CreateWalletRequest request, String callerBpn) { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); final Wallet[] wallets = new Wallet[1]; @@ -221,7 +228,6 @@ protected void doInTransactionWithoutResult(TransactionStatus status) { wallets[0] = createWallet(request, false, callerBpn); } }); - wallets[0] = updateWalletWithWalletKeyES256K(transactionTemplate, wallets); return wallets[0]; } @@ -236,19 +242,51 @@ private Wallet createWallet(CreateWalletRequest request, boolean authority, Stri validateCreateWallet(request, callerBpn); //create private key pair - IKeyGenerator keyGenerator = new X25519Generator(); - KeyPair keyPair = keyGenerator.generateKey(); + final SigningServiceType signingServiceType; + if (authority) { + signingServiceType = miwSettings.authoritySigningServiceType(); + } else { + signingServiceType = request.getSigningServiceType(); + } + + KeyCreationConfig keyCreationConfig = KeyCreationConfig.builder() + .keyName(request.getBusinessPartnerNumber()) + .keyTypes(List.of(KeyType.OCT, KeyType.EC)) + .curve(Curve.SECP256K1) + .build(); + SigningService signingService = availableSigningServices.get(signingServiceType); + + Map keyPairs = signingService.getKeys(keyCreationConfig); //create did json Did did = createDidJson(request.getDidUrl()); + List walletKeyInfos = new ArrayList<>(); + keyPairs.forEach((k, keyPair) -> { + String keyId = UUID.randomUUID().toString(); + JWKVerificationMethod jwkVerificationMethod; + SupportedAlgorithms algorithm; + if (k == KeyType.OCT) { + algorithm = SupportedAlgorithms.ED25519; + JsonWebKey jwk = new JsonWebKey(keyId, keyPair.getPublicKey(), keyPair.getPrivateKey()); + jwkVerificationMethod = + new JWKVerificationMethodBuilder().did(did).jwk(jwk).build(); + } else if (k == KeyType.EC) { + algorithm = SupportedAlgorithms.ES256K; + ECKey ecKey = new ECKey.Builder(Curve.SECP256K1, CommonUtils.ecPublicFrom(keyPair.getPublicKey().asByte())) + .privateKey(CommonUtils.ecPrivateFrom(keyPair.getPrivateKey().asByte())) + .keyID(keyId) + .keyUse(KeyUse.SIGNATURE) + .build(); + jwkVerificationMethod = jwtPresentationES256KService.getJwkVerificationMethod(ecKey, did); + } else { + throw new IllegalArgumentException("unsupported keyType %s".formatted(k.getValue())); + } - String keyId = UUID.randomUUID().toString(); + walletKeyInfos.add(new WalletKeyInfo(keyId, keyPair, algorithm, jwkVerificationMethod)); + }); - JsonWebKey jwk = new JsonWebKey(keyId, keyPair.getPublicKey(), keyPair.getPrivateKey()); - JWKVerificationMethod jwkVerificationMethod = - new JWKVerificationMethodBuilder().did(did).jwk(jwk).build(); - DidDocument didDocument = jwtPresentationES256KService.buildDidDocument(request.getBusinessPartnerNumber(), did, List.of(jwkVerificationMethod)); + DidDocument didDocument = jwtPresentationES256KService.buildDidDocument(request.getBusinessPartnerNumber(), did, walletKeyInfos.stream().map(wki -> wki.verificationMethod).toList()); //Save wallet Wallet wallet = create(Wallet.builder() @@ -256,24 +294,26 @@ private Wallet createWallet(CreateWalletRequest request, boolean authority, Stri .bpn(request.getBusinessPartnerNumber()) .name(request.getCompanyName()) .did(did.toUri().toString()) - .algorithm(ED_25519) + .algorithm(StringPool.ED_25519) + .signingServiceType(signingServiceType) .build()); - WalletKey walletKeyED25519 = WalletKey.builder() - .wallet(wallet) - .keyId(keyId) - .referenceKey(REFERENCE_KEY) - .vaultAccessToken(VAULT_ACCESS_TOKEN) - .privateKey(encryptionUtils.encrypt(getKeyString(keyPair.getPrivateKey().asByte(), StringPool.PRIVATE_KEY))) - .publicKey(encryptionUtils.encrypt(getKeyString(keyPair.getPublicKey().asByte(), StringPool.PUBLIC_KEY))) - .algorithm(SupportedAlgorithms.ED25519.toString()) - .build(); + var walletsKeys = walletKeyInfos.stream().map(e -> + WalletKey.builder() + .wallet(wallet) + .keyId(e.keyId) + .referenceKey(REFERENCE_KEY) + .vaultAccessToken(VAULT_ACCESS_TOKEN) + .privateKey(encryptionUtils.encrypt(CommonUtils.getKeyString(e.keyPair.getPrivateKey().asByte(), StringPool.PRIVATE_KEY))) + .publicKey(encryptionUtils.encrypt(CommonUtils.getKeyString(e.keyPair.getPublicKey().asByte(), StringPool.PUBLIC_KEY))) + .algorithm(e.algorithm.name()) + .build() + ).toList(); - //Save key EdDSA - walletKeyService.getRepository().save(walletKeyED25519); - log.debug("Wallet created for bpn ->{}", StringEscapeUtils.escapeJava(request.getBusinessPartnerNumber())); - //credentials issuance will be moved to the issuer component + signingService.saveKeys(walletsKeys); + + log.debug("Wallet created for bpn ->{}", StringEscapeUtils.escapeJava(request.getBusinessPartnerNumber())); return wallet; } @@ -314,22 +354,8 @@ protected void doInTransactionWithoutResult(TransactionStatus status) { } } }); - updateWalletWithWalletKeyES256K(transactionTemplate, wallets); } - private Wallet updateWalletWithWalletKeyES256K(TransactionTemplate transactionTemplate, Wallet[] wallets) { - String keyId = UUID.randomUUID().toString(); - transactionTemplate.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - // 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) { // check base wallet @@ -341,4 +367,13 @@ private void validateCreateWallet(CreateWalletRequest request, String callerBpn) throw new DuplicateWalletProblem("Wallet is already exists for bpn " + request.getBusinessPartnerNumber()); } } + + + @RequiredArgsConstructor + private class WalletKeyInfo { + private final String keyId; + private final KeyPair keyPair; + private final SupportedAlgorithms algorithm; + private final VerificationMethod verificationMethod; + } } diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/KeyProvider.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/KeyProvider.java new file mode 100644 index 000000000..e8b925dad --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/KeyProvider.java @@ -0,0 +1,64 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.signing; + +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyStorageType; + +import java.util.List; + +/** + * This class will be used by LocalSigningService to retrieve and save keys + * + * @see LocalSigningService + */ +public interface KeyProvider { + + /** + * @param keyName the name of the key that is to be retrieved + * @return the key as a byte-array + * + */ + Object getPrivateKey(String keyName, SupportedAlgorithms algorithm); + + /** + * @param walletKey the key to save + */ + void saveKeys(List walletKey); + + + String getKeyId(String keyName, SupportedAlgorithms algorithm); + + + /** + * @return the type of KeyProvider + * + * @see KeyStorageType + */ + KeyStorageType getKeyStorageType(); + + KeyPair getKeyPair(DID self); + KeyPair getKeyPair(String bpn); +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalKeyProvider.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalKeyProvider.java new file mode 100644 index 000000000..93353361d --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalKeyProvider.java @@ -0,0 +1,90 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.signing; + +import lombok.RequiredArgsConstructor; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyStorageType; +import org.eclipse.tractusx.managedidentitywallets.service.WalletKeyService; +import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class LocalKeyProvider implements KeyProvider { + + private final WalletKeyService walletKeyService; + + private final WalletKeyRepository walletKeyRepository; + + private final EncryptionUtils encryptionUtils; + + @Override + public Object getPrivateKey(String keyName, SupportedAlgorithms algorithm) { + WalletKey walletKey = walletKeyRepository.getByAlgorithmAndWallet_Bpn(algorithm.name(), keyName); + return walletKeyService.getPrivateKeyByKeyId(walletKey.getKeyId(), algorithm); + } + + @Override + public void saveKeys(List walletKeys) { + walletKeyRepository.saveAllAndFlush(walletKeys); + } + + @Override + public String getKeyId(String keyName, SupportedAlgorithms algorithm) { + WalletKey walletKey = walletKeyRepository.getByAlgorithmAndWallet_Bpn(algorithm.name(), keyName); + return walletKey.getKeyId(); + } + + @Override + public KeyStorageType getKeyStorageType() { + return KeyStorageType.DB; + } + + @Override + public KeyPair getKeyPair(DID self) { + KeyPair dto = walletKeyRepository.findFirstByWallet_Did(self.toString()).toDto(); + + return new KeyPair( + dto.keyId(), + encryptionUtils.decrypt(dto.privateKey()), + encryptionUtils.decrypt(dto.publicKey()) + ); + } + + @Override + public KeyPair getKeyPair(String bpn) { + KeyPair dto = walletKeyRepository.findFirstByWallet_Bpn(bpn).toDto(); + + return new KeyPair( + dto.keyId(), + encryptionUtils.decrypt(dto.privateKey()), + encryptionUtils.decrypt(dto.publicKey()) + ); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningService.java new file mode 100644 index 000000000..67a205afa --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningService.java @@ -0,0 +1,36 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.signing; + +/** + * Specialized interface for SigningServices that will sign credentials/presentations locally + * (may retrieve the keys from remote via KeyProvider) + * + * @see SigningService + * @see KeyProvider + */ +public interface LocalSigningService extends SigningService { + /** + * @param keyProvider the KeyProvider to be used by the implementation + */ + void setKeyProvider(KeyProvider keyProvider); +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningServiceImpl.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningServiceImpl.java new file mode 100644 index 000000000..1b1198ff3 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/LocalSigningServiceImpl.java @@ -0,0 +1,375 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.signing; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton; +import com.nimbusds.jose.jwk.Curve; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jose.jwk.gen.ECKeyGenerator; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.SignedJWT; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.apache.commons.lang3.NotImplementedException; +import org.eclipse.tractusx.managedidentitywallets.constant.StringPool; +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.PresentationCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; +import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; +import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; +import org.eclipse.tractusx.managedidentitywallets.service.JwtPresentationES256KService; +import org.eclipse.tractusx.ssi.lib.crypt.IKeyGenerator; +import org.eclipse.tractusx.ssi.lib.crypt.IPrivateKey; +import org.eclipse.tractusx.ssi.lib.crypt.IPublicKey; +import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; +import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519Generator; +import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; +import org.eclipse.tractusx.ssi.lib.exception.json.TransformJsonLdException; +import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; +import org.eclipse.tractusx.ssi.lib.exception.key.KeyGenerationException; +import org.eclipse.tractusx.ssi.lib.exception.proof.SignatureGenerateFailedException; +import org.eclipse.tractusx.ssi.lib.exception.proof.UnsupportedSignatureTypeException; +import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtFactory; +import org.eclipse.tractusx.ssi.lib.model.JsonLdObject; +import org.eclipse.tractusx.ssi.lib.model.base.EncodeType; +import org.eclipse.tractusx.ssi.lib.model.did.DidParser; +import org.eclipse.tractusx.ssi.lib.model.proof.Proof; +import org.eclipse.tractusx.ssi.lib.model.verifiable.Verifiable; +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.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.proof.LinkedDataProofGenerator; +import org.eclipse.tractusx.ssi.lib.proof.SignatureType; +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.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtVCFactoryImpl; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.security.KeyFactory; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Component +public class LocalSigningServiceImpl implements LocalSigningService { + + // this is not autowired by name, and injected by ApplicationConfig, + // since keys could be saved in hashicorp (=> HashicorpKeyProvider, e.g.) and retrieved from there + private KeyProvider keyProvider; + + // Autowired by name!!! + private final SecureTokenService localSecureTokenService; + + @Override + public SignerResult createCredential(CredentialCreationConfig config) { + + byte[] privateKeyBytes = getPrivateKeyBytes(config.getKeyName(), config.getAlgorithm()); + VerifiableEncoding encoding = Objects.requireNonNull(config.getEncoding()); + SignerResult.SignerResultBuilder resultBuilder = SignerResult.builder().encoding(encoding); + switch (encoding) { + case JSON_LD -> { + return resultBuilder.jsonLd(createVerifiableCredential(config, privateKeyBytes)).build(); + } + case JWT -> { + SignedJWT verifiableCredentialAsJwt = createVerifiableCredentialAsJwt(config); + return resultBuilder.jwt(verifiableCredentialAsJwt.serialize()).build(); + } + default -> + throw new IllegalArgumentException("encoding %s is not supported".formatted(config.getEncoding())); + + } + } + + private byte[] getPrivateKeyBytes(String keyName, SupportedAlgorithms supportedAlgorithms) { + byte[] privateKeyBytes; + if (supportedAlgorithms.equals(SupportedAlgorithms.ED25519)) { + privateKeyBytes = ((IPrivateKey) keyProvider.getPrivateKey(keyName, supportedAlgorithms)).asByte(); + } else if (supportedAlgorithms.equals(SupportedAlgorithms.ES256K)) { + ECPrivateKey ecKey = (ECPrivateKey) keyProvider.getPrivateKey(keyName, supportedAlgorithms); + privateKeyBytes = ecKey.getEncoded(); + } else { + throw new IllegalArgumentException("Unknown algorithm " + supportedAlgorithms); + } + return privateKeyBytes; + } + + @SneakyThrows + private SignedJWT createVerifiableCredentialAsJwt(CredentialCreationConfig config) { + if (!config.getAlgorithm().equals(SupportedAlgorithms.ED25519)) { + throw new IllegalArgumentException("VC as JWT is not supported for provided algorithm -> " + config.getAlgorithm()); + } + // JWT Factory + SerializedJwtVCFactoryImpl vcFactory = new SerializedJwtVCFactoryImpl( + new SignedJwtFactory(new OctetKeyPairFactory())); + IPrivateKey iPrivateKey = ((IPrivateKey) keyProvider.getPrivateKey(config.getKeyName(), config.getAlgorithm())); + + return vcFactory.createVCJwt(DidParser.parse(config.getIssuerDoc().getId()), DidParser.parse(config.getHolderDid()), config.getVerifiableCredential(), + iPrivateKey, + keyProvider.getKeyId(config.getKeyName(), config.getAlgorithm())); + } + + @Override + + public Map getKeys(KeyCreationConfig config) throws KeyGenerationException { + List keyTypes = Objects.requireNonNull(config.getKeyTypes()); + Map result = new HashMap<>(); + for (KeyType keyType : keyTypes) { + switch (keyType.getValue().toUpperCase()) { + case "EC" -> { + try { + ECKey ecKey = new ECKeyGenerator(Curve.SECP256K1) + .provider(BouncyCastleProviderSingleton.getInstance()) + .generate(); + ECPrivateKey ecPrivateKey = ecKey.toECPrivateKey(); + ECPublicKey ecPublicKey = ecKey.toECPublicKey(); + + result.put(keyType, new KeyPair(new IPublicKey() { + @Override + public int getKeyLength() { + return 0; + } + + @Override + public String asStringForStoring() { + return null; + } + + @Override + public String asStringForExchange(EncodeType encodeType) { + return null; + } + + @Override + public byte[] asByte() { + return ecPublicKey.getEncoded(); + } + }, new IPrivateKey() { + @Override + public int getKeyLength() { + return 0; + } + + @Override + public String asStringForStoring() { + return null; + } + + @Override + public String asStringForExchange(EncodeType encodeType) { + return null; + } + + @Override + public byte[] asByte() { + return ecPrivateKey.getEncoded(); + } + })); + } catch (JOSEException e) { + throw new BadDataException("Could not generate EC Jwk", e); + } + } + case "RSA" -> + throw new NotImplementedException("%s is not implemented yet".formatted(keyType.toString())); + case "OCT" -> { + IKeyGenerator keyGenerator = new X25519Generator(); + result.put(keyType, keyGenerator.generateKey()); + } + default -> throw new IllegalArgumentException("%s is not supported".formatted(keyType.toString())); + } + } + return result; + } + + @Override + public SigningServiceType getSupportedServiceType() { + return SigningServiceType.LOCAL; + } + + @Override + public SignerResult createPresentation(PresentationCreationConfig config) { + byte[] privateKeyBytes = getPrivateKeyBytes(config.getKeyName(), config.getAlgorithm()); + VerifiableEncoding encoding = Objects.requireNonNull(config.getEncoding()); + SignerResult.SignerResultBuilder resultBuilder = SignerResult.builder().encoding(encoding); + switch (config.getEncoding()) { + case JWT -> { + return resultBuilder.jwt(generateJwtPresentation(config, privateKeyBytes).serialize()).build(); + } + case JSON_LD -> { + try { + return resultBuilder.jsonLd(generateJsonLdPresentation(config, privateKeyBytes)).build(); + } catch (UnsupportedSignatureTypeException | InvalidPrivateKeyFormatException | + SignatureGenerateFailedException | TransformJsonLdException e) { + throw new IllegalStateException(e); + } + } + default -> + throw new IllegalArgumentException("encoding %s is not supported".formatted(config.getEncoding())); + } + } + + @Override + public void setKeyProvider(KeyProvider keyProvider) { + this.keyProvider = Objects.requireNonNull(keyProvider); + } + + @Override + public void saveKeys(List key) { + keyProvider.saveKeys(key); + } + + private SignedJWT generateJwtPresentation(PresentationCreationConfig config, byte[] privateKeyBytes) { + + + if (config.getAlgorithm() == SupportedAlgorithms.ES256K) { + return buildES256K(config, privateKeyBytes); + } else if (config.getAlgorithm() == SupportedAlgorithms.ED25519) { + return buildED25519(config, privateKeyBytes); + } + + throw new IllegalArgumentException("algorithm %s is not supported for VP JWT".formatted(config.getAlgorithm().name())); + } + + private VerifiablePresentation generateJsonLdPresentation(PresentationCreationConfig config, byte[] privateKeyBytes) throws UnsupportedSignatureTypeException, InvalidPrivateKeyFormatException, SignatureGenerateFailedException, TransformJsonLdException { + VerifiablePresentationBuilder verifiablePresentationBuilder = + new VerifiablePresentationBuilder().id(URI.create(config.getVpIssuerDid() + "#" + UUID.randomUUID().toString())) + .type(List.of(VerifiablePresentationType.VERIFIABLE_PRESENTATION)) + .verifiableCredentials(config.getVerifiableCredentials()); + + + VerifiablePresentation verifiablePresentation = verifiablePresentationBuilder.build(); + List contexts = verifiablePresentation.getContext().stream().map(URI::toString).collect(Collectors.toList()); + if (!contexts.contains(StringPool.W3_ID_JWS_2020_V1_CONTEXT_URL)) { + contexts.add(StringPool.W3_ID_JWS_2020_V1_CONTEXT_URL); + } + verifiablePresentation.put(JsonLdObject.CONTEXT, contexts); + LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS); + + X25519PrivateKey privateKey = new X25519PrivateKey(privateKeyBytes); + + Proof proof = generator.createProof(verifiablePresentation, config.getVerificationMethod(), + privateKey); + verifiablePresentation.put(Verifiable.PROOF, proof); + return verifiablePresentation; + } + + @SneakyThrows + private static VerifiableCredential createVerifiableCredential(CredentialCreationConfig config, byte[] privateKeyBytes) { + //VC Builder + + // if the credential does not contain the JWS proof-context add it + URI jwsUri = URI.create("https://w3id.org/security/suites/jws-2020/v1"); + if (!config.getContexts().contains(jwsUri)) { + config.getContexts().add(jwsUri); + } + + + URI id = URI.create(UUID.randomUUID().toString()); + VerifiableCredentialBuilder builder = new VerifiableCredentialBuilder() + .context(config.getContexts()) + .id(URI.create(config.getIssuerDoc().getId() + "#" + id)) + .type(config.getTypes()) + .issuer(config.getIssuerDoc().getId()) + .expirationDate(config.getExpiryDate() != null ? config.getExpiryDate().toInstant() : null) + .issuanceDate(Instant.now()) + .credentialSubject(config.getSubject()); + + + LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS); + URI verificationMethod = config.getIssuerDoc().getVerificationMethods().get(0).getId(); + + Proof proof = generator.createProof(builder.build(), verificationMethod, new X25519PrivateKey(privateKeyBytes)); + + //Adding Proof to VC + builder.proof(proof); + + //Create Credential + return builder.build(); + } + + + private SignedJWT buildED25519(PresentationCreationConfig config, byte[] privateKeyBytes) { + SerializedJwtPresentationFactory presentationFactory = new SerializedJwtPresentationFactoryImpl( + new SignedJwtFactory(new OctetKeyPairFactory()), new JsonLdSerializerImpl(), config.getVpIssuerDid()); + + X25519PrivateKey privateKey = null; + try { + privateKey = new X25519PrivateKey(privateKeyBytes); + } catch (InvalidPrivateKeyFormatException e) { + throw new IllegalArgumentException(e); + } + return presentationFactory.createPresentation(config.getVpIssuerDid(), config.getVerifiableCredentials(), config.getAudience(), privateKey, config.getKeyName()); + } + + @SneakyThrows + private SignedJWT buildES256K(PresentationCreationConfig config, byte[] privateKeyBytes) { + var kf = KeyFactory.getInstance("EC"); + var privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + ECPrivateKey ecPrivateKey = (ECPrivateKey) kf.generatePrivate(privateKeySpec); + + JwtPresentationES256KService presentationFactory = new JwtPresentationES256KService(config.getVpIssuerDid(), new JsonLdSerializerImpl()); + return presentationFactory.createPresentation(config.getVpIssuerDid() + , config.getVerifiableCredentials(), config.getAudience(), ecPrivateKey); + } + + + @Override + public JWT issueToken(DID self, DID partner, Set scopes) { + return localSecureTokenService.issueToken(self, partner, scopes, keyProvider); + } + + @Override + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes) { + return localSecureTokenService.issueToken(self, partner, scopes, keyProvider); + } + + @Override + public JWT issueToken(DID self, DID partner, JWT accessToken) { + return localSecureTokenService.issueToken(self, partner, accessToken, keyProvider); + } + + @Override + public JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken) { + return localSecureTokenService.issueToken(self, partner, accessToken, keyProvider); + } +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SignerResult.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SignerResult.java new file mode 100644 index 000000000..4db8e99ca --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SignerResult.java @@ -0,0 +1,37 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.signing; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import org.eclipse.tractusx.managedidentitywallets.domain.VerifiableEncoding; +import org.eclipse.tractusx.ssi.lib.model.verifiable.Verifiable; + +@Getter +@Setter +@Builder +public class SignerResult { + private VerifiableEncoding encoding; + private Verifiable jsonLd; + private String jwt; +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SigningService.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SigningService.java new file mode 100644 index 000000000..579af4a51 --- /dev/null +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/signing/SigningService.java @@ -0,0 +1,89 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.signing; + +import com.nimbusds.jose.jwk.KeyType; +import com.nimbusds.jwt.JWT; +import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; +import org.eclipse.tractusx.managedidentitywallets.domain.BusinessPartnerNumber; +import org.eclipse.tractusx.managedidentitywallets.domain.CredentialCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.DID; +import org.eclipse.tractusx.managedidentitywallets.domain.KeyCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.PresentationCreationConfig; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; +import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; +import org.eclipse.tractusx.ssi.lib.exception.key.KeyGenerationException; + + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Service used to sign the verifiable credentials and verifiable presentations + */ +public interface SigningService { + /** + * @param config the configuration for creating the credential + * @return SignerResult containing the signed credential + * @see CredentialCreationConfig + * @see SignerResult + */ + SignerResult createCredential(CredentialCreationConfig config); + + + /** + * @param config the config for creating/retrieving the key + * @return KeyPair containing the public and private key (private key depends on the type of signing service used) + * @throws KeyGenerationException when something goes wrong + * @see KeyPair + * @see KeyCreationConfig + */ + Map getKeys(KeyCreationConfig config) throws KeyGenerationException; + + /** + * @param key the key to be saved, LocalSigningService implementations may call KeyProvider.saveKey + * @see KeyProvider + */ + void saveKeys(List key); + + /** + * @return the implementation's supported type + */ + SigningServiceType getSupportedServiceType(); + + /** + * @param config the configuration for creating the presentation + * @return SignerResult containing the signed presentation + * @see PresentationCreationConfig + * @see SignerResult + */ + SignerResult createPresentation(PresentationCreationConfig config); + + JWT issueToken(DID self, DID partner, Set scopes); + + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, Set scopes); + + JWT issueToken(DID self, DID partner, JWT accessToken); + + JWT issueToken(BusinessPartnerNumber self, BusinessPartnerNumber partner, JWT accessToken); +} diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenIssuerImpl.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/LocalSecureTokenIssuer.java similarity index 94% rename from src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenIssuerImpl.java rename to src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/LocalSecureTokenIssuer.java index 45c6f227e..32709faa7 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenIssuerImpl.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/LocalSecureTokenIssuer.java @@ -35,7 +35,6 @@ import org.eclipse.tractusx.managedidentitywallets.domain.DID; import org.eclipse.tractusx.managedidentitywallets.domain.KeyPair; import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenIssuer; -import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; import org.springframework.stereotype.Component; @@ -53,9 +52,7 @@ @Slf4j @Component @RequiredArgsConstructor -public class SecureTokenIssuerImpl implements SecureTokenIssuer { - - private final EncryptionUtils encryptionUtils; +public class LocalSecureTokenIssuer implements SecureTokenIssuer { @Override public JWT createIdToken(KeyPair keyPair, DID self, DID partner, Instant expirationTime, JWT accessToken) { @@ -101,7 +98,7 @@ private JWT createSignedJWT(KeyPair keyPair, JWTClaimsSet.Builder builder) { log.debug("Creating JWS signature for issuer '{}' and holder '{}'", builder.getClaims().get("iss"), builder.getClaims().get("sub")); SignedJWT signedJWT = new SignedJWT(header, body); - String privateKey = encryptionUtils.decrypt(keyPair.privateKey()); + String privateKey = keyPair.privateKey(); // todo bri: this should become dynamic in the future, as we want to support more key algos. OctetKeyPair jwk = new OctetKeyPairFactory().fromPrivateKey(new X25519PrivateKey(privateKey, true)); Ed25519Signer signer = new Ed25519Signer(jwk); diff --git a/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenBeanConfig.java b/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenBeanConfig.java index b27076677..273f06ebc 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenBeanConfig.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/sts/SecureTokenBeanConfig.java @@ -22,11 +22,10 @@ package org.eclipse.tractusx.managedidentitywallets.sts; import org.eclipse.tractusx.managedidentitywallets.dao.repository.JtiRepository; -import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenIssuer; import org.eclipse.tractusx.managedidentitywallets.interfaces.SecureTokenService; -import org.eclipse.tractusx.managedidentitywallets.service.SecureTokenServiceImpl; +import org.eclipse.tractusx.managedidentitywallets.service.LocalSecureTokenService; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -35,13 +34,12 @@ public class SecureTokenBeanConfig { @Bean public SecureTokenService secureTokenService( - WalletKeyRepository keyRepository, WalletRepository walletRepository, SecureTokenIssuer issuer, SecureTokenConfigurationProperties properties, JtiRepository jtiRepository ) { - return new SecureTokenServiceImpl(keyRepository, walletRepository, issuer, properties, jtiRepository); + return new LocalSecureTokenService(walletRepository, issuer, properties, jtiRepository); } } 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 d41ff50a3..c38a50d64 100644 --- a/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java +++ b/src/main/java/org/eclipse/tractusx/managedidentitywallets/utils/CommonUtils.java @@ -22,47 +22,28 @@ package org.eclipse.tractusx.managedidentitywallets.utils; import com.fasterxml.jackson.databind.ObjectMapper; -import com.nimbusds.jwt.SignedJWT; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemWriter; 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.domain.CredentialCreationConfig; import org.eclipse.tractusx.managedidentitywallets.dto.SecureTokenRequest; import org.eclipse.tractusx.managedidentitywallets.exception.BadDataException; -import org.eclipse.tractusx.managedidentitywallets.service.WalletKeyService; -import org.eclipse.tractusx.ssi.lib.crypt.octet.OctetKeyPairFactory; -import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; -import org.eclipse.tractusx.ssi.lib.exception.did.DidParseException; -import org.eclipse.tractusx.ssi.lib.exception.json.TransformJsonLdException; -import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; -import org.eclipse.tractusx.ssi.lib.exception.proof.SignatureGenerateFailedException; -import org.eclipse.tractusx.ssi.lib.exception.proof.UnsupportedSignatureTypeException; -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.DidDocument; -import org.eclipse.tractusx.ssi.lib.model.did.DidParser; -import org.eclipse.tractusx.ssi.lib.model.proof.jws.JWSSignature2020; 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.SignatureType; -import org.eclipse.tractusx.ssi.lib.serialization.jwt.SerializedJwtVCFactoryImpl; import org.springframework.util.MultiValueMap; import java.io.StringWriter; -import java.net.URI; -import java.time.Instant; +import java.security.KeyFactory; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; -import java.util.UUID; import java.util.regex.Pattern; /** @@ -89,77 +70,22 @@ public static String getIdentifierType(String identifier) { } } - /** - * Gets credential. - * - * @param subject the subject - * @param types the types - * @param issuerDoc the issuer doc - * @param privateKeyBytes the private key bytes - * @param holderDid the holder did - * @return the credential - */ - public static HoldersCredential getHoldersCredential(VerifiableCredentialSubject subject, List types, DidDocument issuerDoc, byte[] privateKeyBytes, String holderDid, List contexts, Date expiryDate, boolean selfIssued) { - List cloneTypes = new ArrayList<>(types); - - // Create VC - VerifiableCredential verifiableCredential = createVerifiableCredential(issuerDoc, types, - subject, privateKeyBytes, contexts, expiryDate); + public static HoldersCredential convertVerifiableCredential(VerifiableCredential verifiableCredential, CredentialCreationConfig config) { + List cloneTypes = new ArrayList<>(config.getTypes()); cloneTypes.remove(VerifiableCredentialType.VERIFIABLE_CREDENTIAL); // Create Credential return HoldersCredential.builder() - .holderDid(holderDid) - .issuerDid(issuerDoc.getId().toString()) + .holderDid(config.getHolderDid()) + .issuerDid(config.getIssuerDoc().getId().toString()) .type(String.join(",", cloneTypes)) .credentialId(verifiableCredential.getId().toString()) .data(verifiableCredential) - .selfIssued(selfIssued) + .selfIssued(config.isSelfIssued()) .build(); } - @SneakyThrows({ UnsupportedSignatureTypeException.class, InvalidPrivateKeyFormatException.class, SignatureGenerateFailedException.class, TransformJsonLdException.class }) - private static VerifiableCredential createVerifiableCredential(DidDocument issuerDoc, List verifiableCredentialType, VerifiableCredentialSubject verifiableCredentialSubject, byte[] privateKey, List contexts, Date expiryDate) { - // VC Builder - - // if the credential does not contain the JWS proof-context add it - URI jwsUri = URI.create("https://w3id.org/security/suites/jws-2020/v1"); - if (!contexts.contains(jwsUri)) { - contexts.add(jwsUri); - } - - // check if the expiryDate is set - // if its null then it will be ignored from the SSI Lib - // (VerifiableCredentialBuilder) and will not be added to the VC - Instant expiryInstant = null; - if (expiryDate != null) { - expiryInstant = expiryDate.toInstant(); - } - - URI id = URI.create(UUID.randomUUID().toString()); - VerifiableCredentialBuilder builder = new VerifiableCredentialBuilder() - .context(contexts) - .id(URI.create(issuerDoc.getId() + "#" + id)) - .type(verifiableCredentialType) - .issuer(issuerDoc.getId()) - .expirationDate(expiryInstant) - .issuanceDate(Instant.now()) - .credentialSubject(verifiableCredentialSubject); - - LinkedDataProofGenerator generator = LinkedDataProofGenerator.newInstance(SignatureType.JWS); - URI verificationMethod = issuerDoc.getVerificationMethods().get(0).getId(); - - JWSSignature2020 proof = new JWSSignature2020(generator.createProof(builder.build(), verificationMethod, - new X25519PrivateKey(privateKey))); - - // Adding Proof to VC - builder.proof(proof); - - // Create Credential - return builder.build(); - } - @SneakyThrows public static String getKeyString(byte[] privateKeyBytes, String keyType) { StringWriter stringWriter = new StringWriter(); @@ -170,28 +96,25 @@ public static String getKeyString(byte[] privateKeyBytes, String keyType) { return stringWriter.toString(); } + public static SecureTokenRequest getSecureTokenRequest(MultiValueMap map) { final ObjectMapper objectMapper = new ObjectMapper(); Map singleValueMap = map.toSingleValueMap(); return objectMapper.convertValue(singleValueMap, SecureTokenRequest.class); } - @SneakyThrows({ DidParseException.class }) - public static String vcAsJwt(Wallet issuerWallet, Wallet holderWallet, VerifiableCredential vc, WalletKeyService walletKeyService) { - - Did issuerDid = DidParser.parse(issuerWallet.getDid()); - Did holderDid = DidParser.parse(holderWallet.getDid()); - - // JWT Factory - SerializedJwtVCFactoryImpl vcFactory = new SerializedJwtVCFactoryImpl( - new SignedJwtFactory(new OctetKeyPairFactory())); + @SneakyThrows + public static ECPrivateKey ecPrivateFrom(byte[] encoded) { + var kf = KeyFactory.getInstance("EC"); + var privateKeySpec = new PKCS8EncodedKeySpec(encoded); + return (ECPrivateKey) kf.generatePrivate(privateKeySpec); + } - X25519PrivateKey privateKey = (X25519PrivateKey) walletKeyService.getPrivateKeyByWalletIdAndAlgorithm(issuerWallet.getId(), SupportedAlgorithms.ED25519); - // JWT Factory - SignedJWT vcJWT = vcFactory.createVCJwt(issuerDid, holderDid, vc, - privateKey, - walletKeyService.getWalletKeyIdByWalletId(issuerWallet.getId()) - ); - return vcJWT.serialize(); + @SneakyThrows + public static ECPublicKey ecPublicFrom(byte[] encoded) { + var kf = KeyFactory.getInstance("EC"); + var publicKeySpec = new X509EncodedKeySpec(encoded); + return (ECPublicKey) kf.generatePublic(publicKeySpec); } + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 0ad1e5594..3c01dcab1 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,3 +1,22 @@ +################################################################################ +# 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 +################################################################################ + server: port: ${APPLICATION_PORT:8087} shutdown: graceful @@ -75,6 +94,8 @@ miw: authorityWalletBpn: ${AUTHORITY_WALLET_BPN:BPNL000000000000} authorityWalletName: ${AUTHORITY_WALLET_NAME:Catena-X} authorityWalletDid: ${AUTHORITY_WALLET_DID:did:web:localhost:BPNL000000000000} + authoritySigningServiceType: ${AUTHORITY_SIGNING_SERVICE_TYPE:LOCAL} + localSigningKeyStorageType: ${LOCAL_SIGNING_KEY_STORAGE_TYPE:DB} vcContexts: ${VC_SCHEMA_LINK:https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/businessPartnerData.json} summaryVcContexts: ${SUMMARY_VC_SCHEMA_LINK:https://www.w3.org/2018/credentials/v1, https://catenax-ng.github.io/product-core-schemas/SummaryVC.json} vcExpiryDate: ${VC_EXPIRY_DATE:01-10-2023} #dd-MM-yyyy ie. 01-01-2025 expiry date will be 2024-12-31T18:30:00Z in VC @@ -91,5 +112,8 @@ miw: token-url: ${miw.security.auth-server-url}/realms/${miw.security.realm}/protocol/openid-connect/token refresh-token-url: ${miw.security.token-url} + sts: token-duration: 60000 + token-service: org.eclipse.tractusx.managedidentitywallets.service. + diff --git a/src/main/resources/db/changelog/changes/init.sql b/src/main/resources/db/changelog/changes/init.sql index d6b396fc6..83823c4e4 100644 --- a/src/main/resources/db/changelog/changes/init.sql +++ b/src/main/resources/db/changelog/changes/init.sql @@ -77,4 +77,9 @@ COMMENT ON TABLE public.holders_credential IS 'This table will store holders cre COMMENT ON COLUMN public.holders_credential.is_stored IS 'true is VC is stored using store VC api(Not issued by MIW)'; --changeset nitin:2 -ALTER TABLE public.wallet_key ADD key_id varchar(255) NULL; \ No newline at end of file +ALTER TABLE public.wallet_key ADD key_id varchar(255) NULL; + +--changeset pmanaras:3 +ALTER TABLE public.wallet ADD signing_service_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL'; + + diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfigTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfigTest.java new file mode 100644 index 000000000..07ccba5ad --- /dev/null +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/domain/CredentialCreationConfigTest.java @@ -0,0 +1,157 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.domain; + +import org.eclipse.tractusx.managedidentitywallets.constant.SupportedAlgorithms; +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.verifiable.credential.VerifiableCredentialStatus; +import org.eclipse.tractusx.ssi.lib.model.verifiable.credential.VerifiableCredentialSubject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; + +import java.net.URI; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class CredentialCreationConfigTest { + + @Test + void shouldBuildWithRequiredAttributes() { + CredentialCreationConfig build = assertDoesNotThrow(() -> CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .expiryDate(new Date()) + .subject(mockCredentialSubject()) + .types(Collections.emptyList()) + .issuerDoc(Mockito.mock(DidDocument.class)) + .holderDid(Mockito.mock(Did.class).toString()) + .contexts(Collections.emptyList()) + .verifiableCredentialStatus(Mockito.mock(VerifiableCredentialStatus.class)) + .vcId(URI.create("yada://test.com")) + .keyName("keyName") + .algorithm(SupportedAlgorithms.ED25519) + .build()); + assertNotNull(build); + assertNotNull(build.getExpiryDate()); + assertNotNull(build.getSubject()); + assertNotNull(build.getTypes()); + assertNotNull(build.getIssuerDoc()); + assertNotNull(build.getHolderDid()); + assertNotNull(build.getContexts()); + assertNotNull(build.getVerifiableCredentialStatus()); + assertNotNull(build.getVcId()); + assertNotNull(build.getKeyName()); + assertNotNull(build.getEncoding()); + assertFalse(build.isSelfIssued()); + } + + @Test + void shouldBuildWhenVcIdIsString() { + + CredentialCreationConfig build = assertDoesNotThrow(() -> CredentialCreationConfig.builder() + .encoding(VerifiableEncoding.JSON_LD) + .expiryDate(new Date()) + .subject(mockCredentialSubject()) + .types(Collections.emptyList()) + .issuerDoc(Mockito.mock(DidDocument.class)) + .holderDid(Mockito.mock(Did.class).toString()) + .contexts(Collections.emptyList()) + .verifiableCredentialStatus(Mockito.mock(VerifiableCredentialStatus.class)) + .keyName("keyName") + .algorithm(SupportedAlgorithms.ED25519) + .build()); + } + + @ParameterizedTest + @MethodSource("testConfigs") + void shouldThrowIfRequiredAttributesMissing(TestConfig conf) { + assertThrows(NullPointerException.class, () -> CredentialCreationConfig + .builder().expiryDate(conf.expiryDate) + .subject(conf.subject) + .types(conf.types) + .issuerDoc(conf.issuerDoc) + .holderDid(conf.holderDid) + .contexts(conf.contexts) + .encoding(conf.encoding) + .keyName(conf.keyName)); + + + } + + @Test + void shouldThrowWhenSettingIllegalVcId() { + CredentialCreationConfig.CredentialCreationConfigBuilder builder = CredentialCreationConfig.builder(); + assertThrows(IllegalArgumentException.class, () -> builder.vcId(42)); + } + + @Test + void shouldNotThrowWhenVcIdValid(){ + CredentialCreationConfig.CredentialCreationConfigBuilder builder = CredentialCreationConfig.builder(); + assertDoesNotThrow(() -> builder.vcId("https://test.com")); + assertDoesNotThrow(() -> builder.vcId(URI.create("https://test.com"))); + } + + private static Stream testConfigs() { + return Stream.of( + Arguments.of(new TestConfig(null, null, null, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), null, null, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), null, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, null, null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, Mockito.mock(DidDocument.class), null, null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, Mockito.mock(DidDocument.class), Mockito.mock(Did.class).toString(), null, null, null, null)), + Arguments.of(new TestConfig(mockCredentialSubject(), Collections.emptyList(), new byte[]{}, Mockito.mock(DidDocument.class), Mockito.mock(Did.class).toString(), Collections.emptyList(), null, null, null)) + ); + } + + private record TestConfig( + VerifiableCredentialSubject subject, + List types, + byte[] privateKey, + DidDocument issuerDoc, + String holderDid, + List contexts, + Date expiryDate, + + String keyName, + + VerifiableEncoding encoding + + ) { + } + + + private static VerifiableCredentialSubject mockCredentialSubject() { + return new VerifiableCredentialSubject(Map.of("id", "42")); + } + +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/migration/SetSigningServiceTypeToExistingWalletsTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/migration/SetSigningServiceTypeToExistingWalletsTest.java new file mode 100644 index 000000000..ec7fed508 --- /dev/null +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/migration/SetSigningServiceTypeToExistingWalletsTest.java @@ -0,0 +1,132 @@ +/* + * ******************************************************************************* + * 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.managedidentitywallets.migration; + +import liquibase.Liquibase; +import liquibase.database.Database; +import liquibase.database.DatabaseFactory; +import liquibase.database.jvm.JdbcConnection; +import liquibase.resource.ClassLoaderResourceAccessor; +import lombok.SneakyThrows; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class SetSigningServiceTypeToExistingWalletsTest { + public static final int EXPECTED_WALLET_COUNT = 5; + private static Database database; + + @BeforeAll + @SneakyThrows + public static void beforeAll() { + Connection conn = DriverManager.getConnection( + "jdbc:h2:mem:framework_test;INIT=CREATE SCHEMA IF NOT EXISTS migration", + "admin", + "password"); + + database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(conn)); + } + + @SneakyThrows + @Test + void migrateWallets() { + var connection = (JdbcConnection) database.getConnection(); + + // 1. apply changelog without signing service-type + Liquibase liquibase = new Liquibase("db/signing-service-migration-test/without-changes.xml", new ClassLoaderResourceAccessor(), database); + liquibase.update((String) null); + + // assert that column for signing_service_type does not exist + + List columns = getColumnList(connection); + assertFalse(columns.contains("signing_service_type".toUpperCase())); + + // insert Wallets + insertWallets(connection); + + // assert that there are 5 Wallets created + String sqlCount = "SELECT count(*) as count FROM public.wallet"; + Statement st = connection.createStatement(); + ResultSet count = st.executeQuery(sqlCount); + assertTrue(count.next()); + assertEquals(EXPECTED_WALLET_COUNT, count.getInt("count")); + + // run migration + Liquibase migrate = new Liquibase("db/signing-service-migration-test/signing-service-changelog.xml", new ClassLoaderResourceAccessor(), database); + migrate.update((String) null); + + // assert that column was created + List newColumns = getColumnList(connection); + assertTrue(newColumns.contains("signing_service_type".toUpperCase())); + + // assert that every wallet now has signing_service_type = LOCAL by default + String walletSql = "SELECT * FROM public.wallet"; + Statement walletStatement = connection.createStatement(); + ResultSet walletResult = walletStatement.executeQuery(walletSql); + while(walletResult.next()){ + assertEquals("LOCAL", walletResult.getString("signing_service_type".toUpperCase())); + } + } + + @SneakyThrows + private List getColumnList(JdbcConnection connection){ + String selectColumns = "SHOW COLUMNS FROM public.wallet"; + // COLUMN_NAME | TYPE | IS_NULLABLE | KEY | DEFAULT + ResultSet resultSet = connection.createStatement().executeQuery(selectColumns); + List columns = new ArrayList<>(); + while (resultSet.next()) { + columns.add(resultSet.getString("COLUMN_NAME")); + } + return columns; + } + + @SneakyThrows + private void insertWallets(JdbcConnection connection) { + String insert = "INSERT INTO wallet(id,name,did,bpn,algorithm,did_document,created_at) VALUES(?,?,?,?,?,?,?)"; + PreparedStatement pst = connection.prepareStatement(insert); + + for (int i = 1; i <= EXPECTED_WALLET_COUNT; i++) { + pst.setInt(1, i); + pst.setString(2, "name"+i); + pst.setString(3, "did"+i); + pst.setString(4, "bpn"+i); + pst.setString(5, "algorithm"+i); + pst.setString(6, "document"+i); + pst.setTimestamp(7, Timestamp.from(Instant.now())); + pst.execute(); + } + } + +} diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialServiceTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialServiceTest.java index 5d682abfd..e64a6e0c7 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialServiceTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/service/IssuersCredentialServiceTest.java @@ -37,11 +37,18 @@ import org.eclipse.tractusx.managedidentitywallets.dao.entity.WalletKey; import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; +import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletKeyRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.dto.CredentialVerificationRequest; import org.eclipse.tractusx.managedidentitywallets.dto.CredentialsResponse; 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.interfaces.SecureTokenService; +import org.eclipse.tractusx.managedidentitywallets.signing.LocalKeyProvider; +import org.eclipse.tractusx.managedidentitywallets.signing.LocalSigningServiceImpl; +import org.eclipse.tractusx.managedidentitywallets.signing.SigningService; +import org.eclipse.tractusx.managedidentitywallets.utils.EncryptionUtils; import org.eclipse.tractusx.managedidentitywallets.utils.MockUtil; import org.eclipse.tractusx.managedidentitywallets.utils.TestUtils; import org.eclipse.tractusx.ssi.lib.crypt.KeyPair; @@ -49,7 +56,6 @@ import org.eclipse.tractusx.ssi.lib.crypt.x25519.X25519PrivateKey; import org.eclipse.tractusx.ssi.lib.did.resolver.DidResolver; import org.eclipse.tractusx.ssi.lib.exception.did.DidParseException; -import org.eclipse.tractusx.ssi.lib.exception.did.DidResolverException; import org.eclipse.tractusx.ssi.lib.exception.key.InvalidPrivateKeyFormatException; import org.eclipse.tractusx.ssi.lib.exception.key.KeyTransformationException; import org.eclipse.tractusx.ssi.lib.jwt.SignedJwtFactory; @@ -71,13 +77,12 @@ import org.mockito.stubbing.Answer; import org.springframework.security.oauth2.jwt.JwtException; -import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; -import java.text.ParseException; import java.time.Duration; import java.time.Instant; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -90,6 +95,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -104,6 +110,8 @@ class IssuersCredentialServiceTest { private static WalletKeyService walletKeyService; + private static WalletKeyRepository walletKeyRepository; + private static HoldersCredentialRepository holdersCredentialRepository; private static CommonService commonService; @@ -112,6 +120,10 @@ class IssuersCredentialServiceTest { private static IssuersCredentialService issuersCredentialService; + private static SecureTokenService secureTokenService; + + private static EncryptionUtils encryptionUtils; + private static final ObjectMapper objectMapper = new ObjectMapper(); @BeforeAll @@ -122,20 +134,22 @@ public static void beforeAll() throws SQLException { holdersCredentialRepository = Mockito.mock(HoldersCredentialRepository.class); commonService = Mockito.mock(CommonService.class); issuersCredentialRepository = mock(IssuersCredentialRepository.class); + secureTokenService = mock(SecureTokenService.class); + walletKeyRepository = mock(WalletKeyRepository.class); Connection connection = mock(Connection.class); DataSource dataSource = mock(DataSource.class); when(dataSource.getConnection()).thenReturn(connection); + when(miwSettings.encryptionKey()).thenReturn("26FlcjRKOEML8YW699CXlg=="); + encryptionUtils = new EncryptionUtils(miwSettings); + issuersCredentialService = new IssuersCredentialService( issuersCredentialRepository, miwSettings, new SpecificationUtil(), - walletKeyService, - holdersCredentialRepository, - commonService, - objectMapper); + holdersCredentialRepository, commonService, objectMapper); } @BeforeEach @@ -153,7 +167,7 @@ class issueMembershipCredentialTest { @Test void shouldIssueCredentialAsJwt() - throws IOException, InvalidPrivateKeyFormatException, KeyTransformationException { + throws InvalidPrivateKeyFormatException, KeyTransformationException { Map wallets = mockBaseAndHolderWallet(); Wallet baseWallet = (Wallet) wallets.get("base"); String baseWalletBpn = baseWallet.getBpn(); @@ -173,11 +187,24 @@ void shouldIssueCredentialAsJwt() when(walletKey.getKeyId()).thenReturn(KEY_ID); when(walletKey.getId()).thenReturn(42L); when(baseWallet.getAlgorithm()).thenReturn("ED25519"); + when(baseWallet.getSigningServiceType()).thenReturn(SigningServiceType.LOCAL); when(walletKeyService.getPrivateKeyByWalletIdAndAlgorithm(baseWallet.getId(), SupportedAlgorithms.valueOf(baseWallet.getAlgorithm()))) .thenReturn(new X25519PrivateKey(keyPair.getPrivateKey().asStringForStoring(), true)); - when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId())).thenReturn(walletKeyId); + when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId(), SupportedAlgorithms.ED25519)).thenReturn(walletKeyId); when(walletKeyService.getPrivateKeyByWalletIdAsBytes(baseWallet.getId(), "ED25519")).thenReturn(keyPair.getPrivateKey() .asByte()); + + + when(walletKeyService.getPrivateKeyByKeyId(anyString(), any())).thenReturn(keyPair.getPrivateKey()); + when(walletKeyRepository.getByAlgorithmAndWallet_Bpn(anyString(), anyString())).thenReturn(walletKey); + + LocalSigningServiceImpl localSigningService = new LocalSigningServiceImpl(secureTokenService); + localSigningService.setKeyProvider(new LocalKeyProvider(walletKeyService, walletKeyRepository, encryptionUtils)); + + Map map = new HashMap<>(); + map.put(SigningServiceType.LOCAL, localSigningService); + + issuersCredentialService.setKeyService(map); CredentialsResponse credentialsResponse = assertDoesNotThrow( () -> issuersCredentialService.issueMembershipCredential( issueMembershipCredentialRequest, @@ -195,7 +222,7 @@ class issueFrameWorkCredentialTest { @Test void shouldIssueCredentialAsJwt() - throws IOException, InvalidPrivateKeyFormatException, ParseException, JwtException, KeyTransformationException { + throws InvalidPrivateKeyFormatException, JwtException, KeyTransformationException { Map wallets = mockBaseAndHolderWallet(); Wallet baseWallet = (Wallet) wallets.get("base"); String baseWalletBpn = baseWallet.getBpn(); @@ -228,7 +255,20 @@ void shouldIssueCredentialAsJwt() .asByte()); when(walletKeyService.getPrivateKeyByWalletIdAndAlgorithm(baseWallet.getId(), SupportedAlgorithms.valueOf(baseWallet.getAlgorithm()))) .thenReturn(new X25519PrivateKey(keyPair.getPrivateKey().asStringForStoring(), true)); - when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId())).thenReturn(walletKeyId); + when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId(), SupportedAlgorithms.ED25519)).thenReturn(walletKeyId); + + + when(baseWallet.getSigningServiceType()).thenReturn(SigningServiceType.LOCAL); + when(walletKeyService.getPrivateKeyByKeyId(anyString(), any())).thenReturn(keyPair.getPrivateKey()); + when(walletKeyRepository.getByAlgorithmAndWallet_Bpn(anyString(), anyString())).thenReturn(walletKey); + + LocalSigningServiceImpl localSigningService = new LocalSigningServiceImpl(secureTokenService); + localSigningService.setKeyProvider(new LocalKeyProvider(walletKeyService, walletKeyRepository, encryptionUtils)); + + Map map = new HashMap<>(); + map.put(SigningServiceType.LOCAL, localSigningService); + + issuersCredentialService.setKeyService(map); CredentialsResponse credentialsResponse = assertDoesNotThrow( () -> issuersCredentialService.issueFrameworkCredential(request, true, baseWalletBpn)); @@ -242,7 +282,7 @@ void shouldIssueCredentialAsJwt() class issueDismantlerCredentialTest { @Test - void shouldIssueCredentialAsJwt() throws IOException, InvalidPrivateKeyFormatException, ParseException, + void shouldIssueCredentialAsJwt() throws InvalidPrivateKeyFormatException, JwtException, KeyTransformationException { Map wallets = mockBaseAndHolderWallet(); Wallet baseWallet = (Wallet) wallets.get("base"); @@ -268,8 +308,20 @@ void shouldIssueCredentialAsJwt() throws IOException, InvalidPrivateKeyFormatExc when(walletKeyService.getPrivateKeyByWalletIdAndAlgorithm(baseWallet.getId(), SupportedAlgorithms.valueOf(baseWallet.getAlgorithm()))) .thenReturn(new X25519PrivateKey(keyPair.getPrivateKey().asStringForStoring(), true)); when(walletKeyService.getPrivateKeyByWalletIdAsBytes(baseWallet.getId(), "ED25519")).thenReturn(keyPair.getPrivateKey().asByte()); - when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId())).thenReturn(walletKeyId); + when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId(), SupportedAlgorithms.ED25519)).thenReturn(walletKeyId); + + + when(baseWallet.getSigningServiceType()).thenReturn(SigningServiceType.LOCAL); + when(walletKeyService.getPrivateKeyByKeyId(anyString(), any())).thenReturn(keyPair.getPrivateKey()); + when(walletKeyRepository.getByAlgorithmAndWallet_Bpn(anyString(), anyString())).thenReturn(walletKey); + LocalSigningServiceImpl localSigningService = new LocalSigningServiceImpl(secureTokenService); + localSigningService.setKeyProvider(new LocalKeyProvider(walletKeyService, walletKeyRepository, encryptionUtils)); + + Map map = new HashMap<>(); + map.put(SigningServiceType.LOCAL, localSigningService); + + issuersCredentialService.setKeyService(map); CredentialsResponse credentialsResponse = assertDoesNotThrow( () -> issuersCredentialService.issueDismantlerCredential(request, true, baseWalletBpn)); validateCredentialResponse(credentialsResponse, MockUtil.buildDidDocument(new Did(new DidMethod("web"), @@ -282,7 +334,7 @@ void shouldIssueCredentialAsJwt() throws IOException, InvalidPrivateKeyFormatExc class issueCredentialUsingBaseWallet { @Test - void shouldIssueCredentialAsJwt() throws IOException, ParseException, InvalidPrivateKeyFormatException, + void shouldIssueCredentialAsJwt() throws InvalidPrivateKeyFormatException, KeyTransformationException, JwtException { Map wallets = mockBaseAndHolderWallet(); Wallet baseWallet = (Wallet) wallets.get("base"); @@ -308,7 +360,7 @@ void shouldIssueCredentialAsJwt() throws IOException, ParseException, InvalidPri when(holdersCredentialRepository.save(any(HoldersCredential.class))) .thenAnswer(new Answer() { @Override - public HoldersCredential answer(InvocationOnMock invocation) throws Throwable { + public HoldersCredential answer(InvocationOnMock invocation) { HoldersCredential argument = invocation.getArgument(0, HoldersCredential.class); argument.setId(42L); return argument; @@ -321,7 +373,20 @@ public HoldersCredential answer(InvocationOnMock invocation) throws Throwable { when(walletKey.getId()).thenReturn(42L); when(walletKeyService.getPrivateKeyByWalletIdAndAlgorithm(baseWallet.getId(), SupportedAlgorithms.valueOf(baseWallet.getAlgorithm()))) .thenReturn(new X25519PrivateKey(keyPair.getPrivateKey().asStringForStoring(), true)); - when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId())).thenReturn(walletKeyId); + when(walletKeyService.getWalletKeyIdByWalletId(baseWallet.getId(), SupportedAlgorithms.ED25519)).thenReturn(walletKeyId); + + when(walletKeyRepository.getByKeyIdAndAlgorithm(anyString(), anyString())).thenReturn(walletKey); + when(baseWallet.getSigningServiceType()).thenReturn(SigningServiceType.LOCAL); + when(walletKeyService.getPrivateKeyByKeyId(anyString(), any())).thenReturn(keyPair.getPrivateKey()); + when(walletKeyRepository.getByAlgorithmAndWallet_Bpn(anyString(), anyString())).thenReturn(walletKey); + + LocalSigningServiceImpl localSigningService = new LocalSigningServiceImpl(secureTokenService); + localSigningService.setKeyProvider(new LocalKeyProvider(walletKeyService, walletKeyRepository, encryptionUtils)); + + Map map = new HashMap<>(); + map.put(SigningServiceType.LOCAL, localSigningService); + + issuersCredentialService.setKeyService(map); CredentialsResponse credentialsResponse = assertDoesNotThrow( () -> issuersCredentialService.issueCredentialUsingBaseWallet( @@ -438,7 +503,7 @@ private void mockCommon( when(holdersCredentialRepository.save(any(HoldersCredential.class))) .thenAnswer(new Answer() { @Override - public HoldersCredential answer(InvocationOnMock invocation) throws Throwable { + public HoldersCredential answer(InvocationOnMock invocation) { HoldersCredential argument = invocation.getArgument(0, HoldersCredential.class); argument.setId(42L); return argument; @@ -459,7 +524,7 @@ private void validateCredentialResponse(CredentialsResponse credentialsResponse, SignedJwtVerifier jwtVerifier = new SignedJwtVerifier(new DidResolver() { @Override - public DidDocument resolve(Did did) throws DidResolverException { + public DidDocument resolve(Did did) { return didDocument; } diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/MockUtil.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/MockUtil.java index 7743c10b6..652c975c8 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/MockUtil.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/MockUtil.java @@ -208,7 +208,6 @@ public static void makeFilterWorkForHolder(HoldersCredentialRepository holdersCr Instant.now().plus(Duration.ofDays(5)) ); HoldersCredential holdersCredential = mockHolderCredential(verifiableCredential); - //getRepository().findAll(specification, pageRequest); when(holdersCredentialRepository.findAll(any(Specification.class), any(PageRequest.class))).thenReturn( new PageImpl<>(List.of(holdersCredential)) ); @@ -224,7 +223,6 @@ public static void makeFilterWorkForIssuer(IssuersCredentialRepository holdersCr Instant.now().plus(Duration.ofDays(5)) ); IssuersCredential holdersCredential = mockIssuerCredential(verifiableCredential); - //getRepository().findAll(specification, pageRequest); when(holdersCredentialRepository.findAll(any(Specification.class), any(PageRequest.class))).thenReturn( new PageImpl<>(List.of(holdersCredential)) ); @@ -234,7 +232,7 @@ public static void makeCreateWorkForHolder(HoldersCredentialRepository holdersCr when(holdersCredentialRepository.save(any(HoldersCredential.class))) .thenAnswer(new Answer() { @Override - public HoldersCredential answer(InvocationOnMock invocation) throws Throwable { + public HoldersCredential answer(InvocationOnMock invocation) { HoldersCredential argument = invocation.getArgument(0, HoldersCredential.class); argument.setId(42L); return argument; @@ -247,7 +245,7 @@ public static void makeCreateWorkForIssuer(IssuersCredentialRepository issuersCr when(issuersCredentialRepository.save(any(IssuersCredential.class))) .thenAnswer(new Answer() { @Override - public IssuersCredential answer(InvocationOnMock invocation) throws Throwable { + public IssuersCredential answer(InvocationOnMock invocation) { IssuersCredential argument = invocation.getArgument(0, IssuersCredential.class); argument.setId(42L); return argument; diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java index 12897cf50..066847b3d 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/utils/TestUtils.java @@ -42,6 +42,7 @@ import org.eclipse.tractusx.managedidentitywallets.dao.repository.HoldersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.IssuersCredentialRepository; import org.eclipse.tractusx.managedidentitywallets.dao.repository.WalletRepository; +import org.eclipse.tractusx.managedidentitywallets.domain.SigningServiceType; import org.eclipse.tractusx.managedidentitywallets.dto.CreateWalletRequest; import org.eclipse.tractusx.managedidentitywallets.dto.IssueFrameworkCredentialRequest; import org.eclipse.tractusx.managedidentitywallets.dto.IssueMembershipCredentialRequest; @@ -111,6 +112,7 @@ public static Wallet createWallet(String bpn, String did, WalletRepository walle .didDocument(DidDocument.fromJson(didDocument)) .algorithm(StringPool.ED_25519) .name(bpn) + .signingServiceType(SigningServiceType.LOCAL) .build(); return walletRepository.save(wallet); } @@ -265,6 +267,7 @@ public static Wallet buildWallet(String bpn, String did, String didJson) { .didDocument(DidDocument.fromJson(didJson)) .algorithm(StringPool.ED_25519) .name(bpn) + .signingServiceType(SigningServiceType.LOCAL) .build(); } } diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/DismantlerHoldersCredentialTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/DismantlerHoldersCredentialTest.java index 89fee527f..dbe29b873 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/DismantlerHoldersCredentialTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/DismantlerHoldersCredentialTest.java @@ -49,7 +49,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.ContextConfiguration; import java.util.List; diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/FrameworkHoldersCredentialTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/FrameworkHoldersCredentialTest.java index 1db24128f..23a0b028b 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/FrameworkHoldersCredentialTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/FrameworkHoldersCredentialTest.java @@ -100,7 +100,7 @@ void issueFrameworkCredentialTest403() { } @Test - void issueFrameworkCredentialWithInvalidBpnAccessTest403() throws JsonProcessingException, JSONException { + void issueFrameworkCredentialWithInvalidBpnAccessTest403() throws JSONException { String bpn = TestUtils.getRandomBpmNumber(); String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); TestUtils.createWallet(bpn, did, walletRepository); @@ -118,7 +118,7 @@ void issueFrameworkCredentialWithInvalidBpnAccessTest403() throws JsonProcessing } @Test - void issueFrameWorkVCToBaseWalletTest201() throws JSONException, JsonProcessingException { + void issueFrameWorkVCToBaseWalletTest201() throws JSONException { String bpn = miwSettings.authorityWalletBpn(); String type = "PcfCredential"; //create wallet @@ -156,7 +156,7 @@ void issueFrameWorkVCTest201(IssueFrameworkCredentialRequest request) throws Jso String type = request.getType(); - createAndValidateVC(bpn, did, type); + createAndValidateVC(bpn, type); //check in issuer tables List issuerVCs = issuersCredentialRepository.getByIssuerDidAndHolderDidAndType(miwSettings.authorityWalletDid(), did, MIWVerifiableCredentialType.USE_CASE_FRAMEWORK_CONDITION); Assertions.assertEquals(1, issuerVCs.size()); @@ -176,7 +176,7 @@ static Stream getTypes() { @Test @DisplayName("Issue framework with invalid type") - void issueFrameworkCredentialTest400() throws JsonProcessingException, JSONException { + void issueFrameworkCredentialTest400() throws JSONException { String bpn = TestUtils.getRandomBpmNumber(); String did = DidWebFactory.fromHostnameAndPath(miwSettings.host(), bpn).toString(); Wallet wallet = TestUtils.createWallet(bpn, did, walletRepository); @@ -195,7 +195,7 @@ void issueFrameworkCredentialTest400() throws JsonProcessingException, JSONExcep } - private void createAndValidateVC(String bpn, String did, String type) throws JsonProcessingException, JSONException { + private void createAndValidateVC(String bpn, String type) throws JsonProcessingException, JSONException { //create wallet String baseBpn = miwSettings.authorityWalletBpn(); String defaultLocation = miwSettings.host() + COLON_SEPARATOR + bpn; diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java index 7698ffcf7..ccab25372 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/HoldersCredentialTest.java @@ -225,7 +225,7 @@ void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcepti @Test @DisplayName("Get Credentials as JWT") - void getCredentialsAsJWT200() throws com.fasterxml.jackson.core.JsonProcessingException, JSONException { + void getCredentialsAsJWT200() throws JSONException { String baseDID = miwSettings.authorityWalletDid(); String bpn = TestUtils.getRandomBpmNumber(); diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java index e7c394075..8f96a1dd6 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/IssuersCredentialTest.java @@ -162,7 +162,7 @@ void getCredentials200() throws com.fasterxml.jackson.core.JsonProcessingExcepti @Test @DisplayName("Get Credentials as JWT") - void getCredentialsAsJWT200() throws com.fasterxml.jackson.core.JsonProcessingException, JSONException { + void getCredentialsAsJWT200() throws JSONException { String baseBPN = miwSettings.authorityWalletBpn(); String holderBpn = TestUtils.getRandomBpmNumber(); String holderDID = "did:web:localhost:" + holderBpn; diff --git a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java index 3373c9c64..4537be004 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/vc/VerifiableCredentialIssuerEqualProofSignerTest.java @@ -21,7 +21,6 @@ package org.eclipse.tractusx.managedidentitywallets.vc; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.SneakyThrows; import org.eclipse.tractusx.managedidentitywallets.ManagedIdentityWalletsApplication; import org.eclipse.tractusx.managedidentitywallets.config.MIWSettings; @@ -109,7 +108,7 @@ public void test() { } @SneakyThrows - private VerifiableCredential issueVC(String issuerDid, Wallet signerWallet) throws JsonProcessingException { + private VerifiableCredential issueVC(String issuerDid, Wallet signerWallet) { List contexts = new ArrayList(); contexts.add(URI.create("https://www.w3.org/2018/credentials/v1")); // if the credential does not contain the JWS proof-context add it 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 e4b4d4d2f..c562ce977 100644 --- a/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java +++ b/src/test/java/org/eclipse/tractusx/managedidentitywallets/wallet/WalletTest.java @@ -151,7 +151,7 @@ void createWalletTestWithUserToken403() { @Test @DisplayName("Create wallet with invalid BPN, it should return 400 ") - void createWalletWithInvalidBPNTest400() throws JsonProcessingException, JSONException { + void createWalletWithInvalidBPNTest400() throws JSONException { String bpn = "invalid bpn"; String name = "Sample Wallet"; String baseBpn = miwSettings.authorityWalletBpn(); diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml index 8cef28cf0..6bd0a679d 100644 --- a/src/test/resources/application-test.yaml +++ b/src/test/resources/application-test.yaml @@ -1,3 +1,22 @@ +################################################################################ +# 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 +################################################################################ + spring: main: allow-circular-references: true @@ -10,4 +29,8 @@ miw: security: enabled: true realm: miw_test - clientId: miw_private_client \ No newline at end of file + clientId: miw_private_client + +debug: true + + diff --git a/src/test/resources/db/changelog/changes/add-signing-service-column.sql b/src/test/resources/db/changelog/changes/add-signing-service-column.sql new file mode 100644 index 000000000..54ecd8d77 --- /dev/null +++ b/src/test/resources/db/changelog/changes/add-signing-service-column.sql @@ -0,0 +1,5 @@ +--liquibase formatted sql +--changeset pmanaras:3 +ALTER TABLE public.wallet ADD signing_service_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL'; + + diff --git a/src/test/resources/db/changelog/changes/without-changes-init.sql b/src/test/resources/db/changelog/changes/without-changes-init.sql new file mode 100644 index 000000000..236ab799c --- /dev/null +++ b/src/test/resources/db/changelog/changes/without-changes-init.sql @@ -0,0 +1,82 @@ +--liquibase formatted sql + +--changeset nitin:1 +CREATE TABLE IF NOT EXISTS public.wallet +( + id bigserial NOT NULL, + name varchar(255) NOT NULL, + did varchar(255) NOT NULL, + bpn varchar(255) NOT NULL, + algorithm varchar(255) NOT NULL DEFAULT 'ED25519'::character varying, + did_document text NOT NULL, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT uk_bpn UNIQUE (bpn), + CONSTRAINT uk_did UNIQUE (did), + CONSTRAINT wallet_pkey PRIMARY KEY (id), + CONSTRAINT wallet_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE SET NULL +); +COMMENT ON TABLE public.wallet IS 'This table will store wallets'; + +CREATE TABLE IF NOT EXISTS public.wallet_key +( + id bigserial NOT NULL, + wallet_id bigserial NOT NULL, + vault_access_token varchar(1000) NOT NULL, + reference_key varchar(255) NOT NULL, + private_key text NOT NULL, + public_key text NOT NULL, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT wallet_key_pkey PRIMARY KEY (id), + CONSTRAINT wallet_fk_2 FOREIGN KEY (wallet_id) REFERENCES public.wallet (id) ON DELETE CASCADE, + CONSTRAINT wallet_key_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE CASCADE +); +COMMENT ON TABLE public.wallet_key IS 'This table will store key pair of wallets'; + + +CREATE TABLE IF NOT EXISTS public.issuers_credential +( + id bigserial NOT NULL, + holder_did varchar(255) NOT NULL, + issuer_did varchar(255) NOT NULL, + credential_id varchar(255) NOT NULL, + credential_data text NOT NULL, + credential_type varchar(255) NULL, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT issuers_credential_pkey PRIMARY KEY (id), + CONSTRAINT issuers_credential_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE SET NULL, + CONSTRAINT issuers_credential_holder_wallet_fk FOREIGN KEY (holder_did) REFERENCES public.wallet (did) ON DELETE CASCADE +); +COMMENT ON TABLE public.issuers_credential IS 'This table will store issuers credentials'; + + +CREATE TABLE IF NOT EXISTS public.holders_credential +( + id bigserial NOT NULL, + holder_did varchar(255) NOT NULL, + issuer_did varchar(255) NOT NULL, + credential_id varchar(255) NOT NULL, + credential_data text NOT NULL, + credential_type varchar(255) NULL, + is_self_issued bool NOT null default false, + is_stored bool NOT null default false, + created_at timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at timestamp(6) NULL, + modified_from varchar(255) NULL, + CONSTRAINT holders_credential_pkey PRIMARY KEY (id), + CONSTRAINT holders_credential_fk FOREIGN KEY (modified_from) REFERENCES public.wallet (bpn) ON DELETE SET NULL, + CONSTRAINT holders_credential_holder_wallet_fk FOREIGN KEY (holder_did) REFERENCES public.wallet (did) ON DELETE CASCADE +); +COMMENT ON TABLE public.holders_credential IS 'This table will store holders credentials'; + +COMMENT ON COLUMN public.holders_credential.is_stored IS 'true is VC is stored using store VC api(Not issued by MIW)'; + +--changeset nitin:2 +ALTER TABLE public.wallet_key ADD key_id varchar(255) NULL; + + diff --git a/src/test/resources/db/signing-service-migration-test/signing-service-changelog.xml b/src/test/resources/db/signing-service-migration-test/signing-service-changelog.xml new file mode 100644 index 000000000..347a37e7b --- /dev/null +++ b/src/test/resources/db/signing-service-migration-test/signing-service-changelog.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/src/test/resources/db/signing-service-migration-test/without-changes.xml b/src/test/resources/db/signing-service-migration-test/without-changes.xml new file mode 100644 index 000000000..171456e23 --- /dev/null +++ b/src/test/resources/db/signing-service-migration-test/without-changes.xml @@ -0,0 +1,27 @@ + + + + +