您可以从源环境(通常为线下的密钥管理设施KMI,或者线下的硬件安全模块HSM)将非对称密钥导入专属KMS实例。本文为你介绍如何在源环境生成非对称密钥材料,随后上传到专属KMS实例以便导入非对称密钥材料。

背景信息

您可以调用DescribeKey接口判断非对称密钥材料来源。当OriginEXTERNAL时,说明非对称密钥材料由外部导入,称为外部密钥

当您创建外部密钥,使用您自己导入的非对称密钥材料时,需要注意以下几点:

  • 请确保您使用了符合要求的随机源生成非对称密钥材料。
  • 由于非对称密钥材料将导入到您的密码机集群中,且暂不支持调用DeleteKeyMaterial接口删除非对称密钥材料。因此您需要调用ScheduleKeyDeletion接口,等待7天到30天后,密码机集群中的非对称密钥材料随着CMK一起被删除。
  • 每个CMK只能拥有一个非对称密钥材料。一个非对称密钥材料导入CMK后,CMK将与该非对称密钥材料绑定,后续将无法导入其他非对称密钥材料。

导入非对称密钥材料时需使用密钥如下表所示。

密钥描述提供者标记说明
TAK(Target Asymmetric Key)待导入的目标非对称密钥。源环境的系统或者工具(如线下的密钥管理设施或者HSM)。
  • TAKpub:公钥部分。
  • TAKpriv:私钥部分。
IWK(Import Wrapping Key)用于导入TAK的加密公钥。阿里云专属KMS实例。
  • IWKpub:公钥部分。
  • IWKpriv:私钥部分。
ESK(Ephemeral Symmetric Key)一个瞬时存在的对称密钥,用于直接加密TAKpriv。源环境的系统或者工具,在完成对TAK的导出操作后立即销毁。ESK:对称算法的瞬时密钥。

创建外部密钥

导入非对称密钥材料前,您需要先在专属KMS中创建一个密钥材料来源为外部的非对称密钥。

  1. 登录密钥管理服务控制台
  2. 在页面左上角的地域下拉列表,选择专属KMS所在的地域。
    支持专属KMS的地域,请参见支持的地域
  3. 在左侧导航栏,单击专属KMS
  4. 单击目标专属KMS实例操作列的管理
  5. 用户主密钥区域,单击创建密钥
  6. 创建密钥对话框,选择密钥类型
    支持的非对称密钥类型为:RSA_2048、RSA_3072、RSA_4096、EC_P256、EC_P256K和EC_SM2。
  7. 设置别名描述
  8. 单击高级选项,选择密钥材料来源外部
  9. 勾选我了解使用外部密钥材料的方法和意义,然后单击确定

创建外部密钥成功后,您可以根据密钥类型选择导入SM2、RSA或ECC密钥材料。

导入SM2密钥材料

