非对称密钥规范 - AWS Key Management Service

非对称密钥规范

以下主题提供了有关 AWS KMS 为非对称 KMS 密钥支持的密钥规范的技术信息。包括有关对称加密密钥的 SYMMETRIC_DEFAULT 密钥规范的信息以便进行比较。

RSA 密钥规范

使用 RSA 密钥规范时,AWS KMS 会创建具有 RSA 密钥对的非对称 KMS 密钥。私有密钥永远不会让 AWS KMS 处于未加密状态。在 AWS KMS 内部可以使用公有密钥,或者下载公有密钥供 AWS KMS 外部使用。

警告

在 AWS KMS 外部加密数据时,请确保您可以解密密文。如果您使用已从 AWS KMS 删除的 KMS 密钥中的公有密钥、配置用于签名和验证的 KMS 密钥中的公有密钥或者使用 KMS 密钥不支持的加密算法,则数据将无法恢复。

在 AWS KMS 中,可以使用具有 RSA 密钥对的非对称 KMS 密钥进行加密和解密或签名和验证,但不能同时用于二者。此属性(称为 密钥用法)与密钥规范分开确定,但应该在选择密钥规范之前做出决定。

AWS KMS 支持以下 RSA 密钥规范,用于加密和解密或签名和验证:

  • RSA_2048

  • RSA_3072

  • RSA_4096

RSA 密钥规范因 RSA 密钥长度(以位为单位)而异。选择哪个 RSA 密钥规范可能取决于安全标准或任务要求。一般来说,可使用对任务而言实用又实惠的最大密钥。使用不同 RSA 密钥规范的 KMS 密钥定价不同,并受不同的请求配额限制。有关 AWS KMS 定价的信息,请参阅 AWS Key Management Service 定价。有关请求配额的信息,请参阅 请求配额

用于加密和解密的 RSA 密钥规范

使用 RSA 非对称 KMS 密钥行加密和解密时,用公有密钥加密,然后用私有密钥解密。在 AWS KMS 中为 RSA KMS 密钥调用 Encrypt 操作时,AWS KMS 使用 RSA 密钥对中的公有密钥和您指定的加密算法来加密数据。要解密密文,请调用 Decrypt 操作并指定相同的 KMS 密钥和加密算法。AWS KMS 随后使用 RSA 密钥对中的私有密钥解密数据。

也可以下载公有密钥,并在 AWS KMS 外部使用该密钥加密数据。请确保使用 AWS KMS 支持用于 RSA KMS 密钥的加密算法。要解密密文,请采用相同的 KMS 密钥和加密算法调用 Decrypt 函数。

对于具有 RSA 密钥规范的 KMS 密钥,AWS KMS 支持两种加密算法。这些算法在 PKCS #1 2.2 版中定义,它们内部使用的哈希函数有所不同。在 AWS KMS 中,RSAES_OAEP 算法始终使用相同的哈希函数实现哈希目的和掩码生成函数 (MGF1)。调用 EncryptDecrypt 操作时,需要指定加密算法。您可以为每个请求选择不同的算法。

支持 RSA 密钥规范的加密算法
加密算法 算法描述
RSAES_OAEP_SHA_1 PKCS #1 2.2 版,7.1 节。具有 OAEP 填充且采用 SHA-1 实现哈希和 MGF1 掩码生成函数以及空标签的 RSA 加密。
RSAES_OAEP_SHA_256 PKCS #1,7.1 节。具有 OAEP 填充且采用 SHA-256 实现哈希和 MGF1 掩码生成函数以及空标签的 RSA 加密。

无法将 KMS 密钥配置为使用特定的加密算法。不过,可以使用 kms:EncryptionAlgorithm 策略条件,指定允许委托人与 KMS 密钥一起使用的加密算法。

