Skip to content

Commit

Permalink
[INJICERT-789]: Added a new wellknown endpoint for did.json and simpl…
Browse files Browse the repository at this point in the history
…ified the docker compose file

Signed-off-by: Vishwa <[email protected]>
  • Loading branch information
vishwa-vyom committed Dec 29, 2024
1 parent 0824983 commit 3d9cea6
Show file tree
Hide file tree
Showing 15 changed files with 339 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ public class ErrorConstants {
public static final String INVALID_TEMPLATE_ID = "template_with_id_not_found";
public static final String EMPTY_TEMPLATE_CONTENT = "empty_template_content";
public static final String EXPECTED_TEMPLATE_NOT_FOUND = "expected_template_not_found";
public static final String UNSUPPORTED_IN_CURRENT_PLUGIN_MODE = "unsupported_in_current_plugin_mode";
public static final String UNSUPPORTED_ALGORITHM = "unsupported_algorithm";
public static final String INVALID_CERTIFICATE = "invalid_certificate";
public static final String VERIFICATION_METHOD_GENERATION_FAILED = "verification_method_generation_failed";
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public interface VCIssuanceService {
<T> CredentialResponse<T> getCredential(CredentialRequest credentialRequest);

Map<String, Object> getCredentialIssuerMetadata(String version);

Map<String, Object> getDIDDocument();
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public Map<String, Object> getMetadata(
return vcIssuanceService.getCredentialIssuerMetadata(version);
}

@GetMapping(value = "/.well-known/did.json")
public Map<String, Object> getDIDDocument() {
return vcIssuanceService.getDIDDocument();
}


@ResponseBody
@ExceptionHandler(InvalidNonceException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
import io.mosip.certify.proof.ProofValidator;
import io.mosip.certify.proof.ProofValidatorFactory;
import io.mosip.certify.utils.CredentialUtils;
import io.mosip.certify.utils.DIDDocumentUtil;
import io.mosip.certify.vcsigners.VCSigner;
import io.mosip.kernel.keymanagerservice.dto.KeyPairGenerateResponseDto;
import io.mosip.kernel.keymanagerservice.service.KeymanagerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
Expand Down Expand Up @@ -80,6 +83,9 @@ public class CertifyIssuanceServiceImpl implements VCIssuanceService {
@Value("${mosip.certify.data-provider-plugin.issuer-uri}")
private String issuerURI;

@Value("${mosip.certify.data-provider-plugin.issuer-public-key-uri}")
private String issuerPublicKeyURI;

@Value("${mosip.certify.data-provider-plugin.rendering-template-id:}")
private String renderTemplateId;

Expand All @@ -95,6 +101,11 @@ public class CertifyIssuanceServiceImpl implements VCIssuanceService {
@Autowired
private AuditPlugin auditWrapper;

@Autowired
private KeymanagerService keymanagerService;

private Map<String, Object> didDocument;

@Override
public CredentialResponse getCredential(CredentialRequest credentialRequest) {
// 1. Credential Request validation
Expand Down Expand Up @@ -155,6 +166,18 @@ public Map<String, Object> getCredentialIssuerMetadata(String version) {
throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_OPENID_VERSION);
}

@Override
public Map<String, Object> getDIDDocument() {
if(didDocument != null)
return didDocument;

KeyPairGenerateResponseDto keyPairGenerateResponseDto = keymanagerService.getCertificate(keyChooser.get(vcSignAlgorithm).getFirst(), Optional.of(keyChooser.get(vcSignAlgorithm).getLast()));
String certificateString = keyPairGenerateResponseDto.getCertificate();

didDocument = DIDDocumentUtil.generateDIDDocument(vcSignAlgorithm, certificateString, issuerURI, issuerPublicKeyURI);
return didDocument;
}

private Map<String, Object> convertLatestToVd11(LinkedHashMap<String, Object> vciMetadata) {
// Create a list to hold the transformed credentials
List<Map<String, Object>> credentialsList = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ public Map<String, Object> getCredentialIssuerMetadata(String version) {
throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_OPENID_VERSION);
}

@Override
public Map<String, Object> getDIDDocument() {
throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_IN_CURRENT_PLUGIN_MODE);
}

private Map<String, Object> convertLatestToVd11(LinkedHashMap<String, Object> vciMetadata) {
// Create a list to hold the transformed credentials
List<Map<String, Object>> credentialsList = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package io.mosip.certify.utils;

import java.io.ByteArrayInputStream;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HexFormat;
import java.util.Map;

import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.nimbusds.jose.jwk.RSAKey;

import io.ipfs.multibase.Multibase;
import io.mosip.certify.core.constants.ErrorConstants;
import io.mosip.certify.core.constants.SignatureAlg;
import io.mosip.certify.core.exception.CertifyException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DIDDocumentUtil {

private static final String MULTICODEC_PREFIX = "ed01";

public static Map<String, Object> generateDIDDocument(String vcSignAlgorithm, String certificateString, String issuerURI, String issuerPublicKeyURI) {

HashMap<String,Object> didDocument = new HashMap<String,Object>();
didDocument.put("@context", Collections.singletonList("https://www.w3.org/ns/did/v1"));
didDocument.put("alsoKnownAs", new ArrayList<>());
didDocument.put("service", new ArrayList<>());
didDocument.put("id", issuerURI);
didDocument.put("authentication", Collections.singletonList(issuerPublicKeyURI));
didDocument.put("assertionMethod", Collections.singletonList(issuerPublicKeyURI));

Map<String, Object> verificationMethod = null;
PublicKey publicKey = loadPublicKeyFromCertificate(certificateString);
try {
switch (vcSignAlgorithm) {
case SignatureAlg.ED25519_SIGNATURE_SUITE_2018:
verificationMethod = generateEd25519VerificationMethod(publicKey, issuerURI, issuerPublicKeyURI);
break;
case SignatureAlg.ED25519_SIGNATURE_SUITE_2020:
verificationMethod = generateEd25519VerificationMethod(publicKey, issuerURI, issuerPublicKeyURI);
break;
case SignatureAlg.RSA_SIGNATURE_SUITE_2018:
verificationMethod = generateRSAVerificationMethod(publicKey, issuerURI, issuerPublicKeyURI);
break;
default:
log.error("Unsupported signature algorithm provided :" + vcSignAlgorithm);
throw new CertifyException(ErrorConstants.UNSUPPORTED_ALGORITHM);
}
} catch(CertifyException e) {
throw e;
} catch (Exception e) {
log.error("Exception occured while generating verification method for given certificate", e.getMessage(), e);
throw new CertifyException(ErrorConstants.VERIFICATION_METHOD_GENERATION_FAILED);
}

didDocument.put("verificationMethod", Collections.singletonList(verificationMethod));
return didDocument;
}

private static PublicKey loadPublicKeyFromCertificate(String certificateString) {
try {
ByteArrayInputStream fis = new ByteArrayInputStream(certificateString.getBytes());
CertificateFactory certFactory = CertificateFactory.getInstance("X.509", new BouncyCastleProvider());
X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(fis);
return certificate.getPublicKey();
} catch (Exception e) {
log.error("Convertion from certificate to public key failed", e.getMessage(), e);
throw new CertifyException(ErrorConstants.INVALID_CERTIFICATE);
}
}

private static Map<String, Object> generateEd25519VerificationMethod(PublicKey publicKey, String issuerURI, String issuerPublicKeyURI) throws Exception {
BCEdDSAPublicKey edKey = (BCEdDSAPublicKey) publicKey;
byte[] rawBytes = edKey.getPointEncoding();
byte[] multicodecBytes = HexFormat.of().parseHex(MULTICODEC_PREFIX);
byte[] finalBytes = new byte[multicodecBytes.length + rawBytes.length];
System.arraycopy(multicodecBytes, 0, finalBytes, 0, multicodecBytes.length);
System.arraycopy(rawBytes, 0, finalBytes, multicodecBytes.length, rawBytes.length);
String publicKeyMultibase = Multibase.encode(Multibase.Base.Base58BTC, finalBytes);

Map<String, Object> verificationMethod = new HashMap<>();
verificationMethod.put("id", issuerPublicKeyURI);
verificationMethod.put("type", "Ed25519VerificationKey2020");
verificationMethod.put("@context", "https://w3id.org/security/suites/ed25519-2020/v1");
verificationMethod.put("controller", issuerURI);
verificationMethod.put("publicKeyMultibase", publicKeyMultibase);
return verificationMethod;
}

private static Map<String, Object> generateRSAVerificationMethod(PublicKey publicKey, String issuerURI, String issuerPublicKeyURI) throws Exception {
RSAKey rsaKey = new RSAKey.Builder((RSAPublicKey) publicKey).build();
Map<String,Object> publicKeyJwk = rsaKey.toJSONObject();

Map<String, Object> verificationMethod = new HashMap<>();
verificationMethod.put("id", issuerPublicKeyURI);
verificationMethod.put("type", "JsonWebKey2020");
verificationMethod.put("@context", "https://w3id.org/security/suites/jws-2020/v1");
verificationMethod.put("controller", issuerURI);
verificationMethod.put("publicKeyJwk", publicKeyJwk);
return verificationMethod;
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.mosip.certify;

import io.mosip.certify.core.constants.ErrorConstants;
import io.mosip.certify.core.dto.CredentialRequest;
import io.mosip.certify.core.dto.CredentialResponse;
import io.mosip.certify.core.exception.InvalidRequestException;
import io.mosip.certify.core.spi.VCIssuanceService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

Expand All @@ -24,4 +26,9 @@ public <T> CredentialResponse<T> getCredential(CredentialRequest credentialReque
public Map<String, Object> getCredentialIssuerMetadata(String version) {
return Map.of();
}

@Override
public Map<String, Object> getDIDDocument() {
throw new InvalidRequestException(ErrorConstants.UNSUPPORTED_IN_CURRENT_PLUGIN_MODE);
}
}
Loading

0 comments on commit 3d9cea6

Please sign in to comment.