4

目前正在从 Java 13 升级到 Java 16,并为执行加密/解密的加密库使用以下更新的依赖项:

  1. 充气城堡 1.69
  2. 谷歌 Tink 1.6.1

==================================

加密库类:

package com.decryption.test;

@NoArgsConstructor
@Service
public class CryptoLib {

    protected static final String PROTOCOL_VERSION = "ECv2";

    protected final String DEV_ROOT_PUB_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESXy7kqanQwAM/HBTcV0MVtgQkKLY6UVqP3Z/vdlxiRgFqnc9dZUSD8muUpgeZDD05lM68qoI31mbeX9c8P/9Uw==";

    protected static final String MERCHANT_ID_PREFIX = "merchant:";

    protected static final String HMAC_SHA256_ALGO = "HmacSha256";

    protected static final int HMAC_SHA256_ALGO_SIZE = 256 / 8; // 256 bit key size -> 32 bytes

    protected static final String AES_CTR_ALGO = "AES/CTR/NoPadding";

    protected static final int AES_CTR_ALGO_SIZE = 256 / 8; // 256 bit key size -> 32 bytes

    protected static final byte[] HKDF_EMPTY_SALT = new byte[0];

    protected static final byte[] AES_CTR_ZERO_IV = new byte[16];

    protected final static String ASYMMETRIC_KEY_TYPE = "EC";

    protected static final String SENDER_ID = "Google";

    protected static final String SECURITY_PROVIDER = "BC";

    protected static final String EC_CURVE = "prime256v1";
    protected static final String CSR_SIGNATURE = "SHA256WITHECDSA";
    protected static final String SIGNATURE_ALGORITHM = "SHA256withECDSA";

    @Autowired
    private ProfileManager profileManager;

    @PostConstruct
    public void loadKeys() {
        final StringBuilder message = new StringBuilder("Loading Google pay signing keys. ");
        if (this.profileManager.isProfileActive("dev")) {
            message.append("Profile DEV is active. Only test rig encrypted payloads supported.");
        } else {
            final GooglePaymentsPublicKeysManager googleKeyManager = this.getGoogleKeyManager();
            googleKeyManager.refreshInBackground();
        }
        System.out.println(message);
    }

    private static String getMerchantIdComponent(final String merchantId) {
        return merchantId.startsWith(MERCHANT_ID_PREFIX) ?
                merchantId : MERCHANT_ID_PREFIX + merchantId;
    }


    private GooglePaymentsPublicKeysManager getGoogleKeyManager() {
        return profileManager.isProfileActive("prod") ?
                GooglePaymentsPublicKeysManager.INSTANCE_PRODUCTION :
                GooglePaymentsPublicKeysManager.INSTANCE_TEST;
    }

    public String decrypt(final GoogleDecryptRequest decryptRequest) throws Exception {
        try {
            final PaymentMethodTokenRecipient.Builder builder = new PaymentMethodTokenRecipient.Builder()
                    .protocolVersion(PROTOCOL_VERSION)
                    .recipientId(getMerchantIdComponent(decryptRequest.getMerchantId()));

            if (decryptRequest.getPrivateKey() != null) {
                builder.addRecipientPrivateKey(decryptRequest.getPrivateKey());
            }

            //Add all our retired keys to the list.
            for (final ECPrivateKey ecPrivateKey : decryptRequest.getOldPrivateKeys()) {
                builder.addRecipientPrivateKey(ecPrivateKey);
            }

            if (this.profileManager.isProfileActive("dev")) {
                builder.addSenderVerifyingKey(DEV_ROOT_PUB_KEY);
            } else {
                builder.fetchSenderVerifyingKeysWith(getGoogleKeyManager());
            }

            return builder.build()
                    .unseal(decryptRequest.getEncryptedMessage());
        } catch (final Exception e) {
            final String errMsg = MessageFormat.format("GooglePay Decryption failed for Google merchant ID [{0}]", decryptRequest.getMerchantId());
            throw new Exception(errMsg, e);
        }
    }


    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    static class GoogleEncryptRequest implements EncryptRequest<ECPublicKey> {

        /**
         * Merchant public key
         */
        private ECPublicKey publicKey;

        private String algorithm = "EC";

        /**
         * Google merchantID. If the merchant ID is xxx, this value should be either
         * <ul>
         *     <li>xxx</li>
         *     <li>merchant:xxx</li>
         * </ul>
         */
        private String merchantId;
        /**
         * Message to encrypt
         */
        private String message;
    }