若要获取 KMS 密钥的加密算法,请在 AWS KMS 控制台中或者使用 DescribeKey 操作查看 KMS 密钥的加密配置。您在 AWS KMS 控制台中或使用 GetPublicKey 操作下载公有密钥时,AWS KMS 还会提供密钥规范和加密算法。

您可以根据可在每个请求中加密的明文数据的长度,选择 RSA 密钥规范。下表显示了在对 Encrypt 操作的单次调用中,可以加密的明文的最大长度(以字节为单位)。值因密钥规范和加密算法而异。为进行比较,使用对称加密 KMS 密钥一次最多可加密 4096 字节。

要计算这些算法的最大明文长度(以字节为单位),请使用以下公式:(key_size_in_bits / 8) - (2 * hash_length_in_bits/8) - 2。例如,对于具有 SHA-256 的 RSA_2048,最大明文长度为 (2048/8) - (2 * 256/8) -2 = 190 字节。

Encrypt 操作中的最大明文长度(以字节为单位)
加密算法
密钥规范 RSAES_OAEP_SHA_1 RSAES_OAEP_SHA_256
RSA_2048 214 190
RSA_3072 342 318
RSA_4096 470 446

用于签名和验证的 RSA 密钥规范

使用 RSA 非对称 KMS 密钥进行签名和验证时,用私有密钥为消息生成签名,然后用公有密钥验证签名。

在 AWS KMS 中为非对称 KMS 密钥调用 Sign 操作时,AWS KMS 使用 RSA 密钥对中的私有密钥、消息和您指定的签名算法来生成签名。要验证签名,请调用 Verify 操作。指定签名以及相同的 KMS 密钥、消息和签名算法。AWS KMS 随后使用 RSA 密钥对中的公有密钥验证签名。也可以下载公有密钥,并在 AWS KMS 外部使用该密钥验证签名。

对于具有 RSA 密钥规范的 KMS 密钥,AWS KMS 支持以下签名算法。在您调用 SignVerify 操作后,需要指定签名算法。您可以为每个请求选择不同的算法。

支持 RSA 密钥规范的签名算法
签名算法 算法描述
RSASSA_PKCS1_V1_5_SHA_256 PKCS #1 2.2 版,8.2 节,具有 PKCS #1v1.5 填充和 SHA-256 的 RSA 签名
RSASSA_PKCS1_V1_5_SHA_384 PKCS #1 2.2 版,8.2 节,具有 PKCS #1v1.5 填充和 SHA-384 的 RSA 签名
RSASSA_PKCS1_V1_5_SHA_512 PKCS #1 2.2 版,8.2 节,具有 PKCS #1v1.5 填充和 SHA-512 的 RSA 签名
RSASSA_PSS_SHA_256 PKCS #1 2.2 版,8.1 节,具有 PSS 填充且采用 SHA-256 实现消息摘要和 MGF1 掩码生成函数以及 256 位盐的 RSA 签名
RSASSA_PSS_SHA_384 PKCS #1 2.2 版,8.1 节,具有 PSS 填充且采用 SHA-384 实现消息摘要和 MGF1 掩码生成函数以及 384 位盐的 RSA 签名
RSASSA_PSS_SHA_512 PKCS #1 2.2 版,8.1 节,具有 PSS 填充且采用 SHA-512 实现消息摘要和 MGF1 掩码生成函数以及 512 位盐的 RSA 签名

无法将 KMS 密钥配置为使用特定的签名算法。不过,可以使用 kms:SigningAlgorithm 策略条件,指定允许委托人与 KMS 密钥一起使用的签名算法。

若要获取 KMS 密钥的签名算法,请在 AWS KMS 控制台中或者使用 DescribeKey 操作查看 KMS 密钥的加密配置。您在 AWS KMS 控制台中或使用 GetPublicKey 操作下载公有密钥时,AWS KMS 还会提供密钥规范和签名算法。

椭圆曲线密钥规范

