Exemplo de operações offline
Após baixar a chave pública do seu par de chaves do KMS assimétricas, você poderá compartilhá-lo com outras pessoas e usá-lo para realizar operações offline.
Os logs do AWS CloudTrail que gravam todas as operações do AWS KMS, incluindo solicitação, resposta, data, hora e usuário autorizado, não gravam o uso da chave pública fora do AWS KMS.
Este tópico apresenta exemplos de operações offline e detalhes que as ferramentas do AWS KMS oferecem para facilitar as operações offline.
Tópicos
Derivar segredos compartilhados offline
Você pode baixar a chave pública do seu par de chaves ECC para uso em operações offline, ou seja, operações fora do AWS KMS.
O seguinte passo a passo do OpenSSL
-
Crie um par de chaves ECC no OpenSSL e prepare-o para usar com o AWS KMS.
// Create an ECC key pair in OpenSSL and save the private key in openssl_ecc_key_priv.pem export OPENSSL_CURVE_NAME="P-256" export KMS_CURVE_NAME="ECC_NIST_P256" export OPENSSL_KEY1_PRIV_PEM="openssl_ecc_key1_priv.pem" openssl ecparam -name ${OPENSSL_CURVE_NAME} -genkey -out ${OPENSSL_KEY1_PRIV_PEM} // Derive the public key from the private key export OPENSSL_KEY1_PUB_PEM="openssl_ecc_key1_pub.pem" openssl ec -in ${OPENSSL_KEY1_PRIV_PEM} -pubout -outform pem \ -out ${OPENSSL_KEY1_PUB_PEM} // View the PEM file containing the public key and extract the public key as a // Base64 encoded string into OPENSSL_KEY1_PUB_BASE64 for use with AWS KMS export OPENSSL_KEY1_PUB_BASE64=`cat ${OPENSSL_KEY1_PUB_PEM} | \ tee /dev/stderr | grep -v "PUBLIC KEY" | tr -d "\n"` -
Crie um par de chaves de acordo de chave ECC no AWS KMS e prepare-o para uso com o OpenSSL.
// Create a KMS key on the same curve as the key pair from step 1 // with a key usage of KEY_AGREEMENT // Save its ARN in KMS_KEY1_ARN. export KMS_KEY1_ARN=`aws kms create-key --key-spec ${KMS_CURVE_NAME} \ --key-usage KEY_AGREEMENT | tee /dev/stderr | jq -r .KeyMetadata.Arn` // Download the public key and save the Base64-encoded version in KMS_KEY1_PUB_BASE64 export KMS_KEY1_PUB_BASE64=`aws kms get-public-key --key-id ${KMS_KEY1_ARN} | \ tee /dev/stderr | jq -r .PublicKey` // Create a PEM file for the public KMS key for use with OpenSSL export KMS_KEY1_PUB_PEM="aws_kms_ecdh_key1_pub.pem" echo "-----BEGIN PUBLIC KEY-----" > ${KMS_KEY1_PUB_PEM} echo ${KMS_KEY1_PUB_BASE64} | fold -w 64 >> ${KMS_KEY1_PUB_PEM} echo "-----END PUBLIC KEY-----" >> ${KMS_KEY1_PUB_PEM} -
Derive o segredo compartilhado no OpenSSL usando a chave privada no OpenSSL e a chave pública do KMS.
export OPENSSL_SHARED_SECRET1_BIN="openssl_shared_secret1.bin" openssl pkeyutl -derive -inkey ${OPENSSL_KEY1_PRIV_PEM} \ -peerkey ${KMS_KEY1_PUB_PEM} -out ${OPENSSL_SHARED_SECRET1_BIN}
Verificação off-line com pares de chaves ML-DSA
O AWS KMS é compatível com uma variante protegida da assinatura ML-DSA, conforme descrito no padrão Federal Information Processing Standards (FIPS) 204
Para assinar mensagens de mais de 4 KB, você executa a etapa de pré-processamento de mensagem fora do AWS KMS. Essa etapa de hash cria um μ representativo de mensagem de 64 bytes, conforme definido no NIST FIPS 204, seção 6.2.
O AWS KMS tem um tipo de mensagem denominado EXTERNAL_MU para mensagens de mais de 4 KB. Quando você usa esse tipo em vez do tipo de mensagem RAW, o AWS KMS:
-
Presume que você já tenha executado a etapa de hash
-
Ignora seu próprio processo interno de hash
-
Funciona com mensagens de qualquer tamanho
Quando você verifica uma mensagem, o método usado depende da restrição de tamanho da biblioteca ou sistema externo e ser ou não compatível com o μ representativo de mensagem de 64 bytes:
-
Se a mensagem for menor que a restrição de tamanho, use o tipo de mensagem
RAW. -
Se a mensagem for maior que a restrição de tamanho, use o μ representativo no sistema externo.
As seções a seguir demonstram como assinar mensagens usando o AWS KMS e verificar mensagens usando o OpenSSL. Fornecemos exemplos com mensagens abaixo e acima do limite de tamanho de mensagem de 4 KB imposto pelo AWS KMS. O OpenSSL não impõe um limite no tamanho da mensagem para verificação.
Em ambos os exemplos, primeiro obtenha a chave pública do AWS KMS. Use o seguinte comando AWS CLI:
aws kms get-public-key \ --key-id _<1234abcd-12ab-34cd-56ef-1234567890ab>_ \ --output text \ --query PublicKey | base64 --decode > public_key.der
Tamanho de mensagem menor que 4 KB
Para mensagens de menos de 4 KB, use o tipo de RAW mensagem com o AWS KMS. Embora você possa usar EXTERNAL_MU, isso não é necessário para mensagens dentro do limite de tamanho.
Use o comando AWS CLI a seguir para assinar a mensagem:
aws kms sign \ --key-id _<1234abcd-12ab-34cd-56ef-1234567890ab>_ \ --message 'your message' \ --message-type RAW \ --signing-algorithm ML_DSA_SHAKE_256 \ --output text \ --query Signature | base64 --decode > ExampleSignature.bin
Para verificar essa mensagem usando o OpenSSL, use o seguinte comando:
echo -n 'your message' | ./openssl dgst -verify public_key.der -signature ExampleSignature.bin
Tamanho de mensagem maior que 4 KB
Para assinar mensagens maiores que 4 KB, use o tipo de mensagem EXTERNAL_MU. Quando usa EXTERNAL_MU, você faz externamente o pré-hash da mensagem para um μ representativo de 64 bytes conforme definido no NIST FIPS 204, seção 6.2 e a encaminha para as operações de assinatura ou verificação. Observe que isso é diferente de "MLDSA pré-hash" ou HashML-DSA definido no NIST FIPS 204, seção 5.4.
-
Primeiro, crie um prefixo de mensagem. O prefixo contém um separador de domínio, o tamanho de qualquer contexto e o contexto. O padrão para o separador de domínio e o comprimento de contexto é zero.
-
Anexe o prefixo à mensagem.
-
Use SHAKE256 para fazer o hash da chave pública e anexe-a ao resultado da etapa 2.
-
Por fim, faça um hash no resultado da etapa 3 para gerar o
EXTERNAL_MUde 64 bytes.
O exemplo a seguir usa o OpenSSL 3.5 para criar o: EXTERNAL_MU
{ openssl asn1parse -inform DER -in public_key.der -strparse 17 -noout -out - 2>/dev/null | openssl dgst -provider default -shake256 -xoflen 64 -binary; printf '\x00\x00'; echo -n "your message" } | openssl dgst -provider default -shake256 -xoflen 64 -binary > mu.bin
Depois de criar o arquivo mu.bin, chame a API do AWS KMS com o seguinte comando para assinar a mensagem:
aws kms sign \ --key-id _<1234abcd-12ab-34cd-56ef-1234567890ab>_ \ --message fileb://mu.bin \ --message-type EXTERNAL_MU \ --signing-algorithm ML_DSA_SHAKE_256 \ --output text \ --query Signature | base64 --decode > ExampleSignature.bin
A assinatura resultante é igual à assinatura RAW da mensagem original. Você pode usar o mesmo comando do OpenSSL 3.5 para verificar a mensagem:
echo -n 'your message' | ./openssl dgst -verify public_key.der -signature ExampleSignature.bin
Verificação offline com pares de chaves SM2 (somente nas regiões da China)
Para verificar uma assinatura fora do AWS KMS com uma chave pública SM2, é necessário especificar o ID distintivo. Quando você encaminha uma mensagem raw, MessageType:RAW, para a API Sign, o AWS KMS usa o ID distintivo padrão 1234567812345678, definido pela OSCCA em GM/T 0009-2012. Você não pode especificar seu próprio ID de distinção no AWS KMS.
Porém, se estiver gerando um resumo de mensagens fora do AWS, você poderá especificar seu próprio ID distintivo e, em seguida, transmitir o resumo de mensagem, MessageType:DIGEST, para o AWS KMS assinar. Para isso, altere o valor de DEFAULT_DISTINGUISHING_ID na classe SM2OfflineOperationHelper. O ID distintivo especificado pode ser qualquer string de até 8.192 caracteres. Depois que o AWS KMS assinar o resumo de mensagem, você precisará desse resumo de mensagem ou da mensagem e do ID distintivo utilizados no cálculo do resumo para verificá-lo offline.
Importante
O código de referência SM2OfflineOperationHelper foi projetado para ser compatível com o Bouncy Castle
Classe SM2OfflineOperationHelper
Para ajudar com essas operações offline usando chaves SM2, a classe SM2OfflineOperationHelper para Java tem métodos que realizam as tarefas para você. Essa classe auxiliar pode ser usada como modelo para outros provedores criptográficos.
No AWS KMS, as conversões de texto cifrado bruto e os cálculos de resumos de mensagem SM2DSA ocorrem automaticamente. Nem todos os provedores criptográficos implementam o SM2 da mesma maneira. Algumas bibliotecas, como a OpenSSLSM2OfflineOperationHelper com bibliotecas, como a Bouncy Castle
A classe SM2OfflineOperationHelper fornece métodos para as seguintes operações:
-
- Cálculo de resumo de mensagem
-
Para gerar um resumo de mensagem offline para uso em uma verificação offline ou para transmissão ao AWS KMS para assinatura, use o método
calculateSM2Digest. O métodocalculateSM2Digestgera um resumo de mensagem com o algoritmo de hash SM3. A API GetPublicKey retorna sua chave pública em um formato binário. Você deve analisar a chave binária em uma PublicKey Java. Forneça a chave pública analisada com a mensagem. O método combina automaticamente sua mensagem com o ID distintivo padrão,1234567812345678, mas você pode definir seu próprio ID distintivo alterando o valor deDEFAULT_DISTINGUISHING_ID.
-
- Verificar
-
Para verificar uma assinatura offline, use o método
offlineSM2DSAVerify. O métodoofflineSM2DSAVerifyusa o resumo de mensagem calculado com base no ID distintivo especificado e a mensagem original fornecida para verificar a assinatura digital. A API GetPublicKey retorna sua chave pública em um formato binário. Você deve analisar a chave binária em uma PublicKey Java. Forneça a chave pública analisada com a mensagem original e a assinatura que você deseja verificar. Para obter mais detalhes, consulte Verificação offline com pares de chaves SM2.
-
- Encrypt
-
Para criptografar texto sem formatação offline, use o método
offlineSM2PKEEncrypt. Ele garante que o texto cifrado esteja em um formato que o AWS KMS possa descriptografar. O métodoofflineSM2PKEEncryptcriptografa o texto simples e depois converte o texto cifrado bruto produzido por SM2PKE no formato ASN.1. A API GetPublicKey retorna sua chave pública em um formato binário. Você deve analisar a chave binária em uma PublicKey Java. Forneça a chave pública analisada com o texto simples que você deseja criptografar.Se não tiver certeza de que a conversão é necessário, use a seguinte operação OpenSSL para testar o formato do texto cifrado. Se a operação falhar, significa que você precisa converter o texto cifrado no formato ASN.1.
openssl asn1parse -inform DER -inciphertext.der
Por padrão, a classe SM2OfflineOperationHelper usa o ID distintivo padrão, 1234567812345678, ao gerar resumos de mensagens para operações SM2DSA.
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"); } }