• Daily Archives: 2022年11月16日

微信支付平台证书返回解密方法封装

废话不多说,直接上代码:

package com.isuidian.util;

import java.io.IOException;
import java.security.*;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class WechatStaticUtil {

    private static final int KEY_LENGTH_BYTE = 32;
    private static final int TAG_LENGTH_BIT = 128;

    public static String decryptToString(byte[] apiV3Key, byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException {

        if (apiV3Key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }

        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(apiV3Key, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }

}

 

 

NoSuchMethodError kotlin.collections.ArraysKt.copyInto([B[BIII)[B

查了网络资料,大概原因就是:springboot版本跟okhttp的jar包冲突,因此提示调用的方法没有。

解决方法:

方法一:(已验证)

指定kotlib-stdlib的版本,在pom中加入如下依赖:

<!-- https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib -->
<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib</artifactId>
    <version>1.3.70</version>
</dependency>

方法二:(未验证)

把okhttp3的4.x版本改成3.x版本:

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.14.8</version>
        </dependency>

 

JDK的AES加密异常java.security.InvalidKeyException: Illegal key size 的解决方法

今天在调用获取微信支付平台证书V3接口时,需要对其内容进行解密。根据官方的demo如下:

static final int KEY_LENGTH_BYTE = 32;
	static final int TAG_LENGTH_BIT = 128;

	public String decryptToString(byte[] apiV3Key, byte[] associatedData, byte[] nonce, String ciphertext) throws GeneralSecurityException, IOException {

		if (apiV3Key.length != KEY_LENGTH_BYTE) {
			throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
		}

		try {
			Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

			SecretKeySpec key = new SecretKeySpec(apiV3Key, "AES");
			GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

			cipher.init(Cipher.DECRYPT_MODE, key, spec);
			cipher.updateAAD(associatedData);

			return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			throw new IllegalStateException(e);
		} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
			throw new IllegalArgumentException(e);
		}
	}

发现执行到 cipher.init(Cipher.DECRYPT_MODE, key, spec); 的时候报 java.security.InvalidKeyException: Illegal key size 错误。

经过资料查阅,得出原因如下:

JDK受版本安全限制,默认只允许128位长度以内的。秘钥长度,如果密钥大于128, 会抛出java.security.InvalidKeyException: Illegal key size 异常. java运行时环境默认读到的是受限的policy文件. 文件位于${java_home}/jre/lib/security, 这种限制是因为美国对软件出口的控制所造成的的.JDK1.8之后已经兼容了该问题。

解决方案:

方法一:升级不受限制JDK版本
升级JDK9级以上版本,未测试

方法二:替换JDK受限文件级配置
JDK7的下载地址: Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 Download
JDK8的下载地址: JCE Unlimited Strength Jurisdiction Policy Files for JDK/JRE 8 Download
下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件
如果安装了JDK,还要将两个jar文件也放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件

方法三:JDK1.8 代码策略修改
JDK1.8 已经支持不受限的jar,但配置默认是受限的
修改代码配置,在加密之前,修改不受限配置.

WXBizMsgCrypt wxcpt = createWXBizMsgCrypt(suiteId, getCorpId(postData), false);
Security.setProperty("crypto.policy", "unlimited");
sMsg = wxcpt.decryptMsg(msgSignature, timestamp, nonce, postData);

 

close