在使用椭圆曲线 (ECC) 密钥规范时,AWS KMS 会创建具有 ECC 密钥对的非对称 KMS 密钥用于签名和验证。生成签名的私有密钥永远不会让 AWS KMS 处于未加密状态。您可以在 AWS KMS 内部使用公有密钥验证签名,或者下载公有密钥供 AWS KMS 外部使用。

对于非对称 KMS 密钥,AWS KMS 支持以下 ECC 密钥规范。

  • 非对称 NIST 推荐的椭圆曲线密钥对(签名和验证)

    • ECC_NIST_P256 (secp256r1)

    • ECC_NIST_P384 (secp384r1)

    • ECC_NIST_P521 (secp521r1)

  • 其他非对称椭圆曲线密钥对(签名和验证)

    • ECC_SECG_P256K1 (secp256k1),常用于加密货币。

选择哪个 ECC 密钥规范可能取决于安全标准或任务要求。一般来说,可使用对任务而言实用又实惠的点最多的曲线。

如果您正在创建非对称 KMS 密钥以用于加密货币,请使用 ECC_SEG_P256K1 密钥规范。此密钥规范也可以用于其他目的,但比特币和其他加密货币必须使用此密钥规范。

使用不同 ECC 密钥规范的 KMS 密钥定价不同,并受不同的请求配额限制。有关 AWS KMS 定价的信息,请参阅 AWS Key Management Service 定价。有关请求配额的信息,请参阅 请求配额

下表显示了 AWS KMS 对于每个 ECC 密钥规范支持的签名算法。无法将 KMS 密钥配置为使用特定的签名算法。不过,可以使用 kms:SigningAlgorithm 策略条件,指定允许委托人与 KMS 密钥一起使用的签名算法。

支持 ECC 密钥规范的签名算法
密钥规范 签名算法 算法描述
ECC_NIST_P256 ECDSA_SHA_256 NIST FIPS 186-4,6.4 节,使用密钥指定的曲线并采用SHA-256 实现消息摘要的 ECDSA 签名。
ECC_NIST_P384 ECDSA_SHA_384 NIST FIPS 186-4,6.4 节,使用密钥指定的曲线并采用 SHA-384 实现消息摘要的 ECDSA 签名。
ECC_NIST_P521 ECDSA_SHA_512 NIST FIPS 186-4,6.4 节,使用密钥指定的曲线并采用 SHA-512 实现消息摘要的 ECDSA 签名。
ECC_SECG_P256K1 ECDSA_SHA_256 NIST FIPS 186-4,6.4 节,使用密钥指定的曲线并采用SHA-256 实现消息摘要的 ECDSA 签名。

SM2 密钥规范(仅限中国区域)

SM2 密钥规范是在 GM/T 系列规范中定义的椭圆曲线密钥规范,这一系列规范由中国国家商用密码管理办公室(OSCCA)发布。SM2 密钥规范仅限中国区域使用。使用 SM2 密钥规范时,AWS KMS 会创建具有 SM2 密钥对的非对称 KMS 密钥。您可以在 AWS KMS 内使用 SM2 密钥对,或者下载公有密钥以供在 AWS KMS 外部使用。

与 ECC 密钥规范不同,您可以使用 SM2 KMS 密钥进行签名和验证,或者加密和解密。在创建 KMS 密钥时,您必须指定密钥用途,并且密钥一经创建便无法更改。

AWS KMS 支持以下 SM2 加密算法和签名算法:

  • SM2PKE 加密算法

    SM2PKE 是一种由 OSCCA 在 GM/T 0003.4-2012 中定义的基于椭圆曲线的加密算法。

  • SM2DSA 签名算法

    SM2DSA 是一种由 OSCCA 在 GM/T 0003.2-2012 中定义的基于椭圆曲线的签名算法。SM2DSA 需要一个区分 ID,该 ID 使用 SM3 哈希算法进行哈希处理,然后会与您传递到 AWS KMS 的消息或消息摘要相结合。然后由 AWS KMS 对这个连接的值进行哈希处理和签名。

