为什么AWS KMS的JavaSDK的解密功能不需要加密上下文?

Why does decryption function of Java SDK of AWS KMS does not require an encryption context?

From the description of AWS

When an encryption context is provided in an encryption request, it is cryptographically bound to the ciphertext such that the same encryption context is required to decrypt (or decrypt and re-encrypt) the data. If the encryption context provided in the decryption request is not an exact, case-sensitive match, the decrypt request fails. Only the order of the encryption context pairs can vary.

但是in the example code of JAVA SDK,解密时没有指定加密上下文

crypto.decryptString(prov, ciphertext);

这两个帖子对我来说听起来有点矛盾,因为我认为解密用户需要自己提供加密上下文。我查看了sdk中AwsCrypto.decryptString(final CryptoMaterialsManager provider, final String ciphertext))的源码,密文中似乎也包含了加密上下文。

请问为什么要这样设置?

经过一番研究,我发现至少有两种方式可以进行加密和解密。如果有人感兴趣,就 post 他们在这里。它是用 kotlin 编写的。

class AwsEncryptionSDKWrapper(private val keyIdArn: String, region: String) {
    private val crypto = AwsCrypto()
    private val prov: KmsMasterKeyProvider = KmsMasterKeyProvider.builder().withDefaultRegion(region).withKeysForEncryption(keyIdArn).build()

    fun encrypt(raw: String, encryptionContext: Map<String, String>): String {
        return crypto.encryptString(prov, raw, encryptionContext).result
    }

    fun decrypt(cipherText: String, encryptionContext: Map<String, String>): String {
        val decryptedResponse = crypto.decryptString(prov, cipherText)
        if (decryptedResponse.masterKeyIds[0] != keyIdArn) {
            throw IllegalStateException("Wrong key ID!")
        }

        encryptionContext.entries.forEach { (key, value) ->
            if (value != decryptedResponse.encryptionContext[key]) {
                throw IllegalStateException("Wrong Encryption Context!")
            }
        }
        return decryptedResponse.result
    }
}

class AwsKMSSDKWrapper(region: String) {
    private val client = AWSKMSClientBuilder.standard().withRegion(region).build()

    fun encrypt(keyIdArn: String, raw: String, encryptionContext: Map<String, String>): String {
        val plaintextBytes = raw.toByteArray(StandardCharsets.UTF_8)

        val encReq = EncryptRequest()
        encReq.keyId = keyIdArn
        encReq.plaintext = ByteBuffer.wrap(plaintextBytes)
        encReq.withEncryptionContext(encryptionContext)
        val cipherText = client.encrypt(encReq).ciphertextBlob

        return Base64.getEncoder().encodeToString(cipherText.array())
    }

    fun decrypt(base64CipherText: String, encryptionContext: Map<String, String>, keyIdArn: String): String {
        val req = DecryptRequest()
            .withCiphertextBlob(ByteBuffer.wrap(Base64.getDecoder().decode(base64CipherText)))
            .withEncryptionContext(encryptionContext)

        val resp = client.decrypt(req)
        if (resp.keyId == null || resp.keyId!!.contentEquals(keyIdArn))  throw IllegalStateException("keyid not match ! provided $keyIdArn, actual ${resp.keyId}");

        return resp.plaintext.array().toString(StandardCharsets.UTF_8)
    }
}

特别感谢@kdgregory 指出了我的困惑。