# Sample Source Code

예제로 사용된 전체 소스코드입니다.

package com.iconloop.myid.common;

import com.google.gson.JsonObject;
import foundation.icon.did.Credential;
import foundation.icon.did.DidService;
import foundation.icon.did.Presentation;
import foundation.icon.did.core.Algorithm;
import foundation.icon.did.core.AlgorithmProvider;
import foundation.icon.did.core.DidKeyHolder;
import foundation.icon.did.core.Keystore;
import foundation.icon.did.document.Document;
import foundation.icon.did.document.EncodeType;
import foundation.icon.did.document.PublicKeyProperty;
import foundation.icon.did.exceptions.AlgorithmException;
import foundation.icon.did.exceptions.TransactionException;
import foundation.icon.did.jwe.ECDHKey;
import foundation.icon.did.jwe.EphemeralPublicKey;
import foundation.icon.did.jwt.Jwt;
import foundation.icon.did.protocol.*;
import foundation.icon.did.protocol.jsonld.*;
import foundation.icon.icx.IconService;
import foundation.icon.icx.KeyWallet;
import foundation.icon.icx.data.Address;
import foundation.icon.icx.data.Bytes;
import foundation.icon.icx.data.TransactionResult;
import foundation.icon.icx.transport.http.HttpProvider;
import foundation.icon.myid.BaseService;
import foundation.icon.myid.CredentialService;
import foundation.icon.myid.IssuerService;
import foundation.icon.myid.VerifierService;
import foundation.icon.myid.credential.CredentialInfo;
import foundation.icon.myid.credential.CredentialInfoScoreParameter;
import foundation.icon.myid.credential.RevokeCredentialInfo;
import foundation.icon.myid.credential.RevokeCredentialInfoScoreParameter;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.*;

/**
 * SDK Guide 문서에 사용될 소스코드
 */
public class IssuerVerifierCommonExample {

    // DID와 암호화된 private key를 가지고 있는 Class
    private DidKeyHolder mDidKeyHolder;
    // 지갑
    private KeyWallet mWallet;
    // did score service
    private DidService mDidService;
    // vc score service
    private CredentialService mVcService;
    // VCP message에서 공통으로 쓰이는 nonce값
    private String mNonce;
    // ECDHKey of Issuer
    private ECDHKey mServerECDHKey;

    // MyID Issuer 서비스.
    private IssuerService mIssuerService;
    // MyID Verifier 서비스.
    private VerifierService mVerifierService;
    // MyID WAS URL. 테스트베드는 "https://iv-test.zzeung.id" 이고, 라이브는 파라메타 담당자에게 문의 하십시오.
    private static final String IV_WAS_URL = "https://iv-test.zzeung.id";

    // Credential을 요청하는 Holder의 DID
    private String mHolderDid;
    // Credential을 요청하는 Holder의 ECDH key
    private ECDHKey mHolderECDHKey;


    public enum NodeConnectType {
        MYID_WAS, SCORE
    }

    private NodeConnectType NODE_CONNECT_TYPE = NodeConnectType.MYID_WAS;

