기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.
예제 오프라인 작업
비대칭 KMS 키 페어의 퍼블릭 키를 다운로드한 후 다른 사람과 공유하고 이를 사용하여 오프라인 작업을 수행할 수 있습니다.
AWS CloudTrail 요청, 응답, 날짜, 시간 및 승인된 사용자를 포함하여 모든 AWS KMS 작업을 기록하는 로그는 외부의 퍼블릭 키 사용을 기록하지 않습니다 AWS KMS.
이 주제에서는 오프라인 작업의 예를 제공하고 오프라인 작업을 더 쉽게 하기 위해 도구가 AWS KMS 제공하는 세부 정보를 제공합니다.
공유 보안 비밀을 오프라인으로 도출
오프라인 작업, 즉 AWS KMS외부의 작업에 사용하는 ECC 키 페어의 퍼블릭 키를 다운로드할 수 있습니다.
다음 OpenSSL
-
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"`
-
에서 ECC 키 계약 키 페어를 생성하고 OpenSSL과 함께 사용할 AWS KMS 준비를 합니다.
// 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}
-
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}
ML-DSA 키 페어를 사용한 오프라인 확인
AWS KMS 는 최대 4KB 바이트의 메시지에 대해 연방 정보 처리 표준(FIPS) 204 표준
4KB보다 큰 메시지에 서명하려면 외부에서 메시지 사전 처리 단계를 수행합니다 AWS KMS. 이 해싱 단계는 NIST FIPS 204, 섹션 6.2에 정의된 대로 64바이트 메시지 대표 μ를 생성합니다.
AWS KMS 에는 4KB보다 큰 메시지에 EXTERNAL_MU
대해 라는 메시지 유형이 있습니다. RAW
메시지 유형 대신 이를 사용하는 경우 AWS KMS:
-
해싱 단계를 이미 수행했다고 가정합니다.
-
내부 해싱 프로세스 건너뛰기
-
모든 크기의 메시지와 함께 작동
메시지를 확인할 때 사용하는 방법은 외부 시스템 또는 라이브러리의 크기 제한과 64바이트 메시지 대표 μ를 지원하는지 여부에 따라 달라집니다.
-
메시지가 크기 제한보다 작으면
RAW
메시지 유형을 사용합니다. -
메시지가 크기 제한보다 큰 경우 외부 시스템에서 대표 μ를 사용합니다.
다음 섹션에서는를 사용하여 메시지에 서명하고 OpenSSL을 사용하여 메시지를 AWS KMS 확인하는 방법을 보여줍니다. 에서 부과한 4KB 메시지 크기 제한 미만 및 초과 메시지 모두에 대한 예를 제공합니다 AWS KMS. OpenSSL은 확인을 위해 메시지 크기에 제한을 두지 않습니다.
두 예제 모두 먼저 퍼블릭 키를 가져옵니다 AWS KMS. 다음 AWS CLI 명령을 사용합니다.
aws kms get-public-key \ --key-id _<1234abcd-12ab-34cd-56ef-1234567890ab>_ \ --output text \ --query PublicKey | base64 --decode > public_key.der
메시지 크기가 4KB 미만
4KB 미만의 메시지의 경우 RAW
메시지 유형을와 함께 사용합니다 AWS KMS. 를 사용할 수 있지만 크기 제한 내의 메시지에는 필요하지 EXTERNAL_MU
않습니다.
다음 AWS CLI 명령을 사용하여 메시지에 서명합니다.
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
OpenSSL을 사용하여이 메시지를 확인하려면 다음 명령을 사용합니다.
echo -n 'your message' | ./openssl dgst -verify public_key.der -signature ExampleSignature.bin
메시지 크기가 4KB를 초과함
4KB보다 큰 메시지에 서명하려면 EXTERNAL_MU
메시지 유형을 사용합니다. 를 사용하는 경우 NIST FIPS 204 섹션 6.2에 정의된 대로 64바이트 대표 μ 외부에서 메시지를 EXTERNAL_MU
미리 해시하고 서명 또는 확인 작업에 전달합니다. 이는 NIST FIPS 204 섹션 5.4에 정의된 "사전 해시 MLDSA" 또는 HashML-DSA와 다릅니다.
-
먼저 메시지 접두사를 구성합니다. 접두사에는 도메인 구분자, 모든 컨텍스트의 길이 및 컨텍스트가 포함됩니다. 도메인 구분자 및 컨텍스트 길이의 기본값은 0입니다.
-
메시지 접두사를 메시지에 추가합니다.
-
SHAKE256을 사용하여 퍼블릭 키를 해시하고 2단계의 결과에 추가합니다.
-
마지막으로 3단계의 결과를 해시하여 64바이트를 생성합니다
EXTERNAL_MU
.
다음 예제에서는 OpenSSL 3.5를 사용하여를 구성합니다. 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
mu.bin
파일을 생성한 후 다음 명령을 사용하여 AWS KMS API를 호출하여 메시지에 서명합니다.
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
결과 서명은 원본 메시지의 RAW
서명과 동일합니다. 동일한 OpenSSL 3.5 명령을 사용하여 메시지를 확인할 수 있습니다.
echo -n 'your message' | ./openssl dgst -verify public_key.der -signature ExampleSignature.bin
SM2 키 페어를 사용한 오프라인 인증(중국 리전 전용)
SM2 퍼블릭 키를 AWS KMS 사용하여 외부의 서명을 확인하려면 구분 ID를 지정해야 합니다. 원시 메시지를 Sign APIMessageType:RAW
에 전달하면는 GM/T 0009-2012에서 OSCCA에서 정의한 기본 구분 ID 1234567812345678
를 AWS KMS 사용합니다. AWS KMS내에서 구분 ID를 지정할 수 없습니다.
그러나 외부에서 메시지 다이제스트를 생성하는 경우 고유한 구분 ID를 지정한 다음 메시지 다이제스트를에 전달MessageType:DIGEST
하여 서명 AWS KMS 할 AWS수 있습니다. 이를 위해서는 SM2OfflineOperationHelper
클래스에서 DEFAULT_DISTINGUISHING_ID
값을 변경합니다. 구분 ID는 최대 8,192자 이내의 문자열로 지정할 수 있습니다. 가 메시지 다이제스트에 AWS KMS 서명한 후에는 메시지 다이제스트 또는 메시지와 다이제스트를 계산하여 오프라인으로 확인하는 데 사용되는 구분 ID가 필요합니다.
중요
이 SM2OfflineOperationHelper
참조 코드는 Bouncy Castle
SM2OfflineOperationHelper
클래스
SM2 키를 사용하는 오프라인 작업을 돕기 위해 Java용 SM2OfflineOperationHelper
클래스에는 이 태스크를 수행하는 메서드가 있습니다. 이 도우미 클래스를 다른 암호화 공급자의 모델로 사용할 수 있습니다.
내에서 AWS KMS원시 사이퍼텍스트 변환 및 SM2DSA 메시지 다이제스트 계산은 자동으로 수행됩니다. 모든 암호화 공급자가 동일한 방식으로 SM2를 구현하는 것은 아닙니다. OpenSSLSM2OfflineOperationHelper
클래스를 사용하여 이러한 변환과 계산을 수작업으로 수행해야 합니다.
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로 파싱해야 합니다. 파싱된 공개 키에 암호화하려는 일반 텍스트를 제공합니다.변환을 수행해야 하는지 확신하기 어려운 경우, 다음 OpenSSL 작업을 사용하여 사이퍼텍스트의 형식을 테스트합니다. 작업이 실패할 경우, 사이퍼텍스트를 ASN.1 형식으로 변환합니다.
openssl asn1parse -inform DER -in
ciphertext.der
기본적으로 SM2OfflineOperationHelper
클래스는 SM2DSA 작업에 대한 메시지 다이제스트를 생성할 때 기본 구분 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"); } }