當您創建密鑰材料來源為外部的密鑰時,KMS不會為您創建的用戶主密鑰(CMK)生成密鑰材料,此時您可以將自己的密鑰材料導入到CMK中。本文為您介紹如何導入外部密鑰材料。
背景信息
用戶主密鑰(CMK)是KMS的基本資源,由密鑰ID、基本元數據(如密鑰狀態等)以及用于加密、解密數據的密鑰材料組成。默認情況下,當您創建CMK時,會由KMS生成密鑰材料。您也可以選擇創建密鑰材料來源為外部的密鑰,此時,KMS將不會為您創建的CMK生成密鑰材料,您可以將自己的密鑰材料導入到CMK中。
導入密鑰材料時,您可以導入從未被導入過的密鑰材料,也可以重新導入已經過期和已被刪除的密鑰材料,或者重置密鑰材料的過期時間。
您可以調用DescribeKey接口判斷密鑰材料來源。
當KeyMetadata中Origin為Aliyun_KMS時,說明密鑰材料由KMS生成,該密鑰稱為普通密鑰。
當Origin為EXTERNAL時,說明密鑰材料由外部導入,該密鑰稱為外部密鑰。
當您使用由外部導入的密鑰材料時,需要注意以下幾點:
請確保您使用了符合要求的隨機源生成密鑰材料。
請確保密鑰材料的可靠性。
KMS可以確保導入密鑰材料高可用,但是不能確保導入密鑰材料與KMS生成的密鑰材料具有相同的可靠性。
導入的密鑰材料被刪除后,可以再次導入相同的密鑰材料使得CMK再次可用,因此您需要自行保存密鑰材料的副本。
導入密鑰材料后,您可以直接調用DeleteKeyMaterial接口刪除密鑰材料,也可以設置過期時間,在密鑰材料過期后進行刪除(CMK不會被刪除)。
每個CMK只能擁有一個導入密鑰材料。當您將一個密鑰材料導入CMK時,CMK將與密鑰材料綁定,即便密鑰材料已經過期或者被刪除,也不能導入其他密鑰材料。如果您需要輪換使用外部密鑰材料的CMK,只能創建一個新的CMK然后導入新的密鑰材料。
CMK具有獨立性。例如:您使用CMK加密的數據,無法使用其他CMK進行解密,即便這些CMK都使用相同的密鑰材料。
只能導入128位(SM4)或256位(AES)的對稱密鑰作為密鑰材料。128位(SM4)密鑰僅支持部分地域。更多信息,請參見支持的地域。
通過控制臺導入密鑰材料
創建外部密鑰。
登錄密鑰管理服務控制臺,在頁面左上角的地域下拉列表,選擇密鑰所在的地域。
在左側導航欄,單擊用戶主密鑰。
單擊創建密鑰,在創建密鑰對話框完成各項的配置。
配置項
說明
KMS實例
選擇默認,不支持修改。
密鑰類型
選擇對稱密鑰類型Aliyun_AES_256或Aliyun_SM4。關于對稱密鑰類型的更多信息,請參見對稱密鑰的類型。
密鑰用途
選擇密鑰的用途。取值:
Encrypt/Decrypt:數據加密和解密。
Sign/Verify:產生和驗證數字簽名。
別名
用戶主密鑰的標識符。支持英文字母、數字、下劃線(_)、短劃線(-)和正斜線(/)。
更多信息,請參見別名概述。
保護級別
密鑰的保護級別。取值:
Software:通過軟件模塊對密鑰進行保護。
Hsm:將密鑰托管在密碼機中,使密鑰獲得高安全等級的專用硬件的保護。
描述
密鑰的說明信息。
輪轉周期
設置對稱密鑰自動輪轉的時間周期。
說明僅密鑰類型為Aliyun_AES_256和Aliyun_SM4的對稱密鑰支持設置輪轉周期。
密鑰材料來源
選擇外部。
仔細閱讀參考文檔后勾選我了解使用外部密鑰材料的方法和意義,然后單擊確定。
獲取導入密鑰材料參數。
導入密鑰材料參數包括一個用于加密密鑰材料的公鑰,以及一個導入令牌。
在左側導航欄,單擊用戶主密鑰。
單擊目標密鑰ID進入密鑰管理頁面,在密鑰材料區域,單擊獲取導入參數。
在獲取導入密鑰材料的參數對話框,選擇公鑰類型和加密算法,并單擊下一步。
本文以公鑰類型為RSA_2048、加密算法為RSAES_OAEP_SHA_1為例進行介紹。
說明當公鑰類型取值為RSA_2048時,您可以選擇加密算法RSAES_PKCS1_V1_5(默認值)、RSAES_OAEP_SHA_1或RSAES_OAEP_SHA_256。
當公鑰類型取值為EC_SM2時,您可以選擇加密算法SM2PKE。
下載公鑰和導入令牌,然后單擊關閉。
下載公鑰時需要選擇公鑰格式:
公鑰格式選擇der格式:下載的公鑰文件后綴為.bin(例如:publickey_f240b730-7e3e-4bd7-877f-4fe22524****.bin)。
公鑰格式選擇pem格式:下載的公鑰文件后綴為.pem(例如:publickey_f240b730-7e3e-4bd7-877f-4fe22524****.pem)。
加密密鑰材料。
下載公鑰和導入令牌后,您可以使用該公鑰加密您的密鑰材料。
以使用OpenSSL加密密鑰材料為例:使用的加密算法需要與獲取導入對稱密鑰材料參數時指定的一致(RSAES_OAEP_SHA_1)。
創建一個密鑰材料,使用OpenSSL產生一個32字節的隨機數。
openssl rand -out KeyMaterial.bin 32
根據指定的加密算法(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
將加密后的密鑰材料進行Base64編碼,保存為文本文件。
openssl enc -e -base64 -A -in EncryptedKeyMaterial.bin -out EncryptedKeyMaterial_base64.txt
導入密鑰材料。
導入令牌與加密密鑰材料的公鑰具有綁定關系,一個令牌只能為其生成時指定的主密鑰導入密鑰材料。導入令牌的有效期為24小時,在有效期內可以重復使用,失效以后需要獲取新的導入令牌和公鑰。
在左側導航欄,單擊用戶主密鑰。
單擊目標密鑰ID進入密鑰管理頁面,密鑰材料區域,單擊導入密鑰材料。
在導入打包后的密鑰材料對話框,上傳打包后的密鑰材料和導入令牌。
打包后的密鑰材料:上傳加密密鑰材料中生成的密鑰材料文本文件。
導入令牌:上傳獲取導入密鑰材料參數中獲取的導入令牌文本文件。
設置密鑰材料過期時間,單擊確定。
導入密鑰材料成功后,密鑰狀態從待導入更新為啟用中。
通過ALIYUN CLI導入密鑰材料
創建外部密鑰。
通過命令aliyun kms CreateKey調用CreateKey接口,指定參數Origin為EXTERNAL。
aliyun kms CreateKey --Origin EXTERNAL --Description "External key"
獲取導入密鑰材料參數。
通過命令aliyun kms GetParametersForImport調用GetParametersForImport接口獲取導入密鑰材料參數。
aliyun kms GetParametersForImport --KeyId 1339cb7d-54d3-47e0-b595-c7d3dba8**** --WrappingAlgorithm RSAES_OAEP_SHA_1 --WrappingKeySpec RSA_2048
導入密鑰材料。
使用公鑰對密鑰材料進行加密。
公鑰是一個RSA(2048比特)或者SM2公鑰,使用的加密算法需要與獲取導入密鑰材料參數時指定的一致。由于API返回的公鑰經過Base64編碼,因此在使用時需要先進行Base64解碼。目前KMS支持的加密算法有RSAES_OAEP_SHA_1、RSAES_OAEP_SHA_256、RSAES_PKCS1_V1_5和SM2PKE,其中SM2PKE僅部分地域支持。更多信息,請參見支持的地域。
將加密后的密鑰材料進行Base64編碼。
通過命令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擁有所有API的訪問權限,建議您使用RAM用戶進行API訪問或日常運維。強烈建議不要把AccessKey ID和AccessKey Secret保存到工程代碼里,否則可能導致AccessKey泄露,威脅您賬號下所有資源的安全。
本示例以將AccessKey配置在環境變量ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET的方式來實現身份驗證為例。更多認證信息配置方式,請參見Credentials 設置。
//使用最新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擁有所有API的訪問權限,建議您使用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) } }