当您创建了密钥类型为EC_SM2的外部密钥后,可通过以下方式导入SM2密钥材料。

  1. 从专属KMS获取导入非对称密钥材料的参数。
    导入非对称密钥材料的参数包括一个用于加密非对称密钥材料的公钥(IWKpub),以及一个导入令牌。
    1. 用户主密钥区域,单击目标密钥ID,进入密钥管理页面。
    2. 密钥材料区域,单击获取导入参数
    3. 获取导入密钥材料的参数对话框,选择公钥类型EC_SM2加密算法SM2PKE_SM4_ECB,然后单击下一步
    4. 下载加密公钥和导入令牌,然后单击关闭
  2. 在源环境处理待导入的非对称密钥材料。
    步骤概览:
    1. 生成一个SM4类型的瞬时密钥ESK。
    2. 使用步骤1中获取的加密公钥(IWKpub)加密ESK,得到瞬时对称密钥密文Cipher(ESK),加密算法需要采用SM2公钥加密算法。
    3. 使用ESK加密导出TAKpriv的椭圆曲线参数D(32字节),获取目标非对称密钥的私钥密文Cipher(TAKpriv)。其中加密模式为ECB,填充模式为NoPadding。
    4. 组装结果数据,获取加密密钥材料,组装格式为Cipher(ESK) || Cipher(TAKpriv)。

    假设您在源环境中使用GMSSL产生待导入的非对称密钥材料,本示例对上述步骤概览进行详细讲解。

    1. 创建一个SM2类型的目标非对称密钥私钥,获取私钥的椭圆曲线的参数D。
      #生成SM2私钥。
      gmssl ecparam -name sm2p256v1 -genkey -outform DER -out TakSm2Priv.bin
      #查看私钥的D值。
      gmssl asn1parse -inform DER -in TakSm2Priv.bin
      
      #执行结果示例。
      0:d=0  hl=2 l=   8 prim: OBJECT            :sm2p256v1
         10:d=0  hl=2 l= 119 cons: SEQUENCE
         12:d=1  hl=2 l=   1 prim: INTEGER           :01
         15:d=1  hl=2 l=  32 prim: OCTET STRING    [HEXDUMP]:CB55E5ECC20BF7E9249ADEDE990EF141D14252E024734EB058A6B9F103F12A04
         49:d=1  hl=2 l=  10 cons: cont [ 0 ]
         51:d=2  hl=2 l=   8 prim: OBJECT            :sm2p256v1
         61:d=1  hl=2 l=  68 cons: cont [ 1 ]
         63:d=2  hl=2 l=  66 prim: BIT STRING
      
      #将D值写入文件。
      echo 'CB55E5ECC20BF7E9249ADEDE990EF141D14252E024734EB058A6B9F103F12A04' | xxd -r -p > TakSm2D.bin
    2. 创建一个SM4类型的瞬时对称密钥ESK。
      gmssl rand -out EskSm4.bin 16
    3. 使用步骤1中获取的加密公钥加密ESK,得到瞬时对称密钥密文Cipher(ESK),加密算法需要采用SM2公钥加密。
      gmssl sm2utl -encrypt -in EskSm4.bin -pubin -inkey PublicKey.pem -out CipherEsk.bin
    4. 使用ESK加密私钥的椭圆曲线参数D ,获取目标非对称密钥密文的私钥Cipher(TAKpriv)。其中加密模式为ECB,填充模式为NoPadding。
      xxd -l 16  -c 16 -ps EskSm4.bin | xargs -I {} openssl enc -sms4-ecb -e  -K {} -in 
      TakSm2D.bin -nosalt -nopad -out CipherTakPriv.bin
    5. 组装结果数据,获取加密密钥材料,组装格式为Cipher(ESK) || Cipher(TAKpriv)。
      cat CipherEsk.bin CipherTakPriv.bin > EncryptedKeyMaterial.bin
  3. 导入非对称密钥材料。

    导入密钥材料参数包括一个用于加密密钥材料的公钥,以及一个导入令牌。

    1. 用户主密钥区域,单击目标密钥ID,进入密钥管理页面。
    2. 对称密钥材料区域,单击导入密钥材料
    3. 导入打包后的密钥材料对话框,上传打包后的密钥材料导入令牌
      • 打包后的密钥材料:上传步骤2生成的对称密钥材料文本文件。
      • 导入令牌:上传步骤1获取的导入令牌文本文件。
    4. 单击确定
      导入对称密钥材料成功后,密钥状态从待导入更新为启用中

导入RSA或ECC密钥材料