    public EncryptResponse encrypt(final GoogleEncryptRequest encryptRequest) throws Exception {

        final String DEV_ROOT_PRIV_KEY = "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg3lO35/XiUzEooUJlLKEd0BJmoLgeTvkFSm/b4wMrgdWgCgYIKoZIzj0DAQehRANCAARJfLuSpqdDAAz8cFNxXQxW2BCQotjpRWo/dn+92XGJGAWqdz11lRIPya5SmB5kMPTmUzryqgjfWZt5f1zw//1T";
        final String DEV_ROOT_PUB_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESXy7kqanQwAM/HBTcV0MVtgQkKLY6UVqP3Z/vdlxiRgFqnc9dZUSD8muUpgeZDD05lM68qoI31mbeX9c8P/9Uw==";

        final GoogleEncryptResponse response = new GoogleEncryptResponse();

        try {
            //STEP 1: Generate all keys.

            // Generate root keypair. Used to sign intermediate. jsonPath -> $.intermediateSigningKey.signatures
            final PrivateKey rootPrivKey = CryptoUtility.getPrivateFromPKCS8Base64(DEV_ROOT_PRIV_KEY, ASYMMETRIC_KEY_TYPE);
            final PublicKey rootPubKey = CryptoUtility.getPublicFromBase64(DEV_ROOT_PUB_KEY, ASYMMETRIC_KEY_TYPE); //equiv to the verifying key from the test/prod APIs

            // Generate intermediate keypair. Used to sign whole payload. jsonPath -> $.signature
            final KeyPair intermediateKeyPair =  generateKeyPair();
            final PrivateKey intermediatePrivKey = intermediateKeyPair.getPrivate();
            // Convert to uncompressed point and B64 encode. Goes to $.intermediateSigningKey.signedKey.keyValue
            final PublicKey intermediatePubKey = intermediateKeyPair.getPublic();

            // Generate ephemeral pub key. Used to create a shared secret to decrypt the signed message
            final KeyPair ephemeralKeyPair = generateKeyPair();
            final PrivateKey ephemeralPrivKey = ephemeralKeyPair.getPrivate();
            // Convert to uncompressed point and B64 encode. Goes to $.signedMessage.ephemeralPublicKey
            final PublicKey ephemeralPubKey = ephemeralKeyPair.getPublic();
            final byte[] ephemeralPubKeyBytes = getUncompressedPoint(ephemeralPubKey);

            //Parse merchant public key
            final ECPublicKey merchantPublicKey = encryptRequest.getPublicKey();

            /*
            STEP 2: Sign the intermediate key. This step will generate the intermediateSigningKey block in the json
            ECDSA(length_of_sender_id || sender_id || length_of_protocol_version || protocol_version || length_of_signed_key || signed_key)
             */
            final long keyExpiry = Instant.now().plus(365, ChronoUnit.DAYS).toEpochMilli();
            final String intermediatePubKeyB64 = java.util.Base64.getEncoder().encodeToString(intermediatePubKey.getEncoded());
            response.setIntermediateSigningKey(keyExpiry, intermediatePubKeyB64);

            final byte[] senderComponent = getSignatureBytesForComponent(SENDER_ID);
            final byte[] protocolVersionComponent = getSignatureBytesForComponent(PROTOCOL_VERSION);
            final String signingKeyJson = new Gson().toJson(response.getIntermediateSigningKey().getSignedKey());
            final byte[] signingKeyComponent = getSignatureBytesForComponent(signingKeyJson);

            //Assemble all components into one byte array
            final byte[] intermediateSignatureComponentBytes = ByteBuffer
                    .allocate(senderComponent.length + protocolVersionComponent.length + signingKeyComponent.length)
                    .put(senderComponent)
                    .put(protocolVersionComponent)
                    .put(signingKeyComponent)
                    .array();

            //Sign the byte array using ECDSA
            final String intermediateSignature = ecdsaSignMessageB64(rootPrivKey, intermediateSignatureComponentBytes);
            response.setIntermediateSigningKeySignatures(intermediateSignature);

            /*
            Step 3: Create the signed message. Includes tag, encrypted data and ephemeralPubKey
             */

            //Step 3a: Generate shared secret. Used for symmetric encryption
            final byte[] sharedSecret = EllipticCurves.computeSharedSecret(
                    (ECPrivateKey) ephemeralPrivKey,
                    merchantPublicKey.getW());

            //Step 3b: Generate HMAC key and symmetric key using shared secret
            final byte[] eciesKey = Hkdf.computeEciesHkdfSymmetricKey(
                    ephemeralPubKeyBytes,
                    sharedSecret,
                    HMAC_SHA256_ALGO,
                    HKDF_EMPTY_SALT,
                    SENDER_ID.getBytes(StandardCharsets.UTF_8),
                    HMAC_SHA256_ALGO_SIZE + AES_CTR_ALGO_SIZE //256 bit aes key and 256 bit hmac key
            );
            final byte[] aesKey = Arrays.copyOf(eciesKey, AES_CTR_ALGO_SIZE); // [0,32] bytes
            final byte[] hmacKey = Arrays.copyOfRange(eciesKey, HMAC_SHA256_ALGO_SIZE, HMAC_SHA256_ALGO_SIZE * 2); //[32,64]

            //Step 3c: Encrypt data
            final Cipher cipher = EngineFactory.CIPHER.getInstance(AES_CTR_ALGO);
            cipher.init(
                    Cipher.ENCRYPT_MODE,
                    new SecretKeySpec(aesKey, "AES"),
                    new IvParameterSpec(AES_CTR_ZERO_IV));
            final byte[] cipherBytes = cipher.doFinal(encryptRequest.getMessage().getBytes(StandardCharsets.UTF_8));

            //Step 3d: Compute HMAC tag
            final SecretKeySpec secretKeySpec = new SecretKeySpec(hmacKey, HMAC_SHA256_ALGO);
            final Mac mac = EngineFactory.MAC.getInstance(HMAC_SHA256_ALGO);
            mac.init(secretKeySpec);
            final byte[] hmacBytes = mac.doFinal(cipherBytes);

            //Step 3e: Populate data in response. $.signedMessage
            final String ephemeralPublicKeyB64 = java.util.Base64.getEncoder().encodeToString(ephemeralPubKeyBytes);
            final String hmacB64 = java.util.Base64.getEncoder().encodeToString(hmacBytes);
            final String cipherB64 = java.util.Base64.getEncoder().encodeToString(cipherBytes);
            response.setSignedMessage(cipherB64, hmacB64, ephemeralPublicKeyB64);

            /*
            Step 4: Create $.signature using intermediate priv and the serialized signed key
            ECDSA(length_of_sender_id || sender_id || length_of_recipient_id || recipient_id || length_of_protocolVersion || protocolVersion || length_of_signedMessage || signedMessage)
             */

            final String merchantIdComponentString = getMerchantIdComponent(encryptRequest.getMerchantId());
            final byte[] merchantComponent = getSignatureBytesForComponent(merchantIdComponentString);
            ;
            final String signedMessageJson = new Gson().toJson(response.getSignedMessage());
            final byte[] signedMessageComponent = getSignatureBytesForComponent(signedMessageJson);

            //Assemble all components into one byte array
            final int signatureComponentLength = senderComponent.length + merchantComponent.length + protocolVersionComponent.length + signedMessageComponent.length;
            final byte[] signatureComponentBytes = ByteBuffer
                    .allocate(signatureComponentLength)
                    .put(senderComponent)
                    .put(merchantComponent)
                    .put(protocolVersionComponent)
                    .put(signedMessageComponent)
                    .array();

            final String signatureB64 = ecdsaSignMessageB64(intermediatePrivKey, signatureComponentBytes);
            response.setSignature(signatureB64);
        } catch (final Exception e) {
            throw new Exception("Encryption failed: " +  e);
        }
        return response;
    }



