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 to 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, basic metadata, such as the key status, and key material that is used to encrypt and decrypt data. When you call the CreateKey operation to create a CMK, if you use the default value Aliyun_KMS of the Origin parameter, 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 to the CMK.

You can call the DescribeKey operation to view the key material source of an existing 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.
Before you import external key material, take note of the following points:
  • 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, it cannot ensure that the imported key material has the same reliability as the key material generated by KMS.
    • You can call the DeleteKeyMaterial operation to remove the imported key material. You can also set an expiration time to automatically remove the key material after it expires. The CMK is not deleted. To delete key material generated by KMS, you can only call the ScheduleKeyDeletion operation to specify a waiting period of 7 to 30 days for deleting the CMK. The key material is deleted along with the relevant CMK after the waiting period ends.
    • After you remove the imported key material, you can re-import the same key material to make the relevant CMK available again. Therefore, we recommend that you save a copy of the key material.
  • Key material is unique for each CMK. When you import key material into a CMK, the CMK is associated with the key material. Even after the key material expires or is removed, you cannot import different 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 to be imported must be a 256-bit symmetric key.

Import key material in the KMS console

  1. Create an external CMK.
    1. Log on to the KMS console.
    2. In the top navigation bar, select the region where you want to create an external CMK.
    3. In the left-side navigation pane, click Keys.
    4. Click Create Key.
    5. In the Create Key dialog box, set the Key Spec parameter.
      Set the Key Spec parameter to Aliyun_AES_256. For more information about symmetric key types, see Types of symmetric keys.
    6. Set the Alias Name, Protection Level, Description, and Rotation Period parameters.
    7. Click Advanced and set the Key Material Source parameter to External.
    8. 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 the key material.
    1. In the left-side navigation pane, click Keys.
    2. Find the CMK to which you want to import key material and click its alias in the Key column to go to the CMK management page.
    3. In the Key Material section, click Key Encryption Material.
    4. In the Key Encryption Material dialog box, set the Wrapping Key Type parameter to RSA_2048 and the Wrapping Algorithm parameter to RSAES_OAEP_SHA_1, and click Next.
      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. In this example, the Wrapping Algorithm parameter is set to RSAES_OAEP_SHA_1.

    5. Click Download next to Public Key and that next to Import Token to download the required public key and import token. Then, click Close.
  3. Use OpenSSL to encrypt the 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 the key material. The public key is encoded in Base64. You must first decode the public key. You can use the public key encrypted by using OpenSSL to obtain your key material.
    1. Create key material and use OpenSSL to generate a 32-byte random number.
    2. Decode the public key that is used to encrypt the key material in Base64.
    3. Use the specified encryption algorithm, such as RSAES_OAEP_SHA_1, to encrypt the key material.
    4. Encode the encrypted key material in Base64 and save it to a text file.
      openssl rand -out KeyMaterial.bin 32
      openssl enc -d -base64 -A -in PublicKey_base64.txt -out PublicKey.bin
      openssl rsautl -encrypt -in KeyMaterial.bin -oaep -inkey PublicKey.bin  -keyform DER  -pubin -out EncryptedKeyMaterial.bin
      openssl enc -e -base64 -A -in EncryptedKeyMaterial.bin -out EncryptedKeyMaterial_base64.txt
  4. Import the key material.

    You can import key material into an external CMK that does not have key material. You can also reset the expiration time of key material or re-import key material that has expired or been removed.

    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. The import token can be used to import key material only into the specified CMK. The validity period of an import token is 24 hours. It can be repeatedly used within this period. After it expires, you must obtain a new import token and a new public key.

    1. In the left-side navigation pane, click Keys.
    2. Find the CMK to which you want to import key material and click its alias in the Key column to go to the CMK management page.
    3. In the Key Material section, click Import Key Material.
    4. In the Import Key Material dialog box, set the following parameters:
      • Encrypted Key Material: Upload the text file generated in Step 3 that contains the key material.
      • Import Token: Upload the text file obtained in Step 2 that contains the import token.
    5. Set 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 the key material.

      The public key is a 2,048-bit RSA public key. The encryption algorithm must be the same as that specified when you obtain the parameters that are used to import the key material. The public key returned when you call 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. Run the aliyun kms ImportKeyMaterial command to call the ImportKeyMaterial operation to import the encoded key material and the import token to KMS.
      aliyun kms ImportKeyMaterial --KeyId 1339cb7d-54d3-47e0-b595-c7d3dba8**** --EncryptedKeyMaterial xxx --ImportToken xxxx

Sample code

  • SDK for Java
    // Use the latest KMS SDK for Java. 
    //KmsClient.java
    
    import com.aliyuncs.kms.model.v20160120.*;
    import com.aliyuncs.profile.DefaultProfile;
    
    // Encapsulate KMS API operations. 
    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 remaining operations are encapsulated in the same way as the preceding operations. 
    }
    //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);
                    // Use RSA to parse the 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 precise to the second. A value of 0 indicates that the key material does not expire. 
                            kmsClient.importKeyMaterial(keyId,encryptedKeyMaterial, expireTimestamp);
            } catch(Exception e) {
                    //... Omitted. 
            }
            }
    }
  • 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 executed to generate a 32-byte random number. In actual scenarios, you need to generate a key in your own key management system. In addition, 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)
        }
        // Use RSA to parse the 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)
        }
    }