If you set the Origin parameter to EXTERNAL when you create a customer master key (CMK), Key Management Service (KMS) does not create key material. In this case, you must import external key material into the CMK. This topic describes how to import external key material.
Background information
CMKs are basic resources of KMS. A CMK is composed of a key ID, metadata such as the key status, and key material that is used to encrypt and decrypt data. If you use the default value Aliyun_KMS of the Origin parameter when you create a CMK, KMS generates key material. If you set the Origin parameter to EXTERNAL, KMS does not generate key material. In this case, you must import external key material into the CMK.
You can import key material that has never been imported into an external CMK or is expired or deleted. You can also reset the expiration time of key material.
- If the value of the Origin parameter in KeyMetadata is Aliyun_KMS, the key material is generated by KMS. In this case, the CMK is considered a regular CMK.
- If the value of the Origin parameter is EXTERNAL, the key material is imported from an external source. In this case, the CMK is considered an external CMK.
- Make sure that the source of randomness from which the key material is generated meets security requirements.
- Make sure that the key material is reliable.
- KMS ensures the high availability of imported key material. However, KMS cannot ensure that the imported key material has the same reliability as the key material generated by KMS.
- After you delete the imported key material from a CMK, you can re-import the same key material to make the CMK available again. Therefore, we recommend that you save a copy of the key material.
- You can call the DeleteKeyMaterial operation to delete the imported key material from a CMK. You can also set an expiration time to automatically delete the key material after the key material is expired. The CMK is not deleted.
- A CMK can have only one piece of key material. After you import key material into a CMK, the CMK is bound to the key material. Even after the key material is expired or deleted, you cannot import a different piece of key material into that CMK. If you need to rotate a CMK that uses external key material, you must create a CMK and then import new key material.
- CMKs are independent. You cannot use a CMK to decrypt data that is encrypted by using another CMK, even if the two CMKs use the same key material.
- The key material that you can import must be a 256-bit symmetric AES key.
Import key material in the KMS console
Import key material by using Alibaba Cloud CLI
Import the key material by using KMS SDK
Sample code:
- KMS SDK for Java
// Use the latest version of KMS SDK for Java. //KmsClient.java import com.aliyuncs.kms.model.v20160120.*; import com.aliyuncs.profile.DefaultProfile; // Encapsulate the CreateKey operation. 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"); // Create an external CMK. return this.client.getAcsResponse(request); } //.Omitted. The method that is used to encapsulate other operations is similar to the method that is used to encapsulate the preceding operation. } //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 = "*** Provide your AccessKeyId ***"; String accessKeySecret = "*** Provide your AccessKeySecret ***"; KmsClient kmsclient = new KmsClient(regionId,accessKeyId,accessKeySecret); // Create an external CMK. try { CreateKeyResponse keyResponse = kmsclient.createKey(); String keyId = keyResponse.KeyMetadata.getKeyId(); // Generate a 32-byte random number. byte[] keyMaterial = new byte[32]; new Random().nextBytes(keyMaterial); // Obtain the parameters that are used to import key material. GetParametersForImportResponse paramResponse = kmsclient.getParametersForImport(keyId,"RSAES_OAEP_SHA_256"); String importToekn = paramResponse.getImportToken(); String encryptPublicKey = paramResponse.getPublicKey(); // Decode the public key in Base64. byte[] publicKeyDer = DatatypeConverter.parseBase64Binary(encryptPublicKey); // Generate an RSA public key. KeyFactory keyFact = KeyFactory.getInstance("RSA"); X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyDer); PublicKey publicKey = keyFact.generatePublic(spec); // Encrypt the key material. 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); // Encode the encrypted key material in Base64. String encryptedKeyMaterial = DatatypeConverter.printBase64Binary(cipherDer); // Import the key material. Long expireTimestamp = 1546272000L; // The UNIX timestamp, which is accurate to the second. A value of 0 indicates that the key material does not expire. kmsclient.importKeyMaterial(keyId,encryptedKeyMaterial, expireTimestamp); } catch(Exception e) { //... Omitted. } } }
- KMS SDK for Go
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" ) // Encapsulate the CreateKey operation. func kmsCreateKey(client *kms.Client) (string, error) { request := kms.CreateCreateKeyRequest() request.Scheme = "https" request.Origin = "EXTERNAL" // Create an external CMK. response, err := client.CreateKey(request) if err != nil { return "", fmt.Errorf("CreateKey error:%v", err) } return response.KeyMetadata.KeyId, nil } // Encapsulate the GetParametersForImport operation. 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 } // Encapsulate the ImportKeyMaterial operation. 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 := "*** Provide your AccessKeyId ***" accessKeySecret := "*** Provide your AccessKeySecret ***" regionId := "cn-hangzhou" client, err := kms.NewClientWithAccessKey(regionId, accessKeyId, accessKeySecret) if err != nil { log.Fatalf("NewClientWithAccessKey error:%+v\n", err) } // Create an external CMK. keyId, err := kmsCreateKey(client) if err != nil { log.Fatalf("kmsCreateKey error:%+v\n", err) } // The following sample code is used to generate a 32-byte random number. In actual scenarios, you must generate a key in your own key management system. Then, you must use the public key in the parameters that are used to import key material to encrypt the key material. keyMaterial := randBytes(32) // Obtain the parameters that are used to import key material. encryptPublicKey, importToken, err := kmsGetParametersForImport(client, keyId, "RSA_2048", "RSAES_OAEP_SHA_256") if err != nil { log.Fatalf("kmsGetParametersForImport error:%v\n", err) } // Decode the public key in Base64. publicKeyDer, err := base64.StdEncoding.DecodeString(encryptPublicKey) if err != nil { log.Fatalf("base64.StdEncoding.DecodeString error:%v\n", err) } // Generate an RSA public key.. publicKey, err := x509.ParsePKIXPublicKey(publicKeyDer) if err != nil { log.Fatalf("x509.ParsePKIXPublicKey error:%v\n", err) } // Encrypt the key material. cipherDer, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey.(*rsa.PublicKey), keyMaterial, nil) if err != nil { log.Fatalf("rsa.EncryptOAEP error:%v\n", err) } // Encode the encrypted key material in Base64. encryptedKeyMaterial := base64.StdEncoding.EncodeToString(cipherDer) // Import the key material. err = kmsImportKeyMaterial(client, keyId, importToken, encryptedKeyMaterial) if err != nil { log.Fatalf("ImportKeyMaterial error:%v", err) } }