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.

You can call the DescribeKey operation to view the key material source of a CMK.
  • 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.
Take note of the following points when you import external key material:
  • 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

  1. Create an external CMK.
    1. Log on to the KMS console. In the top navigation bar, select the region where you want to create an external CMK.
    2. In the left-side navigation pane, click Keys.
    3. Click Create Key. In the Create Key dialog box, configure the parameters.
      Parameter Description
      KMS Instance The KMS instance that you want to use. Retain the default value Default. You cannot change the value of the parameter.
      Key Spec The type of the CMK. Set the parameter to Aliyun_AES_256. For more information about the types of symmetric CMKs, see Types of symmetric keys.
      Purpose The purpose of the CMK. Valid values:
      • Encrypt/Decrypt: encrypts or decrypts data.
      • Sign/Verify: generates or verifies a digital signature.
      Alias Name The identifier of the CMK. The name can contain letters, digits, underscores (_), hyphens (-), and forward slashes (/).

      For more information, see Overview.

      Protection Level The protection level of the CMK. Valid values:
      • Software: The CMK is protected by using a software module.
      • Hsm: The CMK is managed in a hardware security module (HSM), and the HSM safeguards the CMK.
      Description The description of the CMK.
      Rotation Period The automatic rotation interval of the CMK.
      Note You can configure this parameter only if you set the Key Spec parameter to Aliyun_AES_256.
      Key Material Source The source of key material. Select External.
    4. Select I understand the implications of using the external key materials key and click OK.
  2. Obtain the parameters that are used to import key material.
    The parameters include a public key and an import token. The public key is used to encrypt key material.
    1. In the left-side navigation pane, click Keys.
    2. Click the ID of the CMK into which you want to import key material to go to the key management page. In the Key Material section, click Obtain Parameters Used to Import Key Material.
    3. In the Obtain Parameters Used to Import Key Material dialog box, configure the Wrapping Key Type parameter and the Wrapping Algorithm parameter and click Next.

      In this example, set the Wrapping Key Type parameter to RSA_2048 and the Wrapping Algorithm parameter to RSAES_OAEP_SHA_1.

      Note

      If you set the Wrapping Key Type parameter to RSA_2048, you can set the Wrapping Algorithm parameter to RSAES_PKCS1_V1_5, RSAES_OAEP_SHA_1, or RSAES_OAEP_SHA_256. The default value is RSAES_PKCS1_V1_5.

    4. Download the public key and the import token. Then, click Close.
      Before you download the public key, you must select a value for Public Key Format.
      • Select DER Format for Public Key Format: The suffix of the downloaded public key file is .bin. Example: publickey_f240b730-7e3e-4bd7-877f-4fe22524****.bin.
      • Select PEM Format for Public Key Format: The suffix of the downloaded public key file is .pem. Example: publickey_f240b730-7e3e-4bd7-877f-4fe22524****.pem.
  3. Encrypt key material.
    After you download the public key and import token, you can use the public key to encrypt key material.

    The following procedure describes how to use OpenSSL to encrypt key material. The encryption algorithm must be the same as the algorithm that you specify when you obtain the parameters that are used to import key material. In this example, the algorithm RSAES_OAEP_SHA_1 is used.

    1. Use OpenSSL to generate a 32-byte random number. The number is used as key material.
      openssl rand -out KeyMaterial.bin 32
    2. Use the specified encryption algorithm to encrypt the key material.
      Before you run the following sample code, take note of the following items:
      • Replace PublicKey.bin in the sample code with the name of the public key file that you downloaded in Obtain the parameters that are used to import key material.
      • In the sample code, the specified public key file is in the DER format. If you select PEM Format when you download the public key, you must replace -keyform DER in the sample code with -keyform PEM.
      openssl rsautl -encrypt -in KeyMaterial.bin -oaep -inkey PublicKey.bin  -keyform DER  -pubin -out EncryptedKeyMaterial.bin
    3. Encode the encrypted key material in Base64 and save the encoded key material to a text file.
      openssl enc -e -base64 -A -in EncryptedKeyMaterial.bin -out EncryptedKeyMaterial_base64.txt
  4. Import the key material.

    Each import token is bound to a public key that is used to encrypt key material. A CMK is specified when an import token is generated. An import token can be used to import key material only for the CMK that is specified. The validity period of an import token is 24 hours. The token can be repeatedly used within this period. After the token expires, you must obtain a new import token and a new public key.

    1. In the left-side navigation pane, click Keys.
    2. Click the ID of the CMK for which you want to import key material to go to the key management page. In the Key Material section, click Import Wrapped Key Material.
    3. In the Import Wrapped Key Material dialog box, configure the Wrapped Key Material and Import Token parameters.
    4. Configure the Valid Until parameter and click OK.
      After the key material is imported, the status of the CMK changes from Pending Import to Enabled.

Import key material by using Alibaba Cloud CLI

  1. Create an external CMK.
    Run the aliyun kms CreateKey command to call the CreateKey operation to create an external CMK by setting the Origin parameter to EXTERNAL.
    aliyun kms CreateKey --Origin EXTERNAL --Description "External key"
  2. Obtain the parameters that are used to import key material.
    Run the aliyun kms GetParametersForImport command to call the GetParametersForImport operation to obtain the parameters that are used to import key material.
    aliyun kms GetParametersForImport --KeyId 1339cb7d-54d3-47e0-b595-c7d3dba8**** --WrappingAlgorithm RSAES_OAEP_SHA_1 --WrappingKeySpec RSA_2048
  3. Import the key material.
    1. Use the public key to encrypt key material.

      The public key is a 2,048-bit Rivest-Shamir-Adleman (RSA) public key. The encryption algorithm must be the same as that specified when you obtain the parameters that are used to import key material. The public key that is returned by the GetParametersForImport operation is encoded in Base64. You must first decode the public key. KMS supports the following encryption algorithms: RSAES_OAEP_SHA_1, RSAES_OAEP_SHA_256, and RSAES_PKCS1_V1_5.

    2. Encode the encrypted key material in Base64.
    3. Pass the key material and import token to the ImportKeyMaterial operation as parameters. Run the aliyun kms ImportKeyMaterial command to import the encoded key material into KMS:
      aliyun kms ImportKeyMaterial --KeyId 1339cb7d-54d3-47e0-b595-c7d3dba8**** --EncryptedKeyMaterial xxx --ImportToken xxxx --KeyMaterialExpireUnix xxxx

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)
        }
    }