    static byte[] getSignatureBytesForComponent(final String component) {
        final byte[] componentLengthBytes = ByteBuffer
                .allocate(4)
                .order(ByteOrder.LITTLE_ENDIAN)
                .putInt(component.length())
                .array();
        final byte[] componentBytes = ByteBuffer
                .wrap(component.getBytes(StandardCharsets.UTF_8))
                .array();
        return ByteBuffer.allocate(componentLengthBytes.length + componentBytes.length)
                .put(componentLengthBytes)
                .put(componentBytes)
                .array();
    }

    public static KeyPair generateKeyPair() throws Exception {
        try {
            final KeyPairGenerator kpg = KeyPairGenerator.getInstance(ASYMMETRIC_KEY_TYPE, SECURITY_PROVIDER);
            kpg.initialize(generateECParameterSpec());
            return kpg.generateKeyPair();
        } catch (final GeneralSecurityException e) {
            System.out.println("Failed to generate ECC KeyPair. " + e);
            throw new Exception("Failed to generate ECC KeyPair.");
        }
    }

    protected static ECNamedCurveSpec generateECParameterSpec() {
        final ECNamedCurveParameterSpec bcParams = ECNamedCurveTable.getParameterSpec(EC_CURVE);
        final ECNamedCurveSpec params = new ECNamedCurveSpec(bcParams.getName(), bcParams.getCurve(),
                bcParams.getG(), bcParams.getN(), bcParams.getH(), bcParams.getSeed());
        return params;
    }