当您创建了密钥类型为RSA_2048、RSA_3072、RSA_4096、EC_P256或者EC_P256K的外部密钥后,可以导入RSA或ECC密钥材料,以下以RSA_2048为例为您进行介绍。

  1. 从专属KMS获取导入非对称密钥材料的参数。
    导入非对称密钥材料的参数包括一个用于加密非对称密钥材料的公钥(IWKpub),以及一个导入令牌。
    1. 用户主密钥区域,单击目标密钥ID,进入密钥管理页面。
    2. 密钥材料区域,单击获取导入参数
    3. 获取导入密钥材料的参数对话框,选择公钥类型RSA_2048加密算法RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD,然后单击下一步
    4. 下载加密公钥和导入令牌,然后单击关闭
  2. 在源环境处理待导入的非对称密钥材料。
    步骤概览:
    1. 在源环境中,生成一个AES_256类型的瞬时对称密钥ESK。
    2. 使用步骤1中获取的加密公钥(IWKpub)加密导出ESK,得到瞬时对称密钥密文Cipher(ESK)。加密时采用RSAES_OAEP标准加密,其中MGF1和哈希算法为SHA256。
    3. 使用ESK加密导出TAKpriv,获取目标非对称密钥的私钥密文Cipher(TAKpriv)。加密时采用ECB模式进行加密,填充模式采用PKCS#7 Padding。
      说明 TAKpriv格式需要遵循:RSA私钥根据RFC3447进行编码,ECC私钥根据RFC5915进行编码。随后根据RFC5208包装为PKCS#8格式。
    4. 组装结果数据,获取加密密钥材料,组装格式为Cipher(ESK) || Cipher(TAKpriv)。

    假设您在源环境中使用OPENSSL产生待导入的非对称密钥材料,本示例对上述步骤概览进行详细讲解。

    1. 创建一个AES_256类型的瞬时对称密钥ESK。
      openssl rand -out EskAes256.bin 32
    2. 创建一个RSA_2048类型的目标非对称密钥私钥,并且将私钥转为PKCS#8格式。
      openssl genrsa -out TakPrivPkcs1.pem 2048
      openssl pkcs8 -topk8 -inform PEM -in TakPrivPkcs1.pem -outform der -nocrypt -out TakPrivPkcs8.bin
    3. 使用加密公钥加密ESK,获取瞬时对称密钥密文Cipher(ESK)。加密时采用RSAES OAEP标准加密,其中MGF1和哈希算法为SHA256。
      openssl pkeyutl -encrypt -pubin -inkey PublicKey.pem  -in EskAes256.bin  -pkeyopt 
      rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256 -out 
      CipherEsk.bin
    4. 使用ESK加密PKCS#8格式RSA_2048私钥TAKpriv,获取目标非对称密钥的私钥密文Cipher(TAKpriv),加密模式为ECB,填充模式为PKCS#7 Padding。
      xxd -l 32  -c 32 -ps EskAes256.bin | xargs -I {} openssl enc  -aes-256-ecb -e  -K {} -in 
      TakPrivPkcs8.bin -nosalt -out CipherTakPriv.bin
    5. 组装结果数据,得到加密密钥材料,组装格式为Cipher(ESK) || Cipher(TAKpriv)。
      cat CipherEsk.bin CipherTakPriv.bin > EncryptedKeyMaterial.bin
      openssl enc -e -base64 -A -in EncryptedKeyMaterial.bin -out EncryptedKeyMaterial_base64.txt
  3. 导入非对称密钥材料。

    导入非对称密钥材料时,可以导入从未导入过密钥材料的外部密钥。导入令牌与加密密钥材料的公钥具有绑定关系,一个令牌只能为其生成时指定的主密钥导入密钥材料。导入令牌的有效期为24小时,在有效期内可以重复使用,失效以后需要获取新的导入令牌和加密公钥。

    1. 用户主密钥区域,单击目标密钥ID,进入密钥管理页面。
    2. 对称密钥材料区域,单击导入密钥材料
    3. 导入打包后的密钥材料对话框,上传打包后的密钥材料导入令牌
      • 打包后的密钥材料:上传步骤2生成的对称密钥材料文本文件。
      • 导入令牌:上传步骤1获取的导入令牌文本文件。
    4. 单击确定
      导入对称密钥材料成功后,密钥状态从待导入更新为启用中

代码示例

