全部產品
Search
文件中心

Key Management Service:匯入密鑰材料

更新時間:Jul 06, 2024

當您建立密鑰材料來源為外部的密鑰時,KMS不會為您建立的使用者主要金鑰(CMK)產生密鑰材料,此時您可以將自己的密鑰材料匯入到CMK中。本文為您介紹如何匯入外部金鑰材料。

背景資訊

使用者主要金鑰(CMK)是KMS的基本資源,由密鑰ID、基本中繼資料(如密鑰狀態等)以及用於加密、解密資料的密鑰材料組成。預設情況下,當您建立CMK時,會由KMS產生密鑰材料。您也可以選擇建立密鑰材料來源為外部的密鑰,此時,KMS將不會為您建立的CMK產生密鑰材料,您可以將自己的密鑰材料匯入到CMK中。

匯入密鑰材料時,您可以匯入從未被匯入過的密鑰材料,也可以重新匯入已經到期和已被刪除的密鑰材料,或者重設密鑰材料的到期時間。

您可以調用DescribeKey介面判斷密鑰材料來源。

  • 當KeyMetadata中OriginAliyun_KMS時,說明密鑰材料由KMS產生,該密鑰稱為普通密鑰

  • OriginEXTERNAL時,說明密鑰材料由外部匯入,該密鑰稱為外部金鑰

當您使用由外部匯入的密鑰材料時,需要注意以下幾點:

  • 請確保您使用了符合要求的隨機源產生密鑰材料。

  • 請確保密鑰材料的可靠性。

    • KMS可以確保匯入密鑰材料高可用,但是不能確保匯入密鑰材料與KMS產生的密鑰材料具有相同的可靠性。

    • 匯入的密鑰材料被刪除後,可以再次匯入相同的密鑰材料使得CMK再次可用,因此您需要自行儲存密鑰材料的副本。

  • 匯入密鑰材料後,您可以直接調用DeleteKeyMaterial介面刪除密鑰材料,也可以設定到期時間,在密鑰材料到期後進行刪除(CMK不會被刪除)。

  • 每個CMK只能擁有一個匯入密鑰材料。當您將一個密鑰材料匯入CMK時,CMK將與密鑰材料綁定,即便密鑰材料已經到期或者被刪除,也不能匯入其他密鑰材料。如果您需要輪換使用外部金鑰材料的CMK,只能建立一個新的CMK然後匯入新的密鑰材料。

  • CMK具有獨立性。例如:您使用CMK加密的資料,無法使用其他CMK進行解密,即便這些CMK都使用相同的密鑰材料。

  • 只能匯入256位(AES)的對稱金鑰作為密鑰材料。