    // Initialize
    public void initForNode() {
        // nodeUrl, networkId, didScoreAddress, vcScoreAddress는 Blockchain 네트워크에 따라 변경됨.
        String nodeUrl = "https://blockchain-test.zzeung.id/api/v3";
        String networkId = "13057";
        BigInteger nid = new BigInteger(networkId);
        String didScoreAddress = "cx1d4b59ddf32c8ecc41bb17129f98093dd632e692";
        String vcScoreAddress = "cxecb23969e2538cdae37a7d0c27902c077d9a2131";
        IconService iconService = new IconService(new HttpProvider(nodeUrl));
        mDidService = new DidService(
                iconService, nid, new Address(didScoreAddress));
        mVcService = new CredentialService(
                iconService, nid, new Address(vcScoreAddress));

        try {
            mDidKeyHolder = Keystore.loadDidKeyHolder("P@assw0rd", new File("/did.json"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        Algorithm algorithm = AlgorithmProvider.create(AlgorithmProvider.Type.ES256K);
        mWallet = KeyWallet.load(
                new Bytes(algorithm.privateKeyToByte(mDidKeyHolder.getPrivateKey()))
        );

        try {
            mServerECDHKey = ECDHKey.generateKey(ECDHKey.CURVE_P256K);
        } catch (Exception e) {
            e.printStackTrace();
        }
        mNonce = EncodeType.HEX.encode(AlgorithmProvider.secureRandom().generateSeed(16));
    }

    // Initialize
    public void initForMyIdServer() {
        try {
            mDidKeyHolder = Keystore.loadDidKeyHolder("P@assw0rd", new File("/did.json"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        mVerifierService = VerifierService.create(IV_WAS_URL);
        mIssuerService = IssuerService.create(IV_WAS_URL);

        try {
            mServerECDHKey = ECDHKey.generateKey(ECDHKey.CURVE_P256K);
        } catch (Exception e) {
            e.printStackTrace();
        }
        mNonce = EncodeType.HEX.encode(AlgorithmProvider.secureRandom().generateSeed(16));
    }


    /**
     * Credential을 발급하기 위해 DID_INIT message를 생성
     */
    public String createDidInitMessage() {
        ProtocolMessage protocolMessage = null;

        try {
            EphemeralPublicKey publicKey = new EphemeralPublicKey.Builder()
                    .kid(mNonce)
                    .epk(mServerECDHKey)
                    .build();
            ClaimRequest request = new ClaimRequest.DidBuilder(ClaimRequest.Type.INIT)
                    .nonce(mNonce)
                    .didKeyHolder(mDidKeyHolder)
                    .publicKey(publicKey)
                    .version("2.0")
                    .build();

            protocolMessage = new ProtocolMessage.RequestBuilder()
                    .type(ProtocolType.DID_INIT)
                    .claimRequest(request)
                    .build();
        } catch (Exception exception) {
            exception.printStackTrace();
        }

        ProtocolMessage.SignResult didInitSignResult = protocolMessage.signEncrypt(mDidKeyHolder);
        if (!didInitSignResult.isSuccess()) {
            System.out.println("DidInit create sign fail.");
        }
        JsonObject didInitObject = didInitSignResult.getResult();
        return didInitObject.toString();
    }


    public void verifyDidAuthMessage(String DID_AUTH_JWE) {
        ProtocolMessage didAuthResPm = ProtocolMessage.valueOf(DID_AUTH_JWE);
        String keyId = didAuthResPm.getJweKid();

        if (didAuthResPm.isProtected()) {
            didAuthResPm.decryptJwe(mServerECDHKey);        // JWE -> JWT 복호화
        }

        boolean verifyResult = false;
        try {
            verifyResult = verifyDidAuth(didAuthResPm, mServerECDHKey, keyId, mNonce);
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (verifyResult) {
            mHolderDid = didAuthResPm.getClaimResponseJwt().getDid();
        } else {
            // Fail to verify DID_AUTH_JWE
        }
    }

    private boolean verifyDidAuth(ProtocolMessage didAuthPm, ECDHKey issuerECDHKey, String issuerKeyId, String nonce) throws Exception {
        ClaimResponse didAuthResponse = didAuthPm.getClaimResponseJwt();
        mHolderECDHKey = didAuthPm.getJwe().getJweHeader().getEpk();

        EphemeralPublicKey holderPublicKey = new EphemeralPublicKey.Builder()
                .kid(issuerKeyId)
                .epk(mHolderECDHKey)
                .build();

        String holderDid = didAuthResponse.getDid();
        String holderKeyId = didAuthResponse.getKeyId();
        Document holderDoc = null;
        try {
            if (NODE_CONNECT_TYPE == NodeConnectType.MYID_WAS) {
                // MyID WAS로 부터 Holder DID Document 조회.
                holderDoc = mIssuerService.getDid(holderDid);
            } else {
                // Blockchain에서 Holder DID Document 조회
                holderDoc = mDidService.readDocument(holderDid);

                PublicKeyProperty ppp = holderDoc.getPublicKeyProperty("xxx");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (holderDoc == null) {
            System.out.println("DidAuth holderDoc is null");
            return false;
        } else if (holderDoc != null) {
            // nonce 확인
            String holderNonce = didAuthResponse.getJwt().getPayload().getNonce();
            if (!nonce.equals(holderNonce)) {
                System.out.println("Not equals nonce value");
                return false;
            }

            PublicKeyProperty publicKeyProperty =
                    holderDoc.getPublicKeyProperty(holderKeyId);
            if (publicKeyProperty == null || publicKeyProperty.isRevoked()) {
                System.out.println("DidAuth's holderDid revoked");
                return false;
            } else {
                PublicKey publicKey = publicKeyProperty.getPublicKey();
                Jwt.VerifyResult verifyResult = didAuthResponse.verify(publicKey);
                if (!verifyResult.isSuccess()) {
                    System.out.println(verifyResult.getFailMessage());  // verify fail
                    return false;
                }
            }
        }
        System.out.println("Verifying DidAuth succeeded.");
        return true;
    }

    public String createCredential() {
        // credential에  담을 claim value 예시
        Map<String, Claim> claims = new LinkedHashMap<>();
        claims.put("name", new Claim("홍길순"));
        claims.put("gender", new Claim("female", "여성"));
        claims.put("phoneNumber", new Claim("01031142962", "010-3114-2962"));
        claims.put("telco", new Claim("SKT"));
        claims.put("birthDate", new Claim("2000-01-01"));
        claims.put("connectingInformation", new Claim("0000000000000000000000000000000000000000"));
        List<String> layoutList = Arrays.asList("name", "gender", "phoneNumber", "telco", "birthDate");

        DisplayLayout displayLayout = new DisplayLayout.StrBuilder().displayLayout(layoutList).build();

        JsonLdParam credentialParam = new JsonLdParam.Builder()
                .context(Arrays.asList("https://vc.zzeung.kr/credentials/v1.json",
                        "http://vc.zzeung.kr/credentials/mobile_authentication/kor/v1.json"))
                .type(Arrays.asList("CredentialParam", "MobileAuthenticationKorCredential"))
                .proofType("hash")
                .hashAlgorithm("SHA-256")
                .claim(claims)
                .displayLayout(displayLayout)
                .build();

        String nonce = EncodeType.HEX.encode(AlgorithmProvider.secureRandom().generateSeed(16));

        Credential credential = new Credential.Builder()
                .didKeyHolder(mDidKeyHolder)     // Issuer DID
                .nonce(nonce)
                .targetDid(mHolderDid)   // Holder DID
                .vcParam(credentialParam)
                .id("https://myid.id/credential/example/phone/vc/0000001")  // Credential의 ID (아무값이나 입력)
                .refreshId("https://vc.example.com/refresh_service/")   // 재발급 URL (optional)
                .refreshType("ManualRefreshService")                   // 재발급 가능함을 명시(optional)
                .version("2.0")
                .build();

        Date issued = new Date();
        // 365일짜리 유효기간을 만듬.
        long duration = credential.getDuration() * 365 * 1000L;
        Date expiration = new Date(issued.getTime() + duration);

        EphemeralPublicKey holderPublicKey = new EphemeralPublicKey.Builder()
                .kid(mNonce)
                .epk(mHolderECDHKey)
                .build();

        ProtocolMessage credentialPm = null;
        try {
            credentialPm = new ProtocolMessage.CredentialBuilder()
                    .type(ProtocolType.RESPONSE_CREDENTIAL)
                    .credential(credential)
                    .requestPublicKey(holderPublicKey)     // received key from holder
                    .issued(issued)
                    .expiration(expiration)
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
        }

        ProtocolMessage.SignResult credentialSignResult =
                credentialPm.signEncrypt(mDidKeyHolder, mServerECDHKey);
        JsonObject credentialObj = credentialSignResult.getResult();

        if (!credentialSignResult.isSuccess()) {
            System.out.println("Signing Credential failed.");
            // mDidKeyHolder의 key store파일이 문제가 없는지 확인 필요.
            return null;
        }

        // 발급할 VC를 Blockchain에 등록
        boolean isRegisterVc = false;
        if (NODE_CONNECT_TYPE == NodeConnectType.MYID_WAS) {
            isRegisterVc = registerCredentialViaMyIDServer(credentialPm);
        } else {
            isRegisterVc = registerCredentialViaScore(credentialPm);
        }
        if(!isRegisterVc) {
            // VC를 블록체인에 등록 실패함.
            return null;
        }

        // TODO 기존에 이 사용자가 발급 받고, blockchain에 저장한 VC가 있다면 여기서 폐기 (Optional)
        String oldVcSignature = ""; // DB 에서 가지고 와야 함.
        if (NODE_CONNECT_TYPE == NodeConnectType.MYID_WAS) {
            revokeCredentialInfoViaMyIDServer(oldVcSignature);
        } else {
            revokeCredentialInfoViaScore(oldVcSignature);
        }

        // TODO 등록한 VC를 나중에 위와같은 코드로 폐기 하기위한 ID인 VC ID (VC Signature)를 DB저장 해야함.
        // TODO 아래 vcSignature를 DB에 저장하고 추 후 폐기가 필요할 때 사용해야 함.
        String vcSignature = credentialPm.getJwt().getSignature();

        return credentialObj.toString();
    }

    public String createRequestPresentationForPhoneId() {
        EphemeralPublicKey verifierPublicKey = new EphemeralPublicKey.Builder()
                .kid(mNonce)
                .epk(mServerECDHKey)
                .build();

        // 제출 요청할 항목
        List<String> requireProperty = Arrays.asList("name", "gender", "telco", "phoneNumber", "connectionInformation", "birthDate");
        // 조건 작성
        VprCondition condition = new VprCondition.SimpleBuilder()   // SimpleCondition
                .conditionId("uuid-requisite-0000-1111-2222")       // 아무값이나 입력
                .context(Arrays.asList(
                        "https://vc.zzeung.kr/credentials/mobile_authentication/kor/v1.json"))
                .credentialType("MobileAuthenticationKorCredential")
                .property(requireProperty)
                .build();

        JsonLdVpr vpr = null;
        try {
            vpr = new JsonLdVpr.Builder()
                    .context(Arrays.asList("https://vc.zzeung.kr/credentials/v1.json"))
                    .id("https://www.ubplus.com/vpr/mobile")  // VPR ID로 아무 값이나 입력.
                    .purpose("휴대폰 인증서로 본인 확인")              // VC 요청의 목적을 입력.
                    .verifier(mDidKeyHolder.getDid())
                    .condition(condition)
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
        }

        ClaimRequest reqPresentation = new ClaimRequest.PresentationBuilder()
                .didKeyHolder(mDidKeyHolder)
                .requestDate(new Date())
                .nonce(mNonce)
                .publicKey(verifierPublicKey)
                .version("2.0")
                .vpr(vpr)
                .build();

        ProtocolMessage reqPresentationPm = null;
        try {
            reqPresentationPm = new ProtocolMessage.RequestBuilder()
                    .type(ProtocolType.REQUEST_PRESENTATION)
                    .claimRequest(reqPresentation)
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        ProtocolMessage.SignResult reqPresentationSignResult =
                reqPresentationPm.signEncrypt(mDidKeyHolder);

        if (!reqPresentationSignResult.isSuccess()) {
            System.out.println("Signing RequestPresentation failed.");
            // mDidKeyHolder의 key store파일이 문제가 없는지 확인 필요.
            return null;
        }

        JsonObject reqPresentationObject = reqPresentationSignResult.getResult();
        System.out.println("VPR : " + reqPresentationObject.toString());

        return reqPresentationObject.toString();
    }


    public boolean verifyPresentationForPhoneId(String PRESENTATION_JWE) {
        ProtocolMessage presentationPm = ProtocolMessage.valueOf(PRESENTATION_JWE);
        String keyId = presentationPm.getJweKid();
        if (presentationPm.isProtected()) {
            presentationPm.decryptJwe(mServerECDHKey);
        }
        System.out.println("decryptJWT :" + presentationPm.getJwtToken());
        Presentation presentation = presentationPm.getPresentationJwt();

        String holderDid = presentation.getDid();
        String holderKeyId = presentation.getKeyId();
        Document holderDoc = null;


        try {
            if (NODE_CONNECT_TYPE == NodeConnectType.MYID_WAS) {
                // MyID WAS로 부터 Holder DID Document 조회.
                holderDoc = mVerifierService.getDid(holderDid);
            } else {
                // Blockchain에서 Holder DID Document 조회
                holderDoc = mDidService.readDocument(holderDid);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (holderDoc == null) {
            System.out.println("Presentation holderDoc is null");
            // blockchain에서 Holder의 DID 확인 실패
            return false;
        }

        PublicKeyProperty publicKeyProperty =
                holderDoc.getPublicKeyProperty(holderKeyId);
        if (publicKeyProperty == null || publicKeyProperty.isRevoked()) {
            System.out.println("Presentation holderDid revoked");
            // Holder의 public key가 없거나 폐기되어서 서명 확인 불가.
            return false;
        }
        PublicKey publicKey = publicKeyProperty.getPublicKey();
        presentation.getJwt();

        try {
            Jwt.VerifyResult verifyResult = presentation.getJwt().verify(publicKey);
            if (!verifyResult.isSuccess()) {
                System.out.println(verifyResult.getFailMessage());  // verify fail
                // Holder의 서명 확인 실패.
                // 즉 이 presentation은 위조 되었거나 holder의 서명에 문제가 있음.
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // presentation내의 VC 검증 시작
        for (VpCriteria criteria : presentation.getVp().getFulfilledCriteria()) {
            Credential credential = Credential.valueOf(criteria.getVc());
            // presentation을 제출한 주체가 VC의 발급받은 자와 동일한지만 확인
            if (!credential.getTargetDid().equals(holderDid)) {
                System.out.println("VC's targetDid does not match holderDid.");
                return false;
            }

            Document issuerDocument = null;
            String issuerDid = credential.getIssuerDid().getDid();
            try {
                if (NODE_CONNECT_TYPE == NodeConnectType.MYID_WAS) {
                    // MyID WAS로 부터 Holder DID Document 조회.
                    issuerDocument = mVerifierService.getDid(issuerDid);
                } else {
                    // Blockchain에서 Holder DID Document 조회
                    issuerDocument = mDidService.readDocument(issuerDid);
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }

            if (issuerDocument == null) {
                // blockchain에서 Holder의 DID 확인 실패
                return false;
            }
            PublicKeyProperty issuerKeyProperty =
                    issuerDocument.getPublicKeyProperty(credential.getKeyId());
            if (issuerKeyProperty.isRevoked()) {
                // Issuer의 key가 폐기 되었음.
                return false;
            }

            try {
                PublicKey issuerPublicKey = issuerKeyProperty.getPublicKey();
                Jwt.VerifyResult credVerifyResult = credential.getJwt().verify(issuerPublicKey);
                if (!credVerifyResult.isSuccess()) {
                    // Issuer key로 VC 서명 검증 실패.
                    return false;
                }
                if (!holderDid.equals(credential.getTargetDid())) {
                    // VC에 있는 Holder DID와 presentation을 제출한 Holder의 DID가 다름.
                    return false;
                }
            } catch (AlgorithmException e) {
                e.printStackTrace();
                return false;
            }

            // 크레덴셜의 신원정보 무결성 체크: VC 내의 claim hash 값과 전달받은 실제 값을 hash하여 비교
            if (!criteria.isVerifyParam()) {
                System.out.println("Claim parameter hash is invalid.");
                // credential 신원정보 무결성 체크 실패!
                // credential 내의 claim hash 값과 param으로 전달받은 실제 값의 hash를 비교 실패.
                // 즉 credential의 신원정보가 위변조 되었을 수 있음.
                return false;
            }

            // 휴대폰 본인인증의 claim 정보.
            Map<String, Claim> claimMap = criteria.getVcParam().getClaim();

            // 요청한 항목이 presentation에 포함 되어 있는지 확인
            if (!claimMap.containsKey("name")) {
                // name : "홍길동"
                System.out.println("Claim parameter does not have `name`");
            }
            if (!claimMap.containsKey("birthDate")) {
                // birthDate (xsd:date): "1994-05-22"
                System.out.println("Claim parameter does not have `birthDate`");
            }
        }
        return true;
    }


    public boolean registerCredentialViaMyIDServer(ProtocolMessage credentialPm) {
        Credential credential = credentialPm.getCredentialJwt();
        BaseService.ServiceResult result = null;
        try {
            result = mIssuerService.registerVC(credential, mDidKeyHolder);
        } catch (AlgorithmException e) {
            e.printStackTrace();
            return false;
        }
        if (!result.isSuccess()) {
            // register fail
            return false;
        }
        return true;
    }

    public boolean registerCredentialViaScore(ProtocolMessage credentialPm) {
        Credential credential = credentialPm.getCredentialJwt();
        Date issueDate = new Date();
        Date expiryDate = credential.getJwt().getPayload().getExp();


        CredentialInfo credentialInfo = new CredentialInfo.Builder()
                .type(foundation.icon.myid.core.PropertyName.CREDENTIAL_INFO_TYPE_REGIST)
                .issuerDid(credential.getDid())
                .sig(credentialPm.getJwt().getSignature())
                .issueDate(issueDate)
                .expiryDate(expiryDate)
                .build();

        Jwt addJwt = CredentialInfoScoreParameter.CredentialInfoParam(
                mDidKeyHolder, credentialInfo);
        String signedJwt = null;
        try {
            signedJwt = mDidKeyHolder.sign(addJwt);
            TransactionResult result = mVcService.register(mWallet, signedJwt);
            if (result.getStatus().intValue() == 0) {
                System.out.println("Registering VcScore failed.");
                // blockchain에 VC 등록 실패.
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    public void getCredentialInfoViaScore() {
        String signedJwt = "eyJ..";     // Credential JWT token
        Credential credential = Credential.valueOf(signedJwt);
        String issuerDid = credential.getDid();
        String sig = Jwt.decode(signedJwt).getSignature();
        CredentialInfo credentialInfo = null;
        try {
            credentialInfo = mVcService.get(sig);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // vc를 검증.
        if (credentialInfo.getIsRevoke()) {
            System.out.println("vc is revoked.");
        }
        Date expiryDate = new Date(credentialInfo.getExpiryDate() * 1000L);
        if (expiryDate.before(new Date())) {
            System.out.println("vc is expired");
        }
    }

    public void getCredentialInfoViaMyIDServer() {
        String signedJwt = "eyJ..";      // Credential JWT token
        Credential credential = Credential.valueOf(signedJwt);
        String issuerDid = credential.getDid();
        String sig = Jwt.decode(signedJwt).getSignature();

        CredentialInfo credentialInfo = mIssuerService.getVC(issuerDid, sig);
        if (credentialInfo == null) {
            System.out.println("vc is null.");
        }
        // vc를 검증.
        if (credentialInfo.getIsRevoke()) {
            System.out.println("vc is revoked.");
        }
        Date expiryDate = new Date(credentialInfo.getExpiryDate() * 1000L);
        if (expiryDate.before(new Date())) {
            System.out.println("vc is expired");
        }
    }

    public void revokeCredentialInfoViaScore(String vcSignature) {
        RevokeCredentialInfo revokedCrendentialInfo = new RevokeCredentialInfo.Builder()
                .type("REVOKE")
                .issuerDid(mDidKeyHolder.getDid())                // issuerDid
                .sig(vcSignature)                                 // 폐기할 VC의 signature
                .revokeDate(new Date())                           // 폐기하는 현재시간
                .build();

        try {
            Jwt revokeJwt = RevokeCredentialInfoScoreParameter
                    .RevokeCredentialInfoParam(mDidKeyHolder, revokedCrendentialInfo);
            String signedRevokeJwt = mDidKeyHolder.sign(revokeJwt);
            TransactionResult result =
                    mVcService.revoke(mWallet, signedRevokeJwt);
        } catch (TransactionException e) {
            String errorCodeString = e.getCode();
            if ("158".equals(errorCodeString)) {
                // 이미 폐기된 VC_ID
            } else if ("144".equals(errorCodeString)) {
                // 폐기할 VC_ID가 blockchain에 없음.
            } else if ("136".equals(errorCodeString)) {
                // 이슈어 private key로 jwt 서명 실패. key store 파일이 문제 없는지 확인 필요.
            } else {
                // 그 외 에러.
            }
        } catch (IOException e) {
            // Failed to revoke public key
            // 네트워크 에러
            return;
        } catch (AlgorithmException e) {
            // Failed to sign
            // key store 파일이 문제가 없는지 확인 필요.
            return;
        }
    }

    public void revokeCredentialInfoViaMyIDServer(String vcSignature) {
        BaseService.ServiceResult result = null;
        try {
            result = mIssuerService.revokeVC(vcSignature, mDidKeyHolder.getDid(), mDidKeyHolder);
        } catch (AlgorithmException e) {
            // Failed to revoke VC
            e.printStackTrace();
        }
        if (!result.isSuccess()) {
            // Failed to revoke VC
        }
    }


    /**
     * Verify Revocation Request and returns siganture to revoke.
     *
     * @param requestRevocationJwt
     * @return
     */
    public String receiveRequestRevocation(String requestRevocationJwt) {
        ProtocolMessage protocolMessage = ProtocolMessage.valueOf(requestRevocationJwt);
        ClaimRequest claimRequest = protocolMessage.getClaimRequestJwt();

        // holder DID Document를 조회.
        String holderDid = claimRequest.getDid();
        String holderKeyId = claimRequest.getKeyId();

        Document holderDoc = null;
        try {
            if (NODE_CONNECT_TYPE == NodeConnectType.MYID_WAS) {
                // MyID WAS로 부터 Holder DID Document 조회.
                holderDoc = mVerifierService.getDid(holderDid);
            } else {
                // Blockchain에서 Holder DID Document 조회
                holderDoc = mDidService.readDocument(holderDid);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (holderDoc == null) {
            System.out.println("Presentation holderDoc is null");
            // blockchain에서 Holder의 DID 확인 실패
            return null;
        }

        PublicKeyProperty publicKeyProperty =
                holderDoc.getPublicKeyProperty(holderKeyId);
        if (publicKeyProperty == null || publicKeyProperty.isRevoked()) {
            System.out.println("Presentation holderDid revoked");
            // Holder의 public key가 없거나 폐기되어서 서명 확인 불가.
            return null;
        }
        PublicKey holderPublicKey = publicKeyProperty.getPublicKey();

        try {
            Jwt.VerifyResult verifyResult = claimRequest.getJwt().verify(holderPublicKey);
            System.out.println("verifyResult:" + verifyResult);
            if (!verifyResult.isSuccess()) {
                System.out.println(verifyResult.getFailMessage());
                return null;
            }
        } catch (AlgorithmException e) {
            System.out.println("Failed to verify request_revocation jwt. msg:" + e.getMessage());
            return null;
        }

        // sig 추출
        String revocationSig = claimRequest.getSig();
        System.out.println("Signature to revoke:" + revocationSig);
        return revocationSig;
    }

    /**
     * Return Revocation Result
     * @param requestRevocationJwt
     * @param success
     * @return
     */
    public String responseRevocation(String requestRevocationJwt, boolean success) {
        ResponseResult responseResult = null;
        if (success) {
            responseResult = new ResponseResult.Builder()
                    .result(true)
                    .build();
        } else {
            responseResult = new ResponseResult.Builder()
                    .result(false)
                    .errorCode("100")
                    .errorMessage("Fail to verify signature.")
                    .build();
        }

        ProtocolMessage pm = ProtocolMessage.valueOf(requestRevocationJwt);
        ClaimRequest claimRequest = pm.getClaimRequestJwt();
        String holderDid = claimRequest.getDid();

        ClaimResponse claimResponse = new ClaimResponse.Builder(ClaimResponse.Type.REVOCATION)
                .didKeyHolder(mDidKeyHolder)
                .responseId(holderDid)        // issuer's did
                .version("2.0")
                .responseResult(responseResult)
                .build();

        ProtocolMessage protocolMessage = new ProtocolMessage.ResponseBuilder()
                .type(ProtocolType.RESPONSE_REVOCATION)
                .claimResponse(claimResponse)
                .build();

        ProtocolMessage.SignResult signResult = protocolMessage.signEncrypt(mDidKeyHolder);

        if (!signResult.isSuccess()) {
            System.out.println("Signing RequestRevocation failed.");
            return null;
        }

        System.out.println("JWT(RESPONE_REVOCATION) : " + signResult.getResult().toString());
        System.out.println();
        return signResult.getResult().toString();
    }
}