

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Ejemplo de operaciones sin conexión
<a name="offline-operations"></a>

Tras [descargar la clave pública](download-public-key.md) de su par de claves KMS asimétricas, puede compartirla con otras personas y utilizarla para realizar operaciones sin conexión.

AWS CloudTrail Los registros que registran todas las AWS KMS operaciones, incluidas la solicitud, la respuesta, la fecha, la hora y el usuario autorizado, no registran el uso de la clave pública fuera de ella. AWS KMS

En este tema se proporcionan ejemplos de operaciones sin conexión y se detallan las herramientas que AWS KMS proporcionan para facilitar las operaciones sin conexión.

**Topics**
+ [Obtención de secretos compartidos sin conexión](#key-spec-ecc-offline)
+ [Verificación fuera de línea con pares de claves ML-DSA](#mldsa-offline-verification)
+ [Verificación fuera de línea con pares de SM2 claves (solo en las regiones de China)](#key-spec-sm-offline-verification)

## Obtención de secretos compartidos sin conexión
<a name="key-spec-ecc-offline"></a>

Puede [descargar la clave pública](download-public-key.md) del par de claves ECC para uso en operaciones sin conexión, es decir, operaciones fuera de AWS KMS.

En el siguiente tutorial de [OpenSSL](https://openssl.org/) se muestra un método para obtener un secreto compartido sin utilizar la clave pública de un par AWS KMS de claves KMS ECC y una clave privada creada con OpenSSL.

1. Cree un key pair ECC en OpenSSL y prepárelo para usarlo con él. 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"`
   ```

1. Cree un par de claves de acuerdo de claves ECC AWS KMS y prepárelo para su uso con 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}
   ```

1. Obtenga el secreto compartido en OpenSSL mediante la clave privada de OpenSSL y la clave pública de 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}
   ```

## Verificación fuera de línea con pares de claves ML-DSA
<a name="mldsa-offline-verification"></a>

AWS KMS admite una variante protegida de la firma ML-DSA, tal como se describe en la sección 3.4 de la [norma federal de procesamiento de información (FIPS) 204](https://csrc.nist.gov/pubs/fips/204/final) para mensajes de hasta 4 KB de bytes.

Para firmar mensajes de más de 4 KB, realice el paso de preprocesamiento del mensaje fuera de. AWS KMS Este paso de uso de hash crea una μ representativa del mensaje de 64 bytes, tal como se define en la sección 6.2 de NIST FIPS 204.

AWS KMS tiene un tipo de mensaje denominado `EXTERNAL_MU` para mensajes de más de 4 KB. Si usa esto en lugar del tipo de `RAW` mensaje, AWS KMS:
+ Asume que ya ha realizado el paso de uso de hash
+ Omite su proceso de hash interno
+ Funciona con mensajes de cualquier tamaño

Al verificar un mensaje, el método que se utilice dependerá de la restricción de tamaño del sistema o de la biblioteca externos y de si admite la μ representativa del mensaje de 64 bytes:
+ Si el mensaje es de menor tamaño que la restricción de tamaño, utilice el tipo de mensaje `RAW`.
+ Si el mensaje es mayor que la restricción de tamaño, utilice la μ representativa en el sistema externo.

En las siguientes secciones se muestra cómo firmar mensajes con OpenSSL AWS KMS y cómo verificarlos. Proporcionamos ejemplos de mensajes con un tamaño inferior o superior al límite de 4 KB impuesto por. AWS KMS OpenSSL no impone un límite al tamaño de los mensajes para su verificación.

Para ambos ejemplos, primero obtenga la clave pública de AWS KMS. Utilice el siguiente comando AWS CLI :

```
aws kms get-public-key \
    --key-id _<1234abcd-12ab-34cd-56ef-1234567890ab>_ \
    --output text \
    --query PublicKey | base64 --decode > public_key.der
```

### Tamaño del mensaje inferior a 4 KB
<a name="mldsa-offline-verification-less-than-4KB"></a>

Para mensajes de menos de 4 KB, usa el tipo de `RAW` mensaje con AWS KMS. Si bien puede usar `EXTERNAL_MU`, no es necesario para los mensajes que estén dentro del límite de tamaño.

Use el siguiente AWS CLI comando para firmar el mensaje:

```
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 el mensaje mediante OpenSSL utilice el siguiente comando:

```
echo -n 'your message' | ./openssl dgst -verify public_key.der -signature ExampleSignature.bin
```

### Tamaño del mensaje superior a 4 KB
<a name="mldsa-offline-verification-more-than-4KB"></a>

Para firmar mensajes de más de 4 KB, utilice el tipo de mensaje `EXTERNAL_MU`. Cuando utilice `EXTERNAL_MU`, codifique con hash previamente el mensaje de forma externa en una μ representativa de 64 bytes, tal como se define en la sección 6.2 de NIST FIPS 204, y lo transfiere a las operaciones de firma o verificación. Tenga en cuenta que es diferente del “MLDSA previo al hash” o del HASHML-DSA definido en la sección 5.4 de NIST FIPS 204. 

1. En primer lugar, cree un prefijo de mensaje. El prefijo contiene un separador de dominios, la longitud de cualquier contexto y el contexto. El valor predeterminado para el separador de dominios y la longitud del contexto es cero.

1. Anexe el prefijo del mensaje al mensaje.

1. Se usa SHAKE256 para codificar la clave pública y anteponerla al resultado del paso 2.

1. Por último, haga un hash del resultado del paso 3 para obtener un `EXTERNAL_MU` 64 bytes.

El siguiente ejemplo utiliza OpenSSL 3.5 para crear `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
```

Tras crear el `mu.bin` archivo, llama a la AWS KMS API con el siguiente comando para firmar el mensaje:

```
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
```

La firma resultante es la misma que la firma `RAW` del mensaje original. Puede usar el mismo comando de OpenSSL 3.5 para verificar el mensaje:

```
echo -n 'your message' | ./openssl dgst -verify public_key.der -signature ExampleSignature.bin
```

## Verificación fuera de línea con pares de SM2 claves (solo en las regiones de China)
<a name="key-spec-sm-offline-verification"></a>

Para verificar una firma fuera o AWS KMS con una clave SM2 pública, debes especificar el identificador distintivo. Al pasar un mensaje sin procesar [https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html#KMS-Sign-request-MessageType](https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html#KMS-Sign-request-MessageType), a la API de [firma](https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html), AWS KMS utiliza el identificador distintivo predeterminado`1234567812345678`, definido por la OSCCA en 0009-2012. GM/T No puede especificar su propio identificador distintivo en AWS KMS.

Sin embargo, si vas a generar un resumen del mensaje fuera de AWS, puedes especificar tu propio identificador distintivo y, a continuación, pasarle el resumen del mensaje a, para que lo firme. [https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html#API_Sign_RequestSyntax](https://docs.aws.amazon.com/kms/latest/APIReference/API_Sign.html#API_Sign_RequestSyntax) AWS KMS Para ello, cambie el valor `DEFAULT_DISTINGUISHING_ID` en la clase `SM2OfflineOperationHelper`. El identificador distintivo que especifique puede ser cualquier cadena de hasta 8192 caracteres. Después de AWS KMS firmar el resumen del mensaje, necesitará el resumen del mensaje o el mensaje y el identificador distintivo utilizados para calcular el resumen para verificarlo sin conexión.

**importante**  
El código de referencia `SM2OfflineOperationHelper` está diseñado para ser compatible con [Bouncy Castle](https://www.bouncycastle.org/documentation/documentation-java/) versión 1.68. Para obtener ayuda con otras versiones, póngase en contacto con [bouncycastle.org](https://www.bouncycastle.org).

### Clase `SM2OfflineOperationHelper`
<a name="key-spec-sm-offline-helper"></a>

Para ayudarle a realizar operaciones con SM2 claves fuera de línea, la `SM2OfflineOperationHelper` clase de Java tiene métodos que realizan las tareas por usted. Puede usar esta clase auxiliar como modelo para otros proveedores de cifrado.

En su interior AWS KMS, las conversiones de texto cifrado sin procesar y los cálculos del resumen de los mensajes de la SM2 DSA se realizan automáticamente. No todos los proveedores de criptografía lo implementan de la misma SM2 manera. Algunas bibliotecas, como las versiones 1.1.1 y posteriores de [OpenSSL](https://openssl.org/), realizan estas acciones automáticamente. AWS KMS confirmó este comportamiento en las pruebas con OpenSSL versión 3.0. Utilice la siguiente clase de `SM2OfflineOperationHelper` con bibliotecas, como [Bouncy Castle](https://www.bouncycastle.org/java.html), que requieren que realice estas conversiones y cálculos manualmente.

La clase `SM2OfflineOperationHelper` proporciona métodos para las siguientes operaciones fuera de línea:
+   
**Cálculo del resumen del mensaje**  
Para generar un resumen de mensajes sin conexión que puedas usar para la verificación sin conexión o al que puedas pasar AWS KMS para firmar, usa este método. `calculateSM2Digest` El `calculateSM2Digest` método genera un resumen del mensaje con el algoritmo SM3 de hash. La [GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html)API devuelve la clave pública en formato binario. Debe analizar la clave binaria para convertirla en una versión Java PublicKey. Proporcione la clave pública analizada con el mensaje. El método combina automáticamente el mensaje con el identificador distintivo predeterminado, `1234567812345678`, pero puede establecer su propio identificador distintivo cambiando el valor `DEFAULT_DISTINGUISHING_ID`.
+   
**Verificar**  
Para verificar una firma sin conexión, utilice el método `offlineSM2DSAVerify`. El método `offlineSM2DSAVerify` utiliza el resumen del mensaje calculado a partir del identificador distintivo especificado y el mensaje original que proporciona para verificar la firma digital. La [GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html)API devuelve la clave pública en formato binario. Debe analizar la clave binaria para convertirla en una versión Java PublicKey. Proporcione la clave pública analizada con el mensaje original y la firma que desea verificar. Para obtener más información, consulte [Verificación sin conexión con pares de SM2 claves](#key-spec-sm-offline-verification).
+   
**Encrypt**  
Para cifrar texto sin formato sin conexión, utilice el método `offlineSM2PKEEncrypt`. Este método garantiza que el texto cifrado esté en un formato que se AWS KMS pueda descifrar. El `offlineSM2PKEEncrypt` método cifra el texto sin formato y, a continuación, convierte el texto cifrado sin procesar producido por PKE al formato ASN.1. SM2 La [GetPublicKey](https://docs.aws.amazon.com/kms/latest/APIReference/API_GetPublicKey.html)API devuelve la clave pública en formato binario. Debe analizar la clave binaria para convertirla en una versión Java PublicKey. Proporcione la clave pública analizada con el texto sin formato que desea cifrar.  
Si no está seguro de si necesita realizar la conversión, utilice la siguiente operación de OpenSSL para probar el formato del texto cifrado. Si la operación falla, debe convertir el texto cifrado al formato ASN.1.  

  ```
  openssl asn1parse -inform DER -in ciphertext.der
  ```

De forma predeterminada, la `SM2OfflineOperationHelper` clase utiliza el identificador distintivo predeterminado al generar resúmenes de mensajes para las operaciones de SM2 DSA. `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");
    }
}
```