AES、DES、RSA等加密出现解密失败原因

AES、DES、RSA等加密出现解密失败原因

AES、DES、RSA等加密出现解密失败原因

我们在用微信支付或者其他第三方接口用对称或者非对称加密时经常会遇到明明公钥私钥都正确,可是第三方服务端还是返回解密失败

解密错误原因

1.本地字符串编码和加密后字节编码不对应,java中String类的getBytes方法如下

public byte[] getBytes() {

return StringCoding.encode(value, 0, value.length);

}

该方法是调用StringCoding的静态encode方法,encode方法如下:

static byte[] encode(char[] ca, int off, int len) {

String csn = Charset.defaultCharset().name();

try {

// 此处调用Charset默认字符编码

return encode(csn, ca, off, len);

} catch (UnsupportedEncodingException x) {

warnUnsupportedCharset(csn);

}

try {

//如果不支持则采用ISO-8859-1编码

return encode("ISO-8859-1", ca, off, len);

} catch (UnsupportedEncodingException x) {

// If this code is hit during VM initialization, MessageUtils is

// the only way we will be able to get any kind of error message.

MessageUtils.err("ISO-8859-1 charset not available: "

+ x.toString());

// If we can not find ISO-8859-1 (a required encoding) then things

// are seriously wrong with the installation.

System.exit(1);

return null;

}

}

我们再看下Charset类默认编码是什么,代码如下所示:

public static Charset defaultCharset() {

//defaultCharset 是Charset静态volatile变量

if (defaultCharset == null) {

synchronized (Charset.class) {

//这里读取属性所设置的文件编码,如果没有则默认UTF-8

String csn = AccessController.doPrivileged(

new GetPropertyAction("file.encoding"));

Charset cs = lookup(csn);

if (cs != null)

defaultCharset = cs;

else

defaultCharset = forName("UTF-8");

}

}

return defaultCharset;

}

从System.getProperty(“file.encoding”)获取的编码跟我在IDEA设置了jvm变量file.endcoding一致为UTF-8,如果不设置file.endcoding变量,则默认系统编码,系统编码如果不支持则默认编码UTF-8,当然java几乎没有不能支持的编码,sun包里StandardCharsets提供了一千多种编码,那么至此我们明白字符串转字节数组时默认是系统编码或者UTF-8,windows下默认gbk,linux下默认ISO8859-1。

通常我们采用AES加密会采用如下方式,

public static byte[] encrypt(String content, String password) {

try {

Cipher cipher = Cipher.getInstance("AES");// 创建密码器

byte[] byteContent = content.getBytes();

cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));// 初始化为加密模式的密码器

return cipher.doFinal(byteContent);// 加密

} catch (NoSuchAlgorithmException e) {

e.printStackTrace();

} catch (NoSuchPaddingException e) {

e.printStackTrace();

} catch (InvalidKeyException e) {

e.printStackTrace();

} catch (IllegalBlockSizeException e) {

e.printStackTrace();

} catch (BadPaddingException e) {

e.printStackTrace();

}

return null;

}

/**

* 生成加密秘钥

*

* @return

*/

private static SecretKeySpec getSecretKey(String password) {

//返回生成指定算法密钥生成器的 KeyGenerator 对象

KeyGenerator kg = null;

try {

kg = KeyGenerator.getInstance("AES");

//AES 要求密钥长度为 128

kg.init(128, new SecureRandom(password.getBytes()));

//生成一个密钥

SecretKey secretKey = kg.generateKey();

return new SecretKeySpec(secretKey.getEncoded(), "AES");// 转换为AES专用密钥

} catch (NoSuchAlgorithmException ex) {

ex.printStackTrace();

}

return null;

}

我们采用如下测试:

public static void main(String[] args) throws UnsupportedEncodingException {

//获取默认编码

System.out.println(System.getProperty("file.encoding"));

String str = "abc123阿达";

byte[] bytes = encrypt(str, "123456");

System.out.println(bytes.length == new String(bytes).getBytes().length);

System.out.println(bytes.length == new String(bytes, "UTF-8").getBytes("UTF-8").length);

System.out.println(bytes.length == new String(bytes, "GBK").getBytes("GBK").length);

System.out.println(bytes.length == new String(bytes, "ISO8859-1").getBytes("ISO8859-1").length);

}

结果如下:

UTF-8

false

false

true

true

综上所知采用sun包下的Cipher加密返回的byte[]编码方式是ISO8859-1,至此我们明白罪魁祸首是这个加密方式的默认编码

如何避免

1.加密后返回byte数组转换成16进制字符串或者base64编码字符串,如下

String str = "abc123阿达";

System.out.println(new String(str.getBytes(),"UTF-8"));

byte[] bytes = encrypt(str, "123456");

String result= Hex.bytes2Hex(bytes);

System.out.println(new String(decrypt(Hex.hexToBytes(result),"123456")));

结果如下

abc123阿达

abc123阿达

同时注意解密时要把字符串进行解码,防止下面异常

javax.crypto.BadPaddingException: Given final block not properly padded

at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)

at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)

at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)

at javax.crypto.Cipher.doFinal(Cipher.java:2165)

2.当加密内容调用微信等第三方时,一般第三方不会解码,所以此时我们只能对加密后byte[]转string时设置iso8859-1编码。不然第三方一直返回解密失败。

相关推荐

正在阅读:哔哩哔哩怎么扫码登录 哔哩哔哩扫码登录方法【详解】哔哩哔哩怎么扫码登录 哔哩哔哩扫码登录方法【详解】
中国男足备战对澳洲世杯预选赛 盼取得最好结果
梦幻西游JDD是什么意思 全副本黑话介绍
喉的五笔怎么打?

喉的五笔怎么打?

06-28 👁️ 573
同样充会员,为什么苹果比安卓贵?
如何设计APP广告位

如何设计APP广告位

09-16 👁️ 5347
好的比喻句,真的会让人记很久很久! (附20个长比喻)
活泼易爆 贵于黄金的小金属——铯
支付宝关联淘宝账号的完整指南:快速绑定与常见问题解决