    private static String ecdsaSignMessageB64(final PrivateKey privateKey, final byte[] messageToSign) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        final Signature intermediateECDSASignature = Signature.getInstance(SIGNATURE_ALGORITHM);
        intermediateECDSASignature.initSign(privateKey);
        intermediateECDSASignature.update(messageToSign);
        final byte[] intermediateSignatureBytes = intermediateECDSASignature.sign();
        return java.util.Base64.getEncoder().encodeToString(intermediateSignatureBytes);

    }

    public static byte[] getUncompressedPoint(final PublicKey publicKey) {
        final java.security.interfaces.ECPublicKey pub = (java.security.interfaces.ECPublicKey) publicKey;
        return pub.getEncoded();
    }

    @Component
    public class ProfileManager {

        @Autowired
        private Environment environment;

        @PostConstruct
        public void setup() {
            System.out.println(this.getActiveProfiles());
        }

        public boolean isProfileActive(final String profile) {
            for (final String activeProfile : this.environment.getActiveProfiles()) {
                if (activeProfile.equalsIgnoreCase(profile)) {
                    return true;
                }
            }
            return false;
        }

        public List<String> getActiveProfiles() {
            return List.of(this.environment.getActiveProfiles());
        }

    }
    @Slf4j
    @Data
    @NoArgsConstructor
    public class GoogleEncryptResponse implements EncryptResponse {

        //These entities represent the structure provided by the GPay spec. See link in class docs
        private String protocolVersion = "ECv2";

        private String signature;

        private IntermediateSigningKey intermediateSigningKey = new IntermediateSigningKey();

        private SignedMessage signedMessage = new SignedMessage();

        public void setIntermediateSigningKey(final long keyExpiration, final String keyValue) {
            this.intermediateSigningKey.getSignedKey().setKeyExpiration(Long.toString(keyExpiration));
            this.intermediateSigningKey.getSignedKey().setKeyValue(keyValue);
        }

        public void setIntermediateSigningKeySignatures(final String... signatures) {
            this.intermediateSigningKey.setSignatures(signatures);
        }

        public void setSignedMessage(final String encryptedMessage, final String tag, final String ephemeralPublicKey) {
            this.signedMessage.setEncryptedMessage(encryptedMessage);
            this.signedMessage.setTag(tag);
            this.signedMessage.setEphemeralPublicKey(ephemeralPublicKey);
        }

        @Override
        public String getResult() {
            Gson gson = new Gson();

            JsonObject intermediateSigningKeyJson = new JsonObject();
            JsonArray intermediateSigningKeyJsonSignatures = new JsonArray();
            Arrays.stream(intermediateSigningKey.getSignatures())
                    .forEach(intermediateSigningKeyJsonSignatures::add);
            intermediateSigningKeyJson.add("signatures", intermediateSigningKeyJsonSignatures);
            intermediateSigningKeyJson.addProperty("signedKey", gson.toJson(getIntermediateSigningKey().getSignedKey()));

            JsonObject payloadRoot = new JsonObject();
            payloadRoot.addProperty("protocolVersion", protocolVersion);
            payloadRoot.addProperty("signature", signature);
            payloadRoot.add("intermediateSigningKey", intermediateSigningKeyJson);
            payloadRoot.addProperty("signedMessage", gson.toJson(getSignedMessage()));

            return getJsonElementHtmlSafe(payloadRoot);
        }


        @Data
        @NoArgsConstructor
        public static class IntermediateSigningKey {
            private SignedKey signedKey = new SignedKey();

            /**
             * Signatures signed by the verifying key. Should be B64 encoded used SHA256 hashing with
             * ECDSA
             */
            private String[] signatures;

            @Data
            @NoArgsConstructor
            static class SignedKey {

                /**
                 * A Base64 version of key encoded in ASN.1 type.
                 * aka, der format. key.getEncoded()
                 */
                private String keyValue;

                /**
                 * Date and time when the intermediate key expires as UTC milliseconds since epoch
                 */
                private String keyExpiration;
            }
        }

        @Setter
        @NoArgsConstructor
        private static class SignedMessage {

            /**
             * A Base64-encoded encrypted message that contains payment information
             */
            private String encryptedMessage;

            /**
             * A Base64-encoded ephemeral public key associated
             * with the private key to encrypt the message in uncompressed point format
             */
            private String ephemeralPublicKey;

            /**
             * A Base64-encoded MAC of encryptedMessage.
             */
            private String tag;

        }
    }

    public interface EncryptResponse {

        String getResult();

    }

    public interface EncryptRequest<K extends PublicKey> {

        /**
         * Public key used to encrypt
         * @return
         */
        K getPublicKey();

        void setPublicKey(K publicKey);

        String getAlgorithm();

        /**
         * Set the PublicKey object using a base64 encoded key.
         * @param base64EncodedKey
         */
        default void setPublicKey(final String base64EncodedKey) throws Exception {
            PublicKey publicKey = CryptoUtility.getPublicFromBase64(base64EncodedKey, getAlgorithm());
            setPublicKey((K) publicKey);
        }

        /**
         * Message to encrypt
         * @return
         */
        String getMessage();
    }

    public static String getJsonElementHtmlSafe(JsonElement element) {
        try {
            StringWriter stringWriter = new StringWriter();
            JsonWriter jsonWriter = new JsonWriter(stringWriter);
            jsonWriter.setLenient(false);
            jsonWriter.setHtmlSafe(true); //Ensures '=' will appear as \u003d

            Streams.write(element, jsonWriter);
            return stringWriter.toString();
        } catch (IOException e) {
            return null;
        }
    }

    @Builder
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class GoogleDecryptRequest implements DecryptRequest<ECPrivateKey> {

        /**
         * This represents the encrypted value
         */
        private String encryptedMessage;

        /**
         * The current active private key
         */
        private ECPrivateKey privateKey;

        /**
         * These privateKey represents a private key that may have been rotated recently.
         * May be empty
         */
        @Singular
        private List<ECPrivateKey> oldPrivateKeys = new ArrayList<>();

        /**
         * Corresponds to the GPay console merchant id
         * Value must be prefixed with merchant:<br/>
         * For example: merchant:1234345345345
         */
        private String merchantId;

        private String algorithm = "EC";

        public void addOldPrivateKey(final ECPrivateKey privateKey) {
            this.oldPrivateKeys.add(privateKey);
        }

        public static class GoogleDecryptRequestBuilder {

            public GoogleDecryptRequestBuilder encryptedMessage(final String encryptedMessage) {
                try {
                    final JsonElement jsonPayload = new JsonParser().parse(encryptedMessage);
                    this.encryptedMessage = getJsonElementHtmlSafe(jsonPayload);
                } catch (final Exception e) {
                    System.out.println("Failed to parse GooglePay encrypted message. Data MUST be a valid json string." + e);
                }
                return this;
            }

        }
    }

    public interface DecryptRequest<K extends PrivateKey> {

        /**
         * Return the message that will be decrypted.
         * @return
         */
        String getEncryptedMessage();

        /**
         * Return the private key required for decryption
         * @return
         */
        K getPrivateKey();

        void setPrivateKey(K privateKey);

        String getAlgorithm();

        /**
         * Set the PublicKey object using a base64 encoded key.
         * @param base64EncodedKey
         */
        default void setPrivateKey(final String base64EncodedKey) throws Exception {
            PrivateKey privateKey = CryptoUtility.getPrivateFromPKCS8Base64(base64EncodedKey, getAlgorithm());
            setPrivateKey((K) privateKey);
        }

    }


}