通過控制台匯入密鑰材料

  1. 建立外部金鑰。

    1. 登入Key Management Service控制台,在頁面左上方的地區下拉式清單,選擇密鑰所在的地區。

    2. 在左側導覽列,單擊使用者主要金鑰

    3. 單擊建立密鑰,在建立密鑰對話方塊完成各項的配置。

      配置項

      說明

      KMS執行個體

      選擇預設,不支援修改。

      密鑰類型

      選擇對稱金鑰類型Aliyun_AES_256。關於對稱金鑰類型的更多資訊,請參見對稱金鑰的類型

      密鑰用途

      選擇密鑰的用途。取值:

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

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

      別名

      使用者主要金鑰的標識符。支援英文字母、數字、底線(_)、短劃線(-)和正斜線(/)。

      更多資訊,請參見別名概述

      保護層級

      密鑰的保護層級。取值:

      • Software:通過軟體模組對密鑰進行保護。

      • Hsm:將密鑰託管在密碼機中,使密鑰獲得高安全等級的專用硬體的保護。

      描述

      密鑰的說明資訊。

      輪轉周期

      設定對稱金鑰自動輪轉的時間周期。

      說明

      僅密鑰類型為Aliyun_AES_256的對稱金鑰支援設定輪轉周期。

      密鑰材料來源

      選擇外部

    4. 仔細閱讀參考文檔後勾選我瞭解使用外部金鑰材料的方法和意義,然後單擊確定

  2. 擷取匯入密鑰材料參數。

    匯入密鑰材料參數包括一個用於加密金鑰材料的公開金鑰,以及一個匯入令牌。

    1. 在左側導覽列,單擊使用者主要金鑰

    2. 單擊目標密鑰ID進入密鑰管理頁面,在密鑰材料地區,單擊擷取匯入參數

    3. 擷取匯入密鑰材料的參數對話方塊,選擇公開金鑰類型密碼編譯演算法,並單擊下一步

      本文以公開金鑰類型RSA_2048密碼編譯演算法RSAES_OAEP_SHA_1為例進行介紹。

      說明

      公開金鑰類型取值為RSA_2048時,您可以選擇密碼編譯演算法RSAES_PKCS1_V1_5(預設值)、RSAES_OAEP_SHA_1或RSAES_OAEP_SHA_256。

    4. 下載公開金鑰和匯入令牌,然後單擊關閉

      下載公開金鑰時需要選擇公開金鑰格式

      • 公開金鑰格式選擇der格式:下載的公開金鑰檔案尾碼為.bin(例如:publickey_f240b730-7e3e-4bd7-877f-4fe22524****.bin)。

      • 公開金鑰格式選擇pem格式:下載的公開金鑰檔案尾碼為.pem(例如:publickey_f240b730-7e3e-4bd7-877f-4fe22524****.pem)。

  3. 加密金鑰材料。

    下載公開金鑰和匯入令牌後,您可以使用該公開金鑰加密您的密鑰材料。

    以使用OpenSSL加密金鑰材料為例:使用的密碼編譯演算法需要與擷取匯入對稱金鑰材料參數時指定的一致(RSAES_OAEP_SHA_1)。

    1. 建立一個密鑰材料,使用OpenSSL產生一個32位元組的隨機數。

      openssl rand -out KeyMaterial.bin 32
    2. 根據指定的密碼編譯演算法(RSAES_OAEP_SHA_1)加密金鑰材料。

      執行下方範例程式碼時,您需要執行以下操作:

      • 將範例程式碼中的PublicKey.bin替換為擷取匯入密鑰材料參數中下載的公開金鑰檔案的名稱。

      • 範例程式碼以公開金鑰格式為der格式為例,如果您下載公開金鑰檔案時選擇了pem格式,需要將範例程式碼中的-keyform DER替換為-keyform PEM

      openssl rsautl -encrypt -in KeyMaterial.bin -oaep -inkey PublicKey.bin  -keyform DER  -pubin -out EncryptedKeyMaterial.bin
    3. 將加密後的密鑰材料進行Base64編碼,儲存為文字檔。

      openssl enc -e -base64 -A -in EncryptedKeyMaterial.bin -out EncryptedKeyMaterial_base64.txt
  4. 匯入密鑰材料。

    匯入令牌與加密金鑰材料的公開金鑰具有綁定關係,一個令牌只能為其產生時指定的主要金鑰匯入密鑰材料。匯入令牌的有效期間為24小時,在有效期間內可以重複使用,失效以後需要擷取新的匯入令牌和公開金鑰。

    1. 在左側導覽列,單擊使用者主要金鑰

    2. 單擊目標密鑰ID進入密鑰管理頁面,密鑰材料地區,單擊匯入密鑰材料

    3. 匯入打包後的密鑰材料對話方塊,上傳打包後的密鑰材料匯入令牌

    4. 設定密鑰材料到期時間,單擊確定

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

通過ALIYUN CLI匯入密鑰材料

  1. 建立外部金鑰。

    通過命令aliyun kms CreateKey調用CreateKey介面,指定參數OriginEXTERNAL

    aliyun kms CreateKey --Origin EXTERNAL --Description "External key"
  2. 擷取匯入密鑰材料參數。

    通過命令aliyun kms GetParametersForImport調用GetParametersForImport介面擷取匯入密鑰材料參數。

    aliyun kms GetParametersForImport --KeyId 1339cb7d-54d3-47e0-b595-c7d3dba8**** --WrappingAlgorithm RSAES_OAEP_SHA_1 --WrappingKeySpec RSA_2048
  3. 匯入密鑰材料。

    1. 使用公開金鑰對密鑰材料進行加密。

      公開金鑰是一個2048位元的RSA公開金鑰,使用的密碼編譯演算法需要與擷取匯入密鑰材料參數時指定的一致。由於API返回的公開金鑰經過Base64編碼,因此在使用時需要先進行Base64解碼。目前KMS支援的密碼編譯演算法有RSAES_OAEP_SHA_1、RSAES_OAEP_SHA_256和RSAES_PKCS1_V1_5。

    2. 將加密後的密鑰材料進行Base64編碼。

    3. 通過命令aliyun kms ImportKeyMaterial調用ImportKeyMaterial介面,將編碼後的密鑰材料與匯入令牌一起,作為ImportKeyMaterial介面的參數匯入KMS。

      aliyun kms ImportKeyMaterial --KeyId 1339cb7d-54d3-47e0-b595-c7d3dba8**** --EncryptedKeyMaterial xxx --ImportToken xxxx --KeyMaterialExpireUnix xxxx

