예제 오프라인 작업 - AWS Key Management Service

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

예제 오프라인 작업

비대칭 키 페어의 퍼블릭 키를 다운로드한 후 다른 사용자와 공유하여 오프라인 작업을 수행할 수 있습니다. KMS

AWS CloudTrail 요청, 응답, 날짜, 시간 및 권한 있는 사용자를 포함하여 모든 AWS KMS 작업을 기록하는 로그는 외부에서 퍼블릭 키 사용을 기록하지 않습니다 AWS KMS.

이 주제에서는 오프라인 작업의 예를 제공하고 오프라인 작업을 더 쉽게 하기 위해 도구가 AWS KMS 제공하는 세부 정보를 제공합니다.

공유 보안 비밀을 오프라인으로 도출

오프라인 작업, 즉 외부 작업에 사용할 수 있도록 ECC 키 페어의 퍼블릭 키를 다운로드할 수 있습니다 AWS KMS.

다음 OpenSSL 연습에서는 ECC KMS 키 페어의 퍼블릭 키와 Open으로 생성된 프라이빗 키를 AWS KMS 사용하지 않고 공유 암호를 도출하는 한 가지 방법을 보여줍니다SSL.

  1. OpenSSL에서 ECC 키 페어를 생성하고 함께 사용할 준비를 합니다 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"`
  2. 에서 ECC 키 계약 키 페어를 생성하고 Open과 함께 사용할 AWS KMS 준비를 합니다SSL.

    // 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}
  3. OpenSSL의 프라이빗 키와 퍼블릭 KMS 키를 사용하여 OpenSSL의 공유 암호를 도출합니다.

    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}

SM2 키 페어를 사용한 오프라인 확인(중국 리전만 해당)

SM2 퍼블릭 키를 AWS KMS 사용하여 외부의 서명을 확인하려면 구별되는 ID를 지정해야 합니다. 원시 메시지를 서명 MessageType:RAW에 전달하면는 GM/T 0009-2012OSCCA에서에 의해 정의된 기본 구분 ID 1234567812345678를 API AWS KMS 사용합니다. AWS KMS내에서 구분 ID를 지정할 수 없습니다.

그러나 외부에서 메시지 다이제스트를 생성하는 경우 고유한 식별 ID를 지정한 다음 메시지 다이제스트 MessageType:DIGEST를에 전달하여 서명 AWS KMS 할 AWS수 있습니다. 이를 위해서는 SM2OfflineOperationHelper 클래스에서 DEFAULT_DISTINGUISHING_ID 값을 변경합니다. 구분 ID는 최대 8,192자 이내의 문자열로 지정할 수 있습니다. 메시지 다이제스트에 AWS KMS 서명한 후에는 메시지 다이제스트 또는 메시지와 다이제스트를 계산하여 오프라인으로 확인하는 데 사용되는 구분 ID가 필요합니다.

중요

SM2OfflineOperationHelper 참조 코드는 Bouncy Castle 버전 1.68과 호환되도록 설계되었습니다. 다른 버전에 대한 도움말은 bouncycastle.org를 참조하세요.

SM2OfflineOperationHelper 클래스

SM2 키를 사용한 오프라인 작업을 지원하기 위해 Java용 SM2OfflineOperationHelper 클래스에는 작업을 수행하는 메서드가 있습니다. 이 도우미 클래스를 다른 암호화 공급자의 모델로 사용할 수 있습니다.

내에서 AWS KMS원시 사이퍼텍스트 변환 및 SM2DSA 메시지 다이제스트 계산은 자동으로 수행됩니다. 모든 암호화 공급자가 동일한 SM2 방식으로 구현하는 것은 아닙니다. OpenSSL 버전 1.1.1 이상과 같은 일부 라이브러리는 이러한 작업을 자동으로 수행합니다.는 OpenSSL 버전 3.0을 사용하여 테스트할 때이 동작을 AWS KMS 확인했습니다. Bouncy Castle과 같은 라이브러리와 함께 다음 SM2OfflineOperationHelper 클래스를 사용하여 이러한 변환과 계산을 수작업으로 수행해야 합니다.

SM2OfflineOperationHelper 클래스는 다음 오프라인 작업에 대한 메서드를 제공합니다.

  • 메시지 다이제스트 계산

    오프라인 확인에 사용할 수 있거나 서명에 전달할 수 있는 메시지 다이제스트를 오프라인 AWS KMS 에서 생성하려면 calculateSM2Digest 메서드를 사용합니다. calculateSM2Digest 메서드는 SM3해싱 알고리즘을 사용하여 메시지 다이제스트를 생성합니다. 는 퍼블릭 키를 이진 형식으로 GetPublicKey API 반환합니다. 바이너리 키를 Java 로 구문 분석해야 합니다 PublicKey. 파싱된 퍼블릭 키에 메시지를 제공합니다. 이 방법은 메시지와 기본 구분 ID, 1234567812345678를 자동으로 결합하지만 DEFAULT_DISTINGUISHING_ID 값을 변경하면 자체적인 구분 ID를 설정할 수 있습니다.

  • Verify

    오프라인에서 서명을 인증하려면 offlineSM2DSAVerify 메서드를 사용하세요. offlineSM2DSAVerify 메서드는 지정된 구분 ID에서 계산된 메시지 다이제스트와 디지털 서녕 인증에 제공하는 원본 메시지를 사용합니다. 는 퍼블릭 키를 이진 형식으로 GetPublicKey API 반환합니다. 바이너리 키를 Java 로 구문 분석해야 합니다 PublicKey. 파싱된 공개 키에 원본 메시지와 인증하려는 서명을 제공합니다. 자세한 내용은 SM2 키 페어를 사용한 오프라인 확인을 참조하세요.

  • 암호화

    일반 텍스트를 오프라인에서 암호화하려면 offlineSM2PKEEncrypt 메서드를 사용합니다. 이 방법을 사용하면 암호 텍스트가 형식으로 되어 있어 암호를 해독 AWS KMS 할 수 있습니다. offlineSM2PKEEncrypt 메서드는 일반 텍스트를 암호화한 다음에서 생성된 원시 사이퍼텍스트SM2PKE를 ASN.1 형식으로 변환합니다. 는 퍼블릭 키를 이진 형식으로 GetPublicKey API 반환합니다. 바이너리 키를 Java 로 구문 분석해야 합니다 PublicKey. 파싱된 공개 키에 암호화하려는 일반 텍스트를 제공합니다.

    변환을 수행해야 하는지 확실하지 않은 경우 다음 열기SSL 작업을 사용하여 암호 텍스트의 형식을 테스트합니다. 작업이 실패하면 사이퍼텍스트를 ASN.1 형식으로 변환해야 합니다.

    openssl asn1parse -inform DER -in ciphertext.der

기본적으로 SM2OfflineOperationHelper 클래스는 SM2DSA 작업에 대한 메시지 다이제스트를 생성할 때 기본 구분 ID1234567812345678인를 사용합니다.

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"); } }