CryptoUtility 类:

package com.decryption.test;

public class CryptoUtility {

    public static PrivateKey getPrivateFromPKCS8Base64(final String base64, final String algorithm) throws Exception {
        final byte[] privateKeyBytes = Base64.decode(base64);
        return getPrivateFromPKCS8Der(privateKeyBytes, algorithm);
    }
    public static PrivateKey getPrivateFromPKCS8Der(final byte[] privateKeyBytes, final String algorithm) throws Exception {
        try {
            final PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privateKeyBytes);
            final KeyFactory kf = KeyFactory.getInstance(algorithm, CryptoLib.SECURITY_PROVIDER);
            return kf.generatePrivate(spec);
        } catch (final Exception e) {
            throw new Exception("Failed to created Private Key." + e);
        }
    }

    public static PublicKey getPublicFromBase64(final String base64, final String algorithm) throws Exception {
        final byte[] publicKeyBytes = Base64.decode(base64);
        return getPublicFromDer(publicKeyBytes, algorithm);
    }

    public static PublicKey getPublicFromDer(final byte[] publicKeyBytes, final String algorithm) throws Exception {
        try {
            final X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyBytes, algorithm);
            final KeyFactory kf = KeyFactory.getInstance(algorithm, CryptoLib.SECURITY_PROVIDER);
            return kf.generatePublic(spec);
        } catch (final Exception e) {
            throw new Exception("Failed to created Public Key from string value" + e);
        }
    }






}

