全部產品
Search
文件中心

Key Management Service:匯入非對稱金鑰材料

更新時間:Dec 30, 2024

當建立密鑰材料來源為外部的非對稱金鑰時,KMS不會產生密鑰材料,您需要匯入自己的密鑰材料。本文介紹如何為非對稱金鑰匯入密鑰材料。

重要

如果您的軟體密鑰管理執行個體、硬體密鑰管理執行個體不支援匯入密鑰材料,或者匯入密鑰材料時返回失敗,請聯絡阿里雲支援人員升級執行個體。

功能介紹

密鑰是KMS的基本資源,由密鑰ID、基本中繼資料(如密鑰狀態等)以及密鑰材料組成。建立密鑰時,您可以選擇由KMS產生密鑰材料,也可以選擇外部來源的密鑰材料。如果選擇了外部來源的密鑰材料,您需要將外部金鑰材料匯入到密鑰中,該功能通常被稱為內建密鑰(BYOK)。

不同KMS密鑰管理類型對匯入密鑰材料的支援情況,請參見下表。關於密鑰管理類型的更多資訊,請參見密鑰管理類型和密鑰規格

  • 對:表示支援匯入相應的密鑰材料。

  • 錯:表示不支援匯入相應的密鑰材料。

密鑰管理類型

匯入對稱金鑰材料

匯入非對稱金鑰材料

預設密鑰

  • 主要金鑰:√

  • 服務密鑰:×

  • 主要金鑰:×

  • 服務密鑰:×

軟體密鑰

硬體密鑰

注意事項

  • 為密鑰首次匯入密鑰材料後,密鑰即和該密鑰材料綁定,不再支援匯入其他密鑰材料。

  • 您可以根據需要將相同的密鑰材料多次匯入到KMS密鑰中,但不能將不同的密鑰材料匯入到一個KMS密鑰。

  • 如果密鑰的密鑰材料到期或被刪除,您可以為密鑰再次匯入相同的密鑰材料,使得該密鑰再次可用,匯入密鑰材料後不支援匯出,因此請您妥善保管密鑰材料。

前提條件

已購買和啟用KMS執行個體。具體操作,請參見購買和啟用KMS執行個體

通過控制台匯入密鑰材料

步驟一:建立非對稱金鑰

匯入密鑰材料前,請建立密鑰材料來源為外部的非對稱金鑰。

軟體密鑰

  1. 登入Key Management Service控制台,在頂部功能表列選擇地區後,在左側導覽列單擊资源 > 密钥管理

  2. 用户主密钥頁簽,实例ID選擇軟體密鑰管理執行個體,單擊创建密钥

  3. 创建密钥面板,完成配置項設定,然後單擊確定

    配置項

    說明

    密钥类型

    選擇非對稱金鑰。

    密钥规格

    非對稱金鑰規格:RSA_2048、RSA_3072、EC_P256、EC_P256K

    密钥用途

    密鑰的用途。取值:

    • Encrypt/Decrypt:資料加密和解密。

    • Sign/Verify:產生和驗證數位簽章。

    密钥别名

    密鑰的別名標識符。支援英文字母、數字、底線(_)、短劃線(-)和正斜線(/)。

    標籤

    密鑰的標籤,方便您對密鑰進行分類管理。每個標籤由一個索引值對(Key:Value)組成,包含標籤鍵(Key)、標籤值(Value)。

    說明
    • 標籤鍵和標籤值的格式:最多支援128個字元,可以包含英文大小寫字母、數字、正斜線(/)、反斜線(\)、底線(_)、短劃線(-)、半形句號(.)、加號(+)、等號(=)、半形冒號(:)、字元at(@)。

    • 標籤鍵不能以aliyun或acs:開頭。

    • 每個密鑰最多可以設定20個標籤索引值對。

    描述信息

    密鑰的說明資訊。

    高级选项

    • 策略配置:詳細資料,請參見密鑰策略概述

    • 密钥材料来源:選擇外部(导入密钥材料)

      說明

      請仔細閱讀並選中我瞭解使用外部金鑰材料的方法和意義

硬體密鑰

  1. 登入Key Management Service控制台,在頂部功能表列選擇地區後,在左側導覽列單擊资源 > 密钥管理

  2. 用户主密钥頁簽,实例ID選擇硬體密鑰管理執行個體,單擊创建密钥

  3. 创建密钥面板,完成配置項設定,然後單擊確定

    配置項

    說明

    密钥类型

    選擇非對稱金鑰。

    密钥规格

    非對稱金鑰規格:RSA_2048、RSA_3072、RSA_4096、EC_P256、EC_P256K、

    密钥用途

    密鑰的用途。取值:

    • Encrypt/Decrypt:資料加密和解密。

    • Sign/Verify:產生和驗證數位簽章。

    密钥别名

    密鑰的別名標識符。支援英文字母、數字、底線(_)、短劃線(-)和正斜線(/)。

    標籤

    密鑰的標籤,方便您對密鑰進行分類管理。每個標籤由一個索引值對(Key:Value)組成,包含標籤鍵(Key)、標籤值(Value)。

    說明
    • 標籤鍵和標籤值的格式:最多支援128個字元,可以包含英文大小寫字母、數字、正斜線(/)、反斜線(\)、底線(_)、短劃線(-)、半形句號(.)、加號(+)、等號(=)、半形冒號(:)、字元at(@)。

    • 標籤鍵不能以aliyun或acs:開頭。

    • 每個密鑰最多可以設定20個標籤索引值對。

    描述信息

    密鑰的說明資訊。

    高级选项

    • 策略配置:詳細資料,請參見密鑰策略概述

    • 密钥材料来源:選擇外部(导入密钥材料)

      說明

      請仔細閱讀並選中我瞭解使用外部金鑰材料的方法和意義

