If you set the Origin parameter to EXTERNAL, 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.

Prerequisites

An Alibaba Cloud account is created. To create an Alibaba Cloud account, visit the account registration page.

Background information

Customer master keys (CMKs) are basic resources of KMS. A CMK is composed of a key ID, basic metadata (such as key state), 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 Origin in KeyMetadata is Aliyun_KMS, the key material is generated by KMS. In this case, the CMK is considered a common key.
  • If the value of Origin is EXTERNAL, the key material is imported from an external source. In this case, the CMK is considered an external key.
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 delete the imported key material. You can also set an expiration time to automatically delete 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 delete 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 that key material. Even after the key material expires or is deleted, 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 key.
    1. Log on to the KMS console.
    2. In the top navigation bar, select the region where you want to create an external key.
    3. In the left-side navigation pane, click Keys.
    4. In the upper-left corner of the page that appears, click Create Key.
    5. In the Create Key dialog box, specify Key Spec.
      Set Key Spec to Aliyun_AES_256. For more information about key types, see Types of symmetric keys.
    6. Specify Alias Name, Description, and Rotation Period.
    7. Click Advanced and set Key Material Source 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.
    These 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 to go to the key management page.
    3. In the Key Material section, click Key Encryption Material.
    4. In the Key Encryption Material dialog box, set Wrapping Algorithm to RSAES_OAEP_SHA_1 and click Next.
      KMS supports the following encryption algorithms: RSAES_PKCS1_V1_5 (default value), RSAES_OAEP_SHA_1, and RSAES_OAEP_SHA_256. In this example, RSAES_OAEP_SHA_1 is used.
    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 key material.
    The public key is a 2048-bit Rivest-Shamir-Adleman (RSA) public key. The encryption algorithm must be consistent with the algorithm specified when you obtain the import parameters. The public key is Base64 encoded. You must first decode the public key. You can use the public key encrypted by using the OpenSSL to obtain your key material.
    1. Create key material and use OpenSSL to generate a 32-byte random number.
    2. Base64 decode the public key that is used to encrypt the key material.
    3. Use an encryption algorithm such as RSAES_OAEP_SHA_1 to encrypt the key material.
    4. Base64 encode the encrypted key material and save it as 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 key material.

    You can import key material into an external key that has never had key material. You can also reset the expiration time of key material or re-import key material that has expired or been deleted.

    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 only be used to import key material into the specified CMK. The lifecycle of an import token is 24 hours. It can be used repeatedly 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 to go to the key management page.
    3. In the Key Material section, click Import Key Material.
    4. In the Import Key Material dialog box, specify the following parameters:
      • Encrypted Key Material: Upload the key material text file generated in Step 3.
      • Import Token: Upload the import token text file obtained in Step 2.
    5. Specify Valid Until and click OK.
      After the key material is imported, the state of the key changes from Pending Import to Enabled.

Import key material by using Alibaba Cloud CLI

  1. Create an external key.
    Run the aliyun kms CreateKey command to call the CreateKey operation to set Origin 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 key material.
    1. Use the public key to encrypt the key material.

      The public key is a 2048-bit Rivest-Shamir-Adleman (RSA) public key. The encryption algorithm must be consistent with the algorithm specified when you obtain the import parameters. The public key returned when you call the GetParametersForImport operation is Base64 encoded. 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. Base64 encode the encrypted key material.
    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;
    
    //KMS API encapsulation
    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 key.
                    return this.client.getAcsResponse(request);
            }
            //... Omitted. The remaining operations are the same as those in the API method.
    }
    //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 key.
            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();
                    //Base64 decode the public key.
                    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);
                    //Base64 encode the encrypted key material.
                    String encryptedKeyMaterial = DatatypeConverter.printBase64Binary(cipherDer);
                    //Import the key material.
                    Long expireTimestamp = 1546272000L; //UNIX timestamp, precise to the second. 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"
    )
    
    //Encapsulation of the CreateKey operation
    func kmsCreateKey(client *kms.Client) (string, error) {
        request := kms.CreateCreateKeyRequest()
        request.Scheme = "https"
        request.Origin = "EXTERNAL" //Create an external key.
        response, err := client.CreateKey(request)
        if err ! = nil {
            return "", fmt.Errorf("CreateKey error:%v", err)
        }
        return response.KeyMetadata.KeyId, nil
    }
    
    //Encapsulation of 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
    }
    
    //Encapsulation of 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 key.
        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 and 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)
        }
        //Base64 decode the public key.
        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)
        }
        //Base64 encode the encrypted key material.
        encryptedKeyMaterial := base64.StdEncoding.EncodeToString(cipherDer)
        //Import the key material.
        err = kmsImportKeyMaterial(client, keyId, importToken, encryptedKeyMaterial)
        if err ! = nil {
            log.Fatalf("ImportKeyMaterial error:%v", err)
        }
    }