JUnit类:

package com.decryption.test;

@ExtendWith(MockitoExtension.class)
public class DecryptionTest {

    @InjectMocks
    private CryptoLib googleECCCryptoLibrary;

    @Mock
    private CryptoLib.ProfileManager profileManager;


    @Test
    void encryptAndDecryptMockData() throws Exception {
        final ECPrivateKey merchantPrivKey = (ECPrivateKey) CryptoUtility.getPrivateFromPKCS8Base64("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgsfylDy+Q3lfR6nbipZGDZWzp6P+qiQeApJizxG/hj96gCgYIKoZIzj0DAQehRANCAAQg1SVNuof6FndzJkbPst37moW+L/36EPLiiosS5BBSsLK4q2aLxzk2M732OpDHkXTp31ZQitPDQImndaY57ZSM", "EC");
        final ECPublicKey merchantPubKey = (ECPublicKey) CryptoUtility.getPublicFromBase64("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEINUlTbqH+hZ3cyZGz7Ld+5qFvi/9+hDy4oqLEuQQUrCyuKtmi8c5NjO99jqQx5F06d9WUIrTw0CJp3WmOe2UjA==", "EC");

        final CryptoLib.GoogleEncryptRequest encRequest = CryptoLib.GoogleEncryptRequest.builder()
                .merchantId("12345678901234567890")
                .message("heyhey!")
                .publicKey(merchantPubKey)
                .build();
        final CryptoLib.EncryptResponse encResponse = googleECCCryptoLibrary.encrypt(encRequest);

        final CryptoLib.GoogleDecryptRequest decryptRequest = CryptoLib.GoogleDecryptRequest.builder()
                .privateKey(merchantPrivKey)
                .merchantId("12345678901234567890")
                .encryptedMessage(encResponse.getResult())
                .build();


        given(this.profileManager.isProfileActive(anyString())).willReturn(true);

        final String decResponse = this.googleECCCryptoLibrary.decrypt(decryptRequest);
        assertEquals(encRequest.getMessage(), decResponse);
    }

}

堆栈跟踪:

    Caused by: java.security.GeneralSecurityException: java.lang.IllegalStateException: java.security.InvalidAlgorithmParameterException: Curve not supported: secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7)
        at com.google.crypto.tink.subtle.EllipticCurves.computeSharedSecret(EllipticCurves.java:963)

ECDHKeyAgreement.engineGenerateSecret()实现似乎在 Java 16 中发生了更改,现在它正在抛出: throw new IllegalStateException(new InvalidAlgorithmParameterException(" 与 Java 13 中的实现相比,不支持曲线。

有什么建议可以在 JDK 16 中使用 BouncyCastle 作为提供者来实现加密,或者是否应该由任何其他提供者替换它?

4

0 回答 0