步驟二:下載封裝公開金鑰和匯入令牌

匯入密鑰材料的參數包含封裝公開金鑰和匯入令牌,封裝公開金鑰用於加密金鑰材料,在匯入處理程序中保護您的密鑰材料,匯入令牌用於匯入密鑰材料。

  1. 定位到目標密鑰,單擊操作列的详情,在密鑰詳情頁面的密钥材料地區,單擊获取导入参数

  2. 获取导入密钥材料的参数對話方塊,選擇公钥类型加密算法後,單擊下一步

    密鑰管理類型

    KMS密鑰的規格

    封裝公開金鑰類型

    密碼編譯演算法

    軟體密鑰

    • RSA_2048

    • RSA_3072

    • EC_P256

    • EC_P256K

    RSA_2048

    RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD

    硬體密鑰

    • RSA_2048

    • RSA_3072

    • RSA_4096

    • EC_P256

    • EC_P256K

    RSA_2048

    RSAES_OAEP_SHA_256_AES_256_ECB_PKCS7_PAD

  3. 下載封裝公開金鑰以及匯入令牌,並妥善儲存。

    • 公钥格式

      • der格式:下載後檔案名稱預設為publickey_******.bin。

      • pem格式:下載後檔案名稱預設為publickey_******.pem。

    • 导入令牌:下載後檔案名稱預設為token_******.txt。

      重要
      • 匯入令牌的有效期間為24小時,在有效期間內可以重複使用,失效後需要擷取新的匯入令牌和公開金鑰。

      • 封裝公開金鑰和匯入令牌必須配套使用。即不允許下載兩次封裝公開金鑰和匯入令牌,使用其中一個的封裝公開金鑰,另一個的匯入令牌。

步驟三:使用封裝公開金鑰加密密鑰材料

請在您的系統內容中產生並加密金鑰材料,過程中會使用到以下密鑰,具體說明請參見下表。

密鑰

用途

提供者

標記說明

目標非對稱金鑰TAK(Target Asymmetric Key)

待匯入的目標非對稱金鑰。

您的系統內容或者工具(如線下的密鑰管理設施KMI,或者線下的硬體安全模組HSM)。

  • TAKpub:公開金鑰部分

  • TAKpriv:私密金鑰部分

加密金鑰IWK(Import Wrapping Key)

用於匯入TAK的加密金鑰。

阿里雲KMS。

  • IWKpub:公開金鑰部分

    說明

    即您在Key Management Service控制台下載的封裝公開金鑰。

  • IWKpriv:私密金鑰部分

瞬時密鑰ESK(Ephemeral Symmetric Key)

一個瞬時存在的對稱金鑰,用於直接加密TAKpriv。

源環境的系統或者工具,在完成對TAK的匯出操作後請立即銷毀。

不涉及

  1. 建立一個目標非對稱金鑰私密金鑰(TAKpriv),密鑰規格與您建立非對稱金鑰時選擇的密鑰規格一致。如果您已有目標非對稱金鑰私密金鑰(TAKpriv),請跳過本步驟。

    說明

    TAKpriv格式需要遵循:RSA私密金鑰根據RFC3447進行編碼,ECC私密金鑰根據RFC5915進行編碼,然後根據RFC5208封裝為PKCS#8格式。

  2. 建立一個瞬時密鑰(ESK)。

  3. 使用加密金鑰公開金鑰(IWKpub)來加密瞬時密鑰(ESK),得到瞬時密鑰密文(Cipher(ESK))。

  4. 使用瞬時密鑰(ESK)加密目標非對稱金鑰私密金鑰(TAKpriv),得到目標非對稱金鑰的私密金鑰密文(Cipher(TAKpriv))。

  5. 按照Cipher(ESK)||Cipher(TAKpriv)格式組裝結果資料,得到加密後的密鑰材料。