通过KMS Java SDK在专属KMS中创建并导入SM2、ECC和RSA密钥的代码示例如下:

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource.PSpecified;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import com.aliyuncs.AcsRequest;
import com.aliyuncs.AcsResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.kms.model.v20160120.CreateKeyRequest;
import com.aliyuncs.kms.model.v20160120.CreateKeyResponse;
import com.aliyuncs.kms.model.v20160120.GetParametersForImportRequest;
import com.aliyuncs.kms.model.v20160120.GetParametersForImportResponse;
import com.aliyuncs.kms.model.v20160120.ImportKeyMaterialRequest;
import com.aliyuncs.kms.model.v20160120.ImportKeyMaterialResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class BringYourOwnAsymmetricKeySample {
    static String regionId = "cn-hangzhou";
    static String accessKeyId = "*** Provide your AccessKeyId ***";
    static String accessKeySecret = "*** Provide your AccessKeySecret ***";
    static String dedicatedKmsInstanceId = "*** Provide your DedicatedKmsInstanceId ***";
    DefaultAcsClient kmsClient;
    private final String SM2PKE_SM4_ECB = "SM2PKE_SM4_ECB";
    private final String RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD = "RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD";
    private static Provider BC = new BouncyCastleProvider();
    private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
    private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
    static {
        java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }

    public static void main(String[] args) {
        //初始化KMS SDK。
        DefaultAcsClient client = getClientForPublicEndpoint(regionId, accessKeyId, accessKeySecret);
        BringYourOwnAsymmetricKeySample sample = new BringYourOwnAsymmetricKeySample(client);

        //创建并导入EC_SM2类型的外部密钥。
        sample.doByok("EC_SM2", "EC_SM2", sample.SM2PKE_SM4_ECB, "SM4");
        //创建并导入EC_P256类型的外部密钥。
        sample.doByok("EC_P256", "RSA_2048", sample.RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD, "AES_256");
        //创建并导入RSA类型的外部密钥。
        sample.doByok("RSA_2048", "RSA_2048", sample.RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD, "AES_256");
    }


    public static DefaultAcsClient getClientForPublicEndpoint(String regionId, String accessKeyId, String accessKeySecret) {
        /**
         * Construct an Aliyun Client:
         * Set RegionId, AccessKeyId and AccessKeySecret
         */
        IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    public BringYourOwnAsymmetricKeySample(DefaultAcsClient kmsClient) {
        this.kmsClient = kmsClient;
    }

    public void doByok(String targetKeySpec, String wrappingKeySpec, String wrappingAlgorithm, String ephemeralKeySpec) {
        try {
            //创建ECC外部密钥。
            CreateKeyResponse.KeyMetadata keyMetadata = this.createExternalKeyInDkms(dedicatedKmsInstanceId, targetKeySpec, "SIGN/VERIFY");
            String keyId = keyMetadata.getKeyId();
            //获取导入密钥材料。
            GetParametersForImportResponse parametersForImportResponse = this.getParametersForImport(keyId, wrappingKeySpec,
                wrappingAlgorithm);
            String importToken = parametersForImportResponse.getImportToken();
            String publicKeyBase64 = parametersForImportResponse.getPublicKey();
            //生成瞬时对称密钥。
            byte[] ephemeralSymmetricKeyPlaintext = this.generateEphemeralSymmetricKey(ephemeralKeySpec);
            //生成目标非对称密钥。
            byte[] targetAsymmetricKeyPlaintext = this.generateTargetAsymmetricKey(targetKeySpec);
            //使用加密公钥加密瞬时对称密钥。
            byte[] ephemeralSymmetricKeyCipher = this.encryptEphemeralSymmetricKey(publicKeyBase64,
                wrappingAlgorithm, ephemeralSymmetricKeyPlaintext);
            //使用瞬时对称密钥加密目标非对称密钥。
            byte[] targetAsymmetricKeyCipher = this.encryptTargetAsymmetricKey(ephemeralSymmetricKeyPlaintext, targetAsymmetricKeyPlaintext,
                wrappingAlgorithm);
            //生成密钥材料。
            byte[] encryptedKeyMaterial = new byte[ephemeralSymmetricKeyCipher.length + targetAsymmetricKeyCipher.length];
            System.arraycopy(ephemeralSymmetricKeyCipher, 0, encryptedKeyMaterial, 0, ephemeralSymmetricKeyCipher.length);
            System.arraycopy(targetAsymmetricKeyCipher, 0, encryptedKeyMaterial, ephemeralSymmetricKeyCipher.length, targetAsymmetricKeyCipher.length);
            String encryptedKeyMaterialBase64 =  DatatypeConverter.printBase64Binary(encryptedKeyMaterial);
            //导入密钥材料到专属KMS。
            this.importKeyMaterial(keyId, encryptedKeyMaterialBase64, importToken, 0L);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private GetParametersForImportResponse getParametersForImport(String keyId, String keySpec, String algorithm) throws Exception {
        GetParametersForImportRequest request = new GetParametersForImportRequest();
        request.setAcceptFormat(FormatType.JSON);
        request.setMethod(MethodType.POST);
        request.setProtocol(ProtocolType.HTTPS);
        request.setKeyId(keyId);
        request.setWrappingKeySpec(keySpec);
        request.setWrappingAlgorithm(algorithm);
        GetParametersForImportResponse resp;
        try {
            resp = this.getAcsResponseWithRetry(request);
        } catch (Exception e) {
            throw e;
        }
        return resp;
    }

    private CreateKeyResponse.KeyMetadata createExternalKeyInDkms(String dedicatedKmsInstance, String keySpec, String keyUsage) throws Exception {
        CreateKeyRequest request = new CreateKeyRequest();
        request.setOrigin("EXTERNAL"); //创建外部密钥。
        request.setKeyStoreId(dedicatedKmsInstance);
        request.setKeySpec(keySpec);
        request.setKeyUsage(keyUsage);

        request.setProtocol(ProtocolType.HTTPS);
        request.setAcceptFormat(FormatType.JSON);
        request.setMethod(MethodType.POST);
        CreateKeyResponse.KeyMetadata ret = null;
        String requestId = null;
        try {
            CreateKeyResponse response = getAcsResponseWithRetry(request);
            ret = response.getKeyMetadata();
            requestId = response.getRequestId();
        } catch (Exception e) {
            throw e;
        }
        return Pair.of(ret, requestId).getKey();
    }

    private <T extends AcsResponse> T getAcsResponseWithRetry(AcsRequest<T> request) throws ServerException,
        ClientException {
        String expStr = "Retry Max Times";
        for (int i = 0; i < 3; i++) {
            try {
                T resp = this.kmsClient.getAcsResponse(request);
                if (resp == null) {
                    throw new ClientException("Get a null response");
                }
                return resp;
            } catch (ServerException e) {//这里会生成NUllPointer。
                throw e;
            } catch (ClientException e) {
                expStr = e.toString();
                if (expStr.contains("SDK.ServerUnreachable")) {//need retry
                    continue;
                }
                throw e;
            }
        }
        throw new ClientException(expStr);
    }

    private byte[] generateEphemeralSymmetricKey(String ephemeralSymmetricKeySpec) throws Exception {
        int ephemeralSymmetricKeyLength = 32; //瞬时对称密钥是AES_256时,长度为32比特。
        if ("SM4".equals(ephemeralSymmetricKeySpec)) {
            ephemeralSymmetricKeyLength = 16;
        }
        byte[] key = new byte[32];
        new Random().nextBytes(key);

        return key;
    }

    private byte[] generateTargetAsymmetricKey(String keySpec) throws Exception {
        PrivateKey privateKey = null;
        //生成SM2密钥,并获取私钥的D值。
        if ("EC_SM2".equals(keySpec)) {
            ECPrivateKey ecPrivateKey = (ECPrivateKey)generateSm2KeyPair().getPrivate();
            byte[] dT = ecPrivateKey.getS().toByteArray();
            byte[] d = new  byte[32];
            if (dT.length == 33) {
                System.arraycopy(dT, 1, d, 0, 32);
            }
            return dT.length == 32 ? dT : d;
        }

        //生成RSA或者ECC私钥。
        if (keySpec.contains("RSA")) {
            String[] keySpecAttrs = keySpec.split("_");
            int bits = Integer.parseInt(keySpecAttrs[keySpecAttrs.length - 1]);
            privateKey = generateRsaKeyPair(bits).getPrivate();
        } else if  (keySpec.contains("EC")) {
            if (keySpec.contains("P256K")) {
                //生成EC_P256K私钥。
                privateKey  = generateEccKeyPair("secp256k1").getPrivate();
            } else {
                //生成EC_P256私钥。
                privateKey=   generateEccKeyPair("secp256r1").getPrivate();
            }
        }
        if (privateKey != null) {
            //返回PKCS#8格式的私钥。
            return  privateKey.getEncoded();
        }
        return null;
    }

    private  KeyPair generateEccKeyPair(String keySpec)
        throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        ECGenParameterSpec ecSpec = new ECGenParameterSpec(keySpec);
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
        keyPairGenerator.initialize(ecSpec, new SecureRandom());
        return keyPairGenerator.generateKeyPair();
    }
    private  KeyPair generateRsaKeyPair(int length) throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
        keyGen.initialize(length);
        return keyGen.genKeyPair();
    }

    private KeyPair generateSm2KeyPair() throws Exception {
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "BC");
        keyGen.initialize(new ECGenParameterSpec("sm2p256v1"), new SecureRandom());
        return keyGen.genKeyPair();
    }


    private byte[] encryptEphemeralSymmetricKey (String publicKeyBase64, String wrappingAlgorithm, byte[] ephemeralSymmetricKeyPlaintext) throws Exception {
        PublicKey publickey = null;
        byte[] enchbk = null;
        if ("RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD".equals(wrappingAlgorithm)) {
            publickey = parseDerPublicKey("RSA", publicKeyBase64);
            Cipher oaepFromAlgo = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSpecified.DEFAULT);
            oaepFromAlgo.init(Cipher.ENCRYPT_MODE, publickey, oaepParams);
            enchbk = oaepFromAlgo.doFinal(ephemeralSymmetricKeyPlaintext);
        } else if ("SM2PKE_SM4_ECB".equals(wrappingAlgorithm)) {
            publickey = parseDerPublicKey("EC", publicKeyBase64, BC);
            BCECPublicKey localECPublicKey = (BCECPublicKey) publickey;
            ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), ecDomainParameters);
            SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
            sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters));
            enchbk = sm2Engine.processBlock(ephemeralSymmetricKeyPlaintext, 0, ephemeralSymmetricKeyPlaintext.length);

        } else {
            throw new Exception("Invalid wrappingAlgorithm");
        }
        return enchbk;
    }

    private PublicKey parseDerPublicKey(String keyType, String pemKey) throws Exception {
        byte[] derKey = DatatypeConverter.parseBase64Binary(pemKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
        return KeyFactory.getInstance(keyType).generatePublic(keySpec);
    }
    private PublicKey parseDerPublicKey(String keyType, String pemKey, Provider provider) throws Exception {
        byte[] derKey = DatatypeConverter.parseBase64Binary(pemKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
        return KeyFactory.getInstance(keyType, provider).generatePublic(keySpec);
    }

    private byte[] encryptTargetAsymmetricKey (byte[] secretKey, byte[] targetAsymmetricKeyPlaintext, String wrappingAlgorithm)
        throws Exception {
        if ("RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD".equals(wrappingAlgorithm)) {
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            return cipher.doFinal(targetAsymmetricKeyPlaintext);
        } else if ("SM2PKE_SM4_ECB".equals(wrappingAlgorithm)) {
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "SM4");
            Cipher cipher = Cipher.getInstance("SM4/ECB/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            return cipher.doFinal(targetAsymmetricKeyPlaintext);
        }

        throw new Exception("Invalid WrappingAlgorithm");
    }

    private boolean importKeyMaterial(
        String keyId,
        String material,
        String token,
        Long expire
    ) throws Exception {
        ImportKeyMaterialRequest req = newImportKeyMaterialRequest(
            keyId, material, token, expire);
        try {
            ImportKeyMaterialResponse resp = this.getAcsResponseWithRetry(req);
        } catch (Exception e) {
            throw e;
        }
        return true;
    }

    private ImportKeyMaterialRequest newImportKeyMaterialRequest(
        String keyId,
        String material,
        String token,
        Long expire
    ) {
        ImportKeyMaterialRequest request = new ImportKeyMaterialRequest();
        request.setAcceptFormat(FormatType.JSON);
        request.setMethod(MethodType.POST);
        request.setProtocol(ProtocolType.HTTPS);
        request.setEncryptedKeyMaterial(material);
        request.setImportToken(token);
        request.setKeyId(keyId);
        request.setKeyMaterialExpireUnix(expire);
        return request;
    }
}