借助 SM2 进行离线操作(仅限中国区域)

您可以下载 SM2 密钥对的公有密钥以在离线操作中使用,即 AWS KMS 外部的操作。但是,离线使用 SM2 公有密钥时,您可能需要手动执行额外的转换和计算。SM2DSA 操作可能需要您提供区分 ID 或计算消息摘要。SM2PKE 加密操作可能需要您将原始加密文字输出转换为某种 AWS KMS 可以接受的格式。

为了帮助您使用这些操作,Java 的 SM2OfflineOperationHelper 类为您执行任务提供了方法。您可以将此帮助程序类用作其他加密提供程序的模型。

重要

SM2OfflineOperationHelper 参考代码旨在兼容 Bouncy Castle 版本 1.68。如需其他版本的帮助,请联系 bouncycastle.org

使用 SM2 密钥对进行离线验证(仅限中国区域)

要使用 SM2 公有密钥验证 AWS KMS 外部的签名,您必须指定区分 ID。当您将原始消息 MessageType:RAW 传递到签名 API 时,AWS KMS 将使用 OSCCA 在 GM/T 0009-2012 中定义的默认区分 ID 1234567812345678。您不能在 AWS KMS 中指定自己的区分 ID。

但是,如果您在 AWS 外部生成消息摘要,可以指定自己的区分 ID,然后将消息摘要 MessageType:DIGEST 传递到 AWS KMS 以进行签名。要执行此操作,请更改 SM2OfflineOperationHelper 类中的 DEFAULT_DISTINGUISHING_ID 值。您指定的区分 ID 可以是长度不超过 8192 个字符的任何字符串。在 AWS KMS 为消息摘要签名后,您需要消息摘要或消息以及用于计算摘要的区分 ID 以进行离线验证。

SM2OfflineOperationHelper

在 AWS KMS 内,原始加密文字转换和 SM2DSA 消息摘要计算会自动进行。并非所有加密提供程序都按相同的方式实现 SM2。有些库(比如 OpenSSL 版本 1.1.1 和更高版本)会自动执行这些操作。AWS KMS 在使用 OpenSSL 版本 3.0 进行测试时确认了此行为。使用以下带有库的 SM2OfflineOperationHelper 类,比如 Bouncy Castle,这需要您手动执行这些转换和计算。

SM2OfflineOperationHelper 类为以下离线操作提供了方法:

  • 消息摘要计算

    要离线生成可用于离线验证或传递到 AWS KMS 以进行签名的消息摘要,请使用 calculateSM2Digest 方法。calculateSM2Digest 方法通过 SM3 哈希算法生成消息摘要。GetPublicKey API 以二进制格式返回您的公有密钥。您必须把二进制密钥解析为 Java PublicKey。提供解析后的公有密钥与消息。该方法会自动将您的消息与默认的区分 ID 1234567812345678 相结合,但您可以通过更改 DEFAULT_DISTINGUISHING_ID 值来设置自己的区分 ID。

  • 验证

    要离线验证签名,请使用 offlineSM2DSAVerify 方法。offlineSM2DSAVerify 方法会使用根据指定区分 ID 计算出的消息摘要和您提供的原始消息来验证数字签名。GetPublicKey API 以二进制格式返回您的公有密钥。您必须把二进制密钥解析为 Java PublicKey。向解析后的公有密钥提供原始消息和要验证的签名。有关更多详细信息,请参阅使用 SM2 密钥对进行离线验证

  • Encrypt

    要离线加密明文,请使用 offlineSM2PKEEncrypt 方法。此方法可确保加密文字采用某种 AWS KMS 可以解密的格式。offlineSM2PKEEncrypt 方法会对明文进行加密,然后将 SM2PKE 生成的原始加密文字转换为 ASN.1 格式。GetPublicKey API 以二进制格式返回您的公有密钥。您必须把二进制密钥解析为 Java PublicKey。为解析后的公有密钥提供您想要加密的明文。

    如果您不确定是否需要进行转换,请使用以下 OpenSSL 操作来测试加密文字的格式。如果操作失败,则需要将加密文字转换为 ASN.1 格式。

    openssl asn1parse -inform DER -in ciphertext.der