樣本:使用OPENSSL產生RSA_2048演算法的密鑰材料

  1. 建立一個RSA_2048演算法的目標非對稱金鑰私密金鑰,並且將私密金鑰轉為PKCS#8格式。

    openssl genrsa -out TakPrivPkcs1.pem 2048
    openssl pkcs8 -topk8 -inform PEM -in TakPrivPkcs1.pem -outform der -nocrypt -out TakPrivPkcs8.bin
  2. 建立一個AES_256演算法的瞬時密鑰(ESK)。

    openssl rand -out EskAes256.bin 32
  3. 使用加密金鑰公開金鑰(IWKpub)加密瞬時密鑰(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
    說明

    請將PublicKey.pem替換為您在Key Management Service控制台下載的公開金鑰檔案的名稱。

  4. 使用瞬時密鑰(ESK)加密目標非對稱金鑰私密金鑰(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)格式組裝結果資料,再進行Base64編碼。

    cat CipherEsk.bin CipherTakPriv.bin > EncryptedKeyMaterial.bin
    openssl enc -e -base64 -A -in EncryptedKeyMaterial.bin -out EncryptedKeyMaterial_base64.txt
    說明

    EncryptedKeyMaterial_base64.txt即為可匯入KMS的密鑰材料檔案。

步驟四:匯入密鑰材料

在密鑰詳情頁面,單擊导入密钥材料,在导入打包后的密钥材料對話方塊,完成各項配置後,單擊確定

配置項

說明

打包后的密钥材料

上傳步驟三:使用封裝公開金鑰加密密鑰材料中產生的密鑰材料檔案。

导入令牌

上傳步驟二:下載封裝公開金鑰和匯入令牌中下載的令牌檔案。

密钥材料过期时间

支援選擇永不过期,也可以自訂到期時間。

重要

如果設定了密鑰材料到期時間,在設定的時間點之後KMS會刪除已到期的密鑰材料,您將無法使用該密鑰材料。如需恢複使用,可以為密鑰再次匯入相同的密鑰材料。

匯入密鑰材料成功後,密鑰狀態從待导入更新為启用中

通過SDK匯入密鑰材料

您可以通過阿里雲SDK在KMS中建立RSA和ECC演算法的密鑰,並匯入密鑰材料。Java程式碼範例如下。

說明

阿里雲帳號AccessKey擁有所有OpenAPI的存取權限,建議您使用RAM使用者進行API訪問或日常營運。強烈建議不要把AccessKey ID和AccessKey Secret儲存到工程代碼裡,否則可能導致AccessKey泄露,威脅您帳號下所有資源的安全。

本樣本以將AccessKey配置在環境變數ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET的方式來實現身分識別驗證為例。

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 = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
    static String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
    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) {
                throw e;
            } catch (ClientException e) {
                expStr = e.toString();
              //need retry
                if (expStr.contains("SDK.ServerUnreachable")) {
                    continue;
                }
                throw e;
            }
        }
        throw new ClientException(expStr);
    }

    private byte[] generateEphemeralSymmetricKey(String ephemeralSymmetricKeySpec) throws Exception {
       //瞬時對稱金鑰是AES_256時,長度為32位元。  
       int ephemeralSymmetricKeyLength = 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;
    }
}
            

常見問題

是否支援刪除密鑰材料?

支援刪除。

重要

匯入的密鑰材料到期或者被刪除後,其密鑰將無法使用,需要再次匯入相同的密鑰材料才可正常使用。

  • 直接刪除密鑰材料

    • 控制台:在密鑰詳情頁的密钥材料地區,單擊删除密钥材料

    • API介面:調用DeleteKeyMaterial介面刪除,該操作不會刪除您的密鑰。

  • 到期後由KMS刪除

    在匯入密鑰材料時設定到期時間,在時間點之後KMS會刪除已到期的密鑰材料。

如何重新匯入相同的密鑰材料?

密鑰材料到期或刪除後,您可以再次匯入相同的密鑰材料,密鑰才可繼續使用。

  1. 刪除到期的密鑰材料。

    在密鑰詳情頁,單擊密钥材料頁簽,單擊删除密钥材料

  2. 重新下載封裝公開金鑰和匯入令牌。具體操作,請參見步驟二:下載封裝公開金鑰和匯入令牌

    說明

    密鑰封裝過程不會影響密鑰材料的內容,因此,您可以使用不同的封裝公開金鑰和不同的封裝演算法來匯入相同的密鑰材料。

  3. 使用封裝公開金鑰加密密鑰材料。具體操作,請參見步驟三:使用封裝公開金鑰加密密鑰材料

    說明

    密鑰材料必須與之前到期的密鑰材料為同一個。

  4. 使用匯入令牌,匯入加密後的密鑰材料。具體操作,請參見步驟四:匯入密鑰材料

如何判斷密鑰材料是由外部匯入還是由KMS產生?

  • 方式一:在Key Management Service控制台查看。

    密钥管理頁面,單擊用户主密钥頁簽,選擇实例ID後定位到目標密鑰,單擊操作列的详情,在詳情頁面查看密鑰材料來源

  • 方式二:通過調用DescribeKey介面查看。

    如果Origin值為EXTERNAL,說明密鑰材料由外部匯入。如果Origin值為Aliyun_KMS,說明密鑰材料由KMS產生。

如何輪轉使用外部金鑰材料的密鑰?

對匯入外部金鑰材料的密鑰,KMS不提供定期自動輪轉功能。如果您需要輪轉,只能建立一個新的密鑰然後匯入新的密鑰材料。

相關文檔