diff --git a/mock-certify-plugin/pom.xml b/mock-certify-plugin/pom.xml
index 187ce98..fc6e540 100644
--- a/mock-certify-plugin/pom.xml
+++ b/mock-certify-plugin/pom.xml
@@ -129,6 +129,26 @@
slf4j-api
2.0.12
+
+ org.jetbrains.kotlinx
+ kotlinx-datetime-jvm
+ 0.6.0
+
+
+ com.android.identity
+ identity-credential
+ 20231002
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.10.1
+
+
+ co.nstant.in
+ cbor
+ 0.9
+
net.javacrumbs.json-unit
json-unit-assertj
@@ -377,4 +397,4 @@
-
\ No newline at end of file
+
diff --git a/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MDocMockVCIssuancePlugin.java b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MDocMockVCIssuancePlugin.java
new file mode 100644
index 0000000..d5542f9
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MDocMockVCIssuancePlugin.java
@@ -0,0 +1,156 @@
+package io.mosip.certify.mock.integration.service;
+
+import foundation.identity.jsonld.JsonLDObject;
+import io.mosip.certify.api.dto.VCRequestDto;
+import io.mosip.certify.api.dto.VCResult;
+import io.mosip.certify.api.exception.VCIExchangeException;
+import io.mosip.certify.api.spi.VCIssuancePlugin;
+import io.mosip.certify.api.util.ErrorConstants;
+import io.mosip.certify.constants.VCFormats;
+import io.mosip.certify.core.exception.CertifyException;
+import io.mosip.certify.mock.integration.mocks.MdocGenerator;
+import io.mosip.esignet.core.dto.OIDCTransaction;
+import io.mosip.kernel.core.keymanager.spi.KeyStore;
+import io.mosip.kernel.keymanagerservice.constant.KeymanagerConstant;
+import io.mosip.kernel.keymanagerservice.entity.KeyAlias;
+import io.mosip.kernel.keymanagerservice.helper.KeymanagerDBHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cache.CacheManager;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import java.security.Key;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
+
+@ConditionalOnProperty(value = "mosip.certify.integration.vci-plugin", havingValue = "MDocMockVCIssuancePlugin")
+@Component
+@Slf4j
+public class MDocMockVCIssuancePlugin implements VCIssuancePlugin {
+ private static final String AES_CIPHER_FAILED = "aes_cipher_failed";
+ private static final String NO_UNIQUE_ALIAS = "no_unique_alias";
+ private static final String USERINFO_CACHE = "userinfo";
+
+ @Autowired
+ private CacheManager cacheManager;
+
+ @Autowired
+ private KeyStore keyStore;
+
+ @Autowired
+ private KeymanagerDBHelper dbHelper;
+
+ @Value("${mosip.certify.cache.security.secretkey.reference-id}")
+ private String cacheSecretKeyRefId;
+
+ @Value("${mosip.certify.cache.security.algorithm-name}")
+ private String aesECBTransformation;
+
+ @Value("${mosip.certify.cache.secure.individual-id}")
+ private boolean secureIndividualId;
+
+ @Value("${mosip.certify.cache.store.individual-id}")
+ private boolean storeIndividualId;
+
+ @Value("${mosip.certify.mock.vciplugin.mdoc.issuer-key-cert:empty}")
+ private String issuerKeyAndCertificate = null;
+
+ private static final String ACCESS_TOKEN_HASH = "accessTokenHash";
+
+ public static final String CERTIFY_SERVICE_APP_ID = "CERTIFY_SERVICE";
+
+ @Override
+ public VCResult getVerifiableCredentialWithLinkedDataProof(VCRequestDto vcRequestDto, String holderId, Map identityDetails) throws VCIExchangeException {
+ log.error("not implemented the format {}", vcRequestDto);
+ throw new VCIExchangeException(ErrorConstants.NOT_IMPLEMENTED);
+ }
+
+ @Override
+ public VCResult getVerifiableCredential(VCRequestDto vcRequestDto, String holderId, Map identityDetails) throws VCIExchangeException {
+ String accessTokenHash = identityDetails.get(ACCESS_TOKEN_HASH).toString();
+ String documentNumber;
+ try {
+ documentNumber = getIndividualId(getUserInfoTransaction(accessTokenHash));
+ } catch (Exception e) {
+ log.error("Error getting documentNumber", e);
+ throw new VCIExchangeException(ErrorConstants.VCI_EXCHANGE_FAILED);
+ }
+
+ if(vcRequestDto.getFormat().equals(VCFormats.MSO_MDOC)){
+ VCResult vcResult = new VCResult<>();
+ String mdocVc;
+ try {
+ mdocVc = new MdocGenerator().generate(mockDataForMsoMdoc(documentNumber),holderId, issuerKeyAndCertificate);
+ } catch (Exception e) {
+ log.error("Exception on mdoc creation", e);
+ throw new VCIExchangeException(ErrorConstants.VCI_EXCHANGE_FAILED);
+ }
+ vcResult.setCredential(mdocVc);
+ vcResult.setFormat(VCFormats.MSO_MDOC);
+ return vcResult;
+ }
+ log.error("not implemented the format {}", vcRequestDto);
+ throw new VCIExchangeException(ErrorConstants.NOT_IMPLEMENTED);
+ }
+
+ private Map mockDataForMsoMdoc(String documentNumber) {
+ Map data = new HashMap<>();
+ log.info("Setting up the data for mDoc");
+ data.put("family_name","Agatha");
+ data.put("given_name","Joseph");
+ data.put("birth_date", "1994-11-06");
+ data.put("issuing_country", "IN");
+ data.put("document_number", documentNumber);
+ data.put("driving_privileges",new HashMap<>(){{
+ put("vehicle_category_code","A");
+ }});
+ return data;
+ }
+
+ /**
+ * TODO: This function getIndividualId is duplicated with Other VCIPlugin class and can be moved to commons
+ */
+ protected String getIndividualId(OIDCTransaction transaction) {
+ if(!storeIndividualId)
+ return null;
+ return secureIndividualId ? decryptIndividualId(transaction.getIndividualId()) : transaction.getIndividualId();
+ }
+
+ private String decryptIndividualId(String encryptedIndividualId) {
+ try {
+ Cipher cipher = Cipher.getInstance(aesECBTransformation);
+ byte[] decodedBytes = Base64.getUrlDecoder().decode(encryptedIndividualId);
+ cipher.init(Cipher.DECRYPT_MODE, getSecretKeyFromHSM());
+ return new String(cipher.doFinal(decodedBytes, 0, decodedBytes.length));
+ } catch(Exception e) {
+ log.error("Error Cipher Operations of provided secret data.", e);
+ throw new CertifyException(AES_CIPHER_FAILED);
+ }
+ }
+
+ private OIDCTransaction getUserInfoTransaction(String accessTokenHash) {
+ return cacheManager.getCache(USERINFO_CACHE).get(accessTokenHash, OIDCTransaction.class);
+ }
+
+ private Key getSecretKeyFromHSM() {
+ String keyAlias = getKeyAlias(CERTIFY_SERVICE_APP_ID, cacheSecretKeyRefId);
+ if (Objects.nonNull(keyAlias)) {
+ return keyStore.getSymmetricKey(keyAlias);
+ }
+ throw new CertifyException(NO_UNIQUE_ALIAS);
+ }
+
+ private String getKeyAlias(String keyAppId, String keyRefId) {
+ Map> keyAliasMap = dbHelper.getKeyAliases(keyAppId, keyRefId, LocalDateTime.now(ZoneOffset.UTC));
+ List currentKeyAliases = keyAliasMap.get(KeymanagerConstant.CURRENTKEYALIAS);
+ if (!currentKeyAliases.isEmpty() && currentKeyAliases.size() == 1) {
+ return currentKeyAliases.getFirst().getAlias();
+ }
+ log.error("CurrentKeyAlias is not unique. KeyAlias count: {}", currentKeyAliases.size());
+ throw new CertifyException(NO_UNIQUE_ALIAS);
+ }
+}
diff --git a/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java
index 16bbc94..e35ed4b 100644
--- a/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java
+++ b/mock-certify-plugin/src/main/java/io.mosip.certify.mock.integration/service/MockVCIssuancePlugin.java
@@ -24,6 +24,7 @@
import io.mosip.certify.api.exception.VCIExchangeException;
import io.mosip.certify.api.spi.VCIssuancePlugin;
import io.mosip.certify.api.util.ErrorConstants;
+import io.mosip.certify.constants.VCFormats;
import io.mosip.certify.core.exception.CertifyException;
import io.mosip.certify.util.UUIDGenerator;
import io.mosip.esignet.core.dto.OIDCTransaction;
@@ -107,7 +108,7 @@ public VCResult getVerifiableCredentialWithLinkedDataProof(VCReque
VCResult vcResult = new VCResult<>();
vcJsonLdObject = buildJsonLDWithLDProof(identityDetails.get(ACCESS_TOKEN_HASH).toString());
vcResult.setCredential(vcJsonLdObject);
- vcResult.setFormat("ldp_vc");
+ vcResult.setFormat(VCFormats.LDP_VC);
return vcResult;
} catch (Exception e) {
log.error("Failed to build mock VC", e);
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/constants/VCFormats.java b/mock-certify-plugin/src/main/java/io/mosip/certify/constants/VCFormats.java
new file mode 100644
index 0000000..86e518b
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/constants/VCFormats.java
@@ -0,0 +1,11 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+package io.mosip.certify.constants;
+
+public class VCFormats {
+ public static final String MSO_MDOC = "mso_mdoc";
+ public static final String LDP_VC = "ldp_vc";
+}
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/mock/integration/mocks/MdocGenerator.java b/mock-certify-plugin/src/main/java/io/mosip/certify/mock/integration/mocks/MdocGenerator.java
new file mode 100644
index 0000000..2501290
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/mock/integration/mocks/MdocGenerator.java
@@ -0,0 +1,150 @@
+package io.mosip.certify.mock.integration.mocks;
+
+import co.nstant.in.cbor.CborBuilder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.CborException;
+import co.nstant.in.cbor.model.DataItem;
+import com.android.identity.credential.NameSpacedData;
+import com.android.identity.internal.Util;
+import com.android.identity.mdoc.mso.MobileSecurityObjectGenerator;
+import com.android.identity.mdoc.util.MdocUtil;
+import com.android.identity.util.Timestamp;
+import io.mosip.certify.util.*;
+
+import java.io.ByteArrayOutputStream;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+
+public class MdocGenerator {
+
+ public static final String NAMESPACE = "org.iso.18013.5.1";
+ public static final String DOCTYPE = NAMESPACE + ".mDL";
+ public static final String DIGEST_ALGORITHM = "SHA-256";
+ public static final String ECDSA_ALGORITHM = "SHA256withECDSA";
+ public static final long SEED = 42L;
+ public static final DateTimeFormatter FULL_DATE_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE;
+
+ /**
+ * @param data - content of the mdoc
+ * @param holderId - documentNumber of the mDL
+ * @param issuerKeyAndCertificate - Document signet related details
+ * @return
+ * @throws Exception
+ *
+ * As of now, only issuer certificate (DS) is available and its used to sign the mdoc. But as per spec,
+ * DS certificate is signed by the issuing authority’s root CA certificate basically IA creates a certificate chain keeping the
+ * root = root CA certificate
+ * leaf = DS certificate
+ * And only the DS certificate is attached to the credential.
+ * Root certificate is not available as of now and is a limitation.
+ */
+ public String generate(Map data, String holderId, String issuerKeyAndCertificate) throws Exception {
+ KeyPairAndCertificateExtractor keyPairAndCertificateExtractor = new KeyPairAndCertificateExtractor();
+ KeyPairAndCertificate issuerDetails = keyPairAndCertificateExtractor.extract(issuerKeyAndCertificate);
+
+ if (issuerDetails.keyPair() == null) {
+ throw new RuntimeException("Unable to load Crypto details");
+ }
+
+ JwkToKeyConverter jwkToKeyConverter = new JwkToKeyConverter();
+ PublicKey devicePublicKey = jwkToKeyConverter.convertToPublicKey(holderId.replace("did:jwk:", ""));
+ KeyPair issuerKeypair = issuerDetails.keyPair();
+
+ LocalDate issueDate = LocalDate.now();
+ String formattedIssueDate = issueDate.format(FULL_DATE_FORMATTER);
+ LocalDate expiryDate = issueDate.plusYears(5);
+ String formattedExpiryDate = expiryDate.format(FULL_DATE_FORMATTER);
+
+ NameSpacedData.Builder nameSpacedDataBuilder = new NameSpacedData.Builder();
+ nameSpacedDataBuilder.putEntryString(NAMESPACE, "issue_date", formattedIssueDate);
+ nameSpacedDataBuilder.putEntryString(NAMESPACE, "expiry_date", formattedExpiryDate);
+
+ Map drivingPrivileges = (Map) data.get("driving_privileges");
+ drivingPrivileges.put("issue_date", formattedIssueDate);
+ drivingPrivileges.put("expiry_date", formattedExpiryDate);
+
+ data.keySet().forEach(key -> nameSpacedDataBuilder.putEntryString(NAMESPACE, key, data.get(key).toString()));
+
+ NameSpacedData nameSpacedData = nameSpacedDataBuilder.build();
+ Map> generatedIssuerNameSpaces = MdocUtil.generateIssuerNameSpaces(nameSpacedData, new Random(SEED), 16);
+ Map calculateDigestsForNameSpace = MdocUtil.calculateDigestsForNameSpace(NAMESPACE, generatedIssuerNameSpaces, DIGEST_ALGORITHM);
+
+ MobileSecurityObjectGenerator mobileSecurityObjectGenerator = new MobileSecurityObjectGenerator(DIGEST_ALGORITHM, DOCTYPE, devicePublicKey);
+ mobileSecurityObjectGenerator.addDigestIdsForNamespace(NAMESPACE, calculateDigestsForNameSpace);
+
+ Timestamp currentTimestamp = Timestamp.now();
+ Timestamp validUntil = Timestamp.ofEpochMilli(addYearsToDate(currentTimestamp.toEpochMilli(), 2));
+ mobileSecurityObjectGenerator.setValidityInfo(currentTimestamp, currentTimestamp, validUntil, null);
+
+ byte[] mso = mobileSecurityObjectGenerator.generate();
+
+ DataItem coseSign1Sign = Util.coseSign1Sign(
+ issuerKeypair.getPrivate(),
+ ECDSA_ALGORITHM,
+ Util.cborEncode(Util.cborBuildTaggedByteString(mso)),
+ null,
+ Collections.singletonList(issuerDetails.certificate())
+ );
+
+ return construct(generatedIssuerNameSpaces, coseSign1Sign);
+ }
+
+ private String construct(Map> nameSpaces, DataItem issuerAuth) throws CborException {
+ MDoc mDoc = new MDoc(DOCTYPE, new IssuerSigned(nameSpaces, issuerAuth));
+ byte[] cbor = mDoc.toCBOR();
+ return Base64.getUrlEncoder().encodeToString(cbor);
+ }
+
+ private long addYearsToDate(long dateInEpochMillis, int years) {
+ Instant instant = Instant.ofEpochMilli(dateInEpochMillis);
+ Instant futureInstant = instant.plus(years * 365L, ChronoUnit.DAYS);
+ return futureInstant.toEpochMilli();
+ }
+}
+
+
+class MDoc {
+ private final String docType;
+ private final IssuerSigned issuerSigned;
+
+ public MDoc(String docType, IssuerSigned issuerSigned) {
+ this.docType = docType;
+ this.issuerSigned = issuerSigned;
+ }
+
+ public byte[] toCBOR() throws CborException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ CborEncoder cborEncoder = new CborEncoder(byteArrayOutputStream);
+ cborEncoder.encode(
+ new CborBuilder().addMap()
+ .put("docType", docType)
+ .put(CBORConverter.toDataItem("issuerSigned"), CBORConverter.toDataItem(issuerSigned.toMap()))
+ .end()
+ .build()
+ );
+ return byteArrayOutputStream.toByteArray();
+ }
+}
+
+class IssuerSigned {
+ private final Map> nameSpaces;
+ private final DataItem issuerAuth;
+
+ public IssuerSigned(Map> nameSpaces, DataItem issuerAuth) {
+ this.nameSpaces = nameSpaces;
+ this.issuerAuth = issuerAuth;
+ }
+
+ public Map toMap() {
+ Map map = new HashMap<>();
+ map.put("nameSpaces", CBORConverter.toDataItem(nameSpaces));
+ map.put("issuerAuth", issuerAuth);
+ return map;
+ }
+}
+
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/CBORConverter.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/CBORConverter.java
new file mode 100644
index 0000000..418c50c
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/CBORConverter.java
@@ -0,0 +1,65 @@
+package io.mosip.certify.util;
+
+import co.nstant.in.cbor.CborDecoder;
+import co.nstant.in.cbor.model.Array;
+import co.nstant.in.cbor.model.DataItem;
+import co.nstant.in.cbor.model.Map;
+import co.nstant.in.cbor.model.SimpleValue;
+import co.nstant.in.cbor.model.UnicodeString;
+import co.nstant.in.cbor.model.UnsignedInteger;
+
+import java.util.List;
+
+public class CBORConverter {
+
+ public static DataItem toDataItem(Object value) {
+ if (value instanceof DataItem) {
+ return (DataItem) value;
+ } else if (value instanceof String) {
+ return new UnicodeString((String) value);
+ } else if (value instanceof Integer) {
+ return new UnsignedInteger(((Integer) value).longValue());
+ } else if (value instanceof Long) {
+ return new UnsignedInteger((Long) value);
+ } else if (value instanceof Boolean) {
+ return ((Boolean) value) ? SimpleValue.TRUE : SimpleValue.FALSE;
+ } else if (value instanceof java.util.Map, ?>) {
+ Map cborMap = new Map();
+ java.util.Map, ?> map = (java.util.Map, ?>) value;
+ for (java.util.Map.Entry, ?> entry : map.entrySet()) {
+ cborMap.put(new UnicodeString((String) entry.getKey()), toDataItem(entry.getValue()));
+ }
+ return cborMap;
+ } else if (value instanceof List>) {
+ Array cborArray = new Array();
+ List> list = (List>) value;
+ for (Object item : list) {
+ cborArray.add(toDataItem(item));
+ }
+ return cborArray;
+ } else if (value instanceof Object[]) {
+ Array cborArray = new Array();
+ Object[] array = (Object[]) value;
+ for (Object item : array) {
+ cborArray.add(toDataItem(item));
+ }
+ return cborArray;
+ } else if (value instanceof byte[]) {
+ try {
+ List dataItems = CborDecoder.decode((byte[]) value);
+ if (!dataItems.isEmpty()) {
+ return dataItems.get(0);
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Failed to decode ByteArray", e);
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported value: " + value + " " + value.getClass().getSimpleName());
+ }
+
+ return null;
+ }
+}
+
+
+
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/JwkToKeyConverter.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/JwkToKeyConverter.java
new file mode 100644
index 0000000..d7e8a33
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/JwkToKeyConverter.java
@@ -0,0 +1,28 @@
+package io.mosip.certify.util;
+
+import com.nimbusds.jose.jwk.ECKey;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jose.jwk.RSAKey;
+
+import java.nio.charset.StandardCharsets;
+import java.security.PublicKey;
+import java.util.Base64;
+
+
+public class
+JwkToKeyConverter {
+
+ public PublicKey convertToPublicKey(String encodedData) throws Exception {
+ String jwkJsonString = new String(Base64.getUrlDecoder().decode(encodedData), StandardCharsets.UTF_8);
+ JWK jwk = JWK.parse(jwkJsonString);
+
+ if (jwk instanceof RSAKey) {
+ return ((RSAKey) jwk).toPublicKey();
+ } else if (jwk instanceof ECKey) {
+ return ((ECKey) jwk).toPublicKey();
+ }
+
+ throw new IllegalArgumentException("Unsupported key type");
+ }
+
+}
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificate.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificate.java
new file mode 100644
index 0000000..c95e10f
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificate.java
@@ -0,0 +1,7 @@
+package io.mosip.certify.util;
+
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+
+public record KeyPairAndCertificate(KeyPair keyPair, X509Certificate certificate) {
+}
diff --git a/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificateExtractor.java b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificateExtractor.java
new file mode 100644
index 0000000..398b625
--- /dev/null
+++ b/mock-certify-plugin/src/main/java/io/mosip/certify/util/KeyPairAndCertificateExtractor.java
@@ -0,0 +1,49 @@
+package io.mosip.certify.util;
+
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.ByteArrayInputStream;
+import java.security.*;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+@Slf4j
+public class KeyPairAndCertificateExtractor {
+ public KeyPairAndCertificate extract(String keyCert) {
+ String[] splitKeyCert = keyCert.split("\\|\\|");
+ try {
+ X509Certificate certificate = convertStringToX509Certificate((splitKeyCert[1]));
+ return (new KeyPairAndCertificate(getKeyPair(splitKeyCert[0], certificate), certificate));
+ } catch (Exception e) {
+ log.error("Failed to extract key certificate", e);
+ }
+
+ return null;
+ }
+
+ private X509Certificate convertStringToX509Certificate(String certString) throws CertificateException {
+ byte[] certBytes = Base64.getDecoder().decode(certString);
+
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certBytes));
+ }
+
+ private KeyPair getKeyPair(String base64PrivateKey, X509Certificate certificate) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ byte[] privateKeyBytes = Base64.getDecoder().decode(base64PrivateKey);
+
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
+ KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
+
+ PublicKey publicKey = certificate.getPublicKey();
+
+ return new KeyPair(publicKey, privateKey);
+ }
+}
+
+