通過SDK匯入密鑰材料

程式碼範例:

  • JAVA SDK

    說明

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

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

    //使用最新KMS JAVA SDK。
    //KmsClient.java
    
    import com.aliyuncs.kms.model.v20160120.*;
    import com.aliyuncs.profile.DefaultProfile;
    
    //KMS API封裝。
    public class KmsClient {
            DefaultAcsClient client;
    
            public KmsClient( String region_id, String ak, String secret) {
                    DefaultProfile profile = DefaultProfile.getProfile(region_id, ak, secret);
                    this.client = new DefaultAcsClient(profile);
            }
    
            public CreateKeyResponse createKey() throws Exception {
                    CreateKeyRequest request = new CreateKeyRequest();
                    request.setOrigin("EXTERNAL"); //建立外部金鑰。
                    return this.client.getAcsResponse(request);
            }
            //... 省略,其餘API類似。
    }
    //example.java
    import com.aliyuncs.kms.model.v20160120.*;
    import KmsClient;
    import java.security.KeyFactory;
    import java.security.PublicKey;
    import java.security.spec.MGF1ParameterSpec;
    import javax.crypto.Cipher;
    import javax.crypto.spec.OAEPParameterSpec;
    import javax.crypto.spec.PSource.PSpecified;
    import java.security.spec.X509EncodedKeySpec;
    import java.util.Random;
    import javax.xml.bind.DatatypeConverter;
    
    public class CreateAndImportExample {
            public static void main(String[] args) {
                    String regionId = "cn-hangzhou";
            String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
            String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
            KmsClient kmsclient = new KmsClient(regionId,accessKeyId,accessKeySecret);
            //建立外部金鑰。
            try {
                    CreateKeyResponse keyResponse = kmsclient.createKey();
                    String keyId = keyResponse.KeyMetadata.getKeyId();
                    //產生一個32位元組隨機數。
                    byte[] keyMaterial = new byte[32];
                    new Random().nextBytes(keyMaterial);
                    //擷取匯入密鑰材料參數。
                    GetParametersForImportResponse paramResponse = kmsclient.getParametersForImport(keyId,"RSAES_OAEP_SHA_256");
                    String importToekn = paramResponse.getImportToken();
                    String encryptPublicKey = paramResponse.getPublicKey();
                    //Base64解碼公開金鑰。
                    byte[] publicKeyDer = DatatypeConverter.parseBase64Binary(encryptPublicKey);
                    //解析成RSA的公開金鑰。
                    KeyFactory keyFact = KeyFactory.getInstance("RSA");
                    X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyDer);
                    PublicKey publicKey = keyFact.generatePublic(spec);
                    //加密金鑰材料。
                    Cipher oaepFromAlgo = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
                    String hashFunc = "SHA-256";
                    OAEPParameterSpec oaepParams = new OAEPParameterSpec(hashFunc, "MGF1", new MGF1ParameterSpec(hashFunc), PSpecified.DEFAULT);
                    oaepFromAlgo.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
                    byte[] cipherDer = oaepFromAlgo.doFinal(keyMaterial);
                    //加密後的密鑰材料,需要進行Base64編碼。
                    String encryptedKeyMaterial = DatatypeConverter.printBase64Binary(cipherDer);
                    //匯入密鑰材料。
                    Long expireTimestamp = 1546272000L; //Unix時間戳記,精確到秒,0表示永不到期。
                            kmsclient.importKeyMaterial(keyId,encryptedKeyMaterial, expireTimestamp);
            } catch(Exception e) {
                    //... 省略。
            }
            }
    }
  • Go SDK

    說明

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

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

    package main
    
    import (
        "crypto/rand"
        "crypto/rsa"
        "crypto/sha256"
        "crypto/x509"
        "encoding/base64"
        "fmt"
        "log"
        random "math/rand"
        "time"
    
        "github.com/aliyun/alibaba-cloud-sdk-go/services/kms"
    )
    
    //KMS CreateKey API封裝。
    func kmsCreateKey(client *kms.Client) (string, error) {
        request := kms.CreateCreateKeyRequest()
        request.Scheme = "https"
        request.Origin = "EXTERNAL" //建立外部金鑰。
        response, err := client.CreateKey(request)
        if err != nil {
            return "", fmt.Errorf("CreateKey error:%v", err)
        }
        return response.KeyMetadata.KeyId, nil
    }
    
    //KMS GetParametersForImport API封裝。
    func kmsGetParametersForImport(client *kms.Client, keyId, wrappingKeySpec, wrappingAlgorithm string) (string, string, error) {
        request := kms.CreateGetParametersForImportRequest()
        request.Scheme = "https"
        request.KeyId = keyId
        request.WrappingKeySpec = wrappingKeySpec
        request.WrappingAlgorithm = wrappingAlgorithm
        response, err := client.GetParametersForImport(request)
        if err != nil {
            return "", "", fmt.Errorf("GetParametersForImport error:%v", err)
        }
        return response.PublicKey, response.ImportToken, nil
    }
    
    //KMS ImportKeyMaterial API封裝。
    func kmsImportKeyMaterial(client *kms.Client, keyId, importToken, encryptedKeyMaterial string) error {
        request := kms.CreateImportKeyMaterialRequest()
        request.Scheme = "https"
        request.KeyId = keyId
        request.ImportToken = importToken
        request.EncryptedKeyMaterial = encryptedKeyMaterial
        _, err := client.ImportKeyMaterial(request)
        if err != nil {
            return fmt.Errorf("ImportKeyMaterial error:%v", err)
        }
        return nil
    }
    
    func randBytes(n int) []byte {
        var r = random.New(random.NewSource(time.Now().UnixNano()))
        bytes := make([]byte, n)
        for i := range bytes {
            bytes[i] = byte(r.Intn(256))
        }
        return bytes
    }
    
    func main() {
        accessKeyId := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
        accessKeySecret := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
        regionId := "cn-hangzhou"
        client, err := kms.NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret)
        if err != nil {
            log.Fatalf("NewClientWithAccessKey error:%+v\n", err)
        }
        //建立一個外部金鑰。
        keyId, err := kmsCreateKey(client)
        if err != nil {
            log.Fatalf("kmsCreateKey error:%+v\n", err)
        }
        //以下範例程式碼產生一個32位元組隨機數。在實際應用中您需要通過使用者的密鑰管理系統產生密鑰,並使用匯入密鑰材料參數中的公開金鑰進行加密。
        keyMaterial := randBytes(32)
        //擷取匯入密鑰材料參數。
        encryptPublicKey, importToken, err := kmsGetParametersForImport(client, keyId, "RSA_2048", "RSAES_OAEP_SHA_256")
        if err != nil {
            log.Fatalf("kmsGetParametersForImport error:%v\n", err)
        }
        //Base64解碼公開金鑰。
        publicKeyDer, err := base64.StdEncoding.DecodeString(encryptPublicKey)
        if err != nil {
            log.Fatalf("base64.StdEncoding.DecodeString error:%v\n", err)
        }
        //解析成RSA的公開金鑰。
        publicKey, err := x509.ParsePKIXPublicKey(publicKeyDer)
        if err != nil {
            log.Fatalf("x509.ParsePKIXPublicKey error:%v\n", err)
        }
        //加密金鑰材料。
        cipherDer, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey.(*rsa.PublicKey), keyMaterial, nil)
        if err != nil {
            log.Fatalf("rsa.EncryptOAEP error:%v\n", err)
        }
        //加密後的密鑰材料,需要進行Base64編碼。
        encryptedKeyMaterial := base64.StdEncoding.EncodeToString(cipherDer)
        //匯入密鑰材料。
        err = kmsImportKeyMaterial(client, keyId, importToken, encryptedKeyMaterial)
        if err != nil {
            log.Fatalf("ImportKeyMaterial error:%v", err)
        }
    }