默认情况下,在为 SM2DSA 操作生成消息摘要时,SM2OfflineOperationHelper 类使用默认的区分 ID 1234567812345678

package com.amazon.kms.utils; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.jce.interfaces.ECPublicKey; import java.util.Arrays; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.gm.GMNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.params.ParametersWithID; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.SM2Signer; import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; public class SM2OfflineOperationHelper { // You can change the DEFAULT_DISTINGUISHING_ID value to set your own distinguishing ID, // the DEFAULT_DISTINGUISHING_ID can be any string up to 8,192 characters long. private static final byte[] DEFAULT_DISTINGUISHING_ID = "1234567812345678".getBytes(StandardCharsets.UTF_8); private static final X9ECParameters SM2_X9EC_PARAMETERS = GMNamedCurves.getByName("sm2p256v1"); // ***calculateSM2Digest*** // Calculate message digest public static byte[] calculateSM2Digest(final PublicKey publicKey, final byte[] message) throws NoSuchProviderException, NoSuchAlgorithmException { final ECPublicKey ecPublicKey = (ECPublicKey) publicKey; // Generate SM3 hash of default distinguishing ID, 1234567812345678 final int entlenA = DEFAULT_DISTINGUISHING_ID.length * 8; final byte [] entla = new byte[] { (byte) (entlenA & 0xFF00), (byte) (entlenA & 0x00FF) }; final byte [] a = SM2_X9EC_PARAMETERS.getCurve().getA().getEncoded(); final byte [] b = SM2_X9EC_PARAMETERS.getCurve().getB().getEncoded(); final byte [] xg = SM2_X9EC_PARAMETERS.getG().getXCoord().getEncoded(); final byte [] yg = SM2_X9EC_PARAMETERS.getG().getYCoord().getEncoded(); final byte[] xa = ecPublicKey.getQ().getXCoord().getEncoded(); final byte[] ya = ecPublicKey.getQ().getYCoord().getEncoded(); final byte[] za = MessageDigest.getInstance("SM3", "BC") .digest(ByteBuffer.allocate(entla.length + DEFAULT_DISTINGUISHING_ID.length + a.length + b.length + xg.length + yg.length + xa.length + ya.length).put(entla).put(DEFAULT_DISTINGUISHING_ID).put(a).put(b).put(xg).put(yg).put(xa).put(ya) .array()); // Combine hashed distinguishing ID with original message to generate final digest return MessageDigest.getInstance("SM3", "BC") .digest(ByteBuffer.allocate(za.length + message.length).put(za).put(message) .array()); } // ***offlineSM2DSAVerify*** // Verify digital signature with SM2 public key public static boolean offlineSM2DSAVerify(final PublicKey publicKey, final byte [] message, final byte [] signature) throws InvalidKeyException { final SM2Signer signer = new SM2Signer(); CipherParameters cipherParameters = ECUtil.generatePublicKeyParameter(publicKey); cipherParameters = new ParametersWithID(cipherParameters, DEFAULT_DISTINGUISHING_ID); signer.init(false, cipherParameters); signer.update(message, 0, message.length); return signer.verifySignature(signature); } // ***offlineSM2PKEEncrypt*** // Encrypt data with SM2 public key public static byte[] offlineSM2PKEEncrypt(final PublicKey publicKey, final byte [] plaintext) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, IOException { final Cipher sm2Cipher = Cipher.getInstance("SM2", "BC"); sm2Cipher.init(Cipher.ENCRYPT_MODE, publicKey); // By default, Bouncy Castle returns raw ciphertext in the c1c2c3 format final byte [] cipherText = sm2Cipher.doFinal(plaintext); // Convert the raw ciphertext to the ASN.1 format before passing it to AWS KMS final ASN1EncodableVector asn1EncodableVector = new ASN1EncodableVector(); final int coordinateLength = (SM2_X9EC_PARAMETERS.getCurve().getFieldSize() + 7) / 8 * 2 + 1; final int sm3HashLength = 32; final int xCoordinateInCipherText = 33; final int yCoordinateInCipherText = 65; byte[] coords = new byte[coordinateLength]; byte[] sm3Hash = new byte[sm3HashLength]; byte[] remainingCipherText = new byte[cipherText.length - coordinateLength - sm3HashLength]; // Split components out of the ciphertext System.arraycopy(cipherText, 0, coords, 0, coordinateLength); System.arraycopy(cipherText, cipherText.length - sm3HashLength, sm3Hash, 0, sm3HashLength); System.arraycopy(cipherText, coordinateLength, remainingCipherText, 0,cipherText.length - coordinateLength - sm3HashLength); // Build standard SM2PKE ASN.1 ciphertext vector asn1EncodableVector.add(new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(coords, 1, xCoordinateInCipherText)))); asn1EncodableVector.add(new ASN1Integer(new BigInteger(1, Arrays.copyOfRange(coords, xCoordinateInCipherText, yCoordinateInCipherText)))); asn1EncodableVector.add(new DEROctetString(sm3Hash)); asn1EncodableVector.add(new DEROctetString(remainingCipherText)); return new DERSequence(asn1EncodableVector).getEncoded("DER"); } }

SYMMETRIC_DEFAULT 密钥规范

默认密钥规范 SYMMETRIC_DEFAULT 是对称加密 KMS 密钥的密钥规范。当您在 AWS KMS 控制台中选择Symmetric(对称)密钥类型和Encrypt and decrypt(加密和解密)密钥用法时,它会选择 SYMMETRIC_DEFAULT 密钥规范。在 CreateKey 操作中,如果未指定 KeySpec 值,则会选择 SYMMETRIC_DEFAULT。如果您没有理由使用其他密钥规范,SymMETIC_DEFAULT 是个不错的选择。

SYMMETRIC_DEFAULT 目前代表 AES-256-GCM,这是一种基于伽罗瓦计数器模式 (GCM) 中的高级加密标准 (AES) 的对称算法,具有 256 位密钥,是用于安全加密的行业标准。此算法生成的密文支持附加身份验证数据 (AAD),如加密上下文,且 GCM 对密文提供额外的完整性检查。有关技术详细信息,请参阅 AWS Key Management Service加密详细信息

在 AES-256-GCM 下加密的数据现在和将来都受到保护。密码学家认为这种算法具备抗量子性。理论上,在未来针对使用 256 位 AES-GCM 密钥创建的密文的大规模量子计算攻击中,密钥的有效安全性将会降至 128 位。但是,此安全级别足以对抗对 AWS KMS 密文进行的暴力破解攻击。

唯一的例外是,在中国区域 SYMMETRIC_DEFAULT 表示使用 SM4 加密的 128 位对称密钥。您只能在中国区域内创建 128 位的 SM4 密钥。您无法在中国区域内创建 256 位的 AES-GCM KMS 密钥。

在 AWS KMS 中,可以使用对称加密 KMS 密钥加密、解密和重新加密数据,生成数据密钥和数据密钥对。与 AWS KMS 集成的 AWS 服务使用对称加密 KMS 密钥来加密静态数据。您可以将自己的密钥材料导入对称加密 KMS 密钥中,并在自定义密钥存储中创建对称加密 KMS 密钥。有关可以对对称 KMS 密钥和非对称 KMS 密钥执行的操作的比较表格,请参阅比较对称 KMS 密钥与非对称 KMS 密钥

有关 AWS KMS 和对称加密密钥的技术详细信息,请参阅 AWS Key Management Service 加密详细信息