现象描述:
+ V3 E6 m! k3 C, S* j) O$ _+ A8 l7 k- E f
windows上加解密正常,linux上加密正常,解密时发生如下异常:
, ]% {. l) U) `+ m4 v4 ]2 V) U' G6 I: F* x
javax.crypto.BadPaddingException: Given final block not properly padded
/ }- ~- r" |9 ^! `2 p at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..), i8 ` e: ~6 ]5 z/ C) F
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
* F2 E9 D' \% S- F4 A at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
2 X+ g) r& L* L9 m at javax.crypto.Cipher.doFinal(DashoA13*..)
8 Y1 E( S' J% K- _7 n! V: c at chb.test.crypto.AESUtils.crypt(AESUtils.java:386)
' m1 u+ `3 a/ y3 l. c' W9 [% X' U at chb.test.crypto.AESUtils.AesDecrypt(AESUtils.java:254)9 A1 q* ]. j# w0 Z
at chb.test.crypto.AESUtils.main(AESUtils.java:40) 解决方法:7 K! O) s% t7 z. {8 g( z
" B- \2 Z% G7 z# i3 @- J( J
经过检查之后,定位在生成KEY的方法上,如下:4 y! M% w" _, X
- public static SecretKey getKey (String strKey) {9 R k2 E# y5 ]- @; i' ^
- try { 4 i( k% U$ P5 W" e+ t @) E* a+ ]
- KeyGenerator _generator = KeyGenerator.getInstance( "AES" );
' L0 _. \0 ]" d5 ] - _generator.init(128, new SecureRandom(strKey.getBytes()));
! |% A: Y+ P9 |7 V - return _generator.generateKey();
K# n* O D( ^& x% R - } catch (Exception e) {; b2 j1 a$ W' `* |9 U
- throw new RuntimeException( " 初始化密钥出现异常 " );" a7 M% c3 A' c
- }
) u" c. X5 w3 b/ W; c: ]" N1 [4 q - }
复制代码 修改到如下方式,问题解决:) Y8 o% H2 ]" D0 m! o1 T
- public static SecretKey getKey(String strKey) {% S* O3 W9 I7 z2 Q0 x. C
- try { G. M$ T2 E% \
- KeyGenerator _generator = KeyGenerator.getInstance( "AES" );4 [# L0 m! Q9 r! _
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );, s0 I! e! p- T: o$ g
- secureRandom.setSeed(strKey.getBytes());$ i4 A0 c8 h( ^1 T
- _generator.init(128,secureRandom);( j: P; Y7 a }
- return _generator.generateKey();+ M' R" s, `3 N9 ?% o; b0 ?; V
- } catch (Exception e) {1 j1 ~8 h, I0 w. h9 p8 S* V
- throw new RuntimeException( " 初始化密钥出现异常 " );
X* h1 b/ [; t2 D7 h/ T - }
/ Z1 @. [2 }7 G& e x V - }
复制代码 原因分析3 F7 i! a. w7 K. @# I) W) I
SecureRandom 实现完全隨操作系统本身的內部狀態,除非調用方在調用 getInstance 方法之後又調用了 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。
, \% c) P9 W5 n* F* _
3 v0 I3 L6 l8 N: H原因二:
" J; V& }$ |" C
/ e' p) a' p/ }$ b1、加密完byte[] 后,需要将加密了的byte[] 转换成base64保存,如:
/ [9 G5 R* K' G2 e* y; D8 eBASE64Encoder base64encoder = new BASE64Encoder(); , M' j- {# \ h U4 z9 L: I
String encode=base64encoder.encode(bytes);
( {' J; S1 G& N$ O( z- d$ L0 @
( f; o& Y7 P* ?8 J. {2、解密前,需要将加密后的字符串从base64转回来再解密,如: & q& H. w. d0 F3 j
BASE64Decoder base64decoder = new BASE64Decoder(); 9 z% w: h7 U* V0 r$ G, D
byte[] encodeByte = base64decoder.decodeBuffer(str); 3 T! r, f" Y$ S! k/ G
) m: K8 k; f4 @; _$ O+ y
+ R, m8 O; R3 J6 q+ D. ^完整例子:/ a% r7 M. N6 A/ q% y+ b9 j7 t
- package com.travelsky.tdp.pkgStock.util;6 [* L! G# ?8 k
( w& n5 ^' P6 _" K) t. W# e8 S. x- import java.io.IOException;3 j. @& y: r' Z$ H7 @5 Y9 n
- import java.io.UnsupportedEncodingException;
. O- X& z& _+ l- v9 k' C8 z - import java.security.InvalidKeyException;9 I" o6 s" U9 c, b3 n
- import java.security.NoSuchAlgorithmException;
' Q5 c- ~' J$ h - import java.security.SecureRandom;
+ Y1 L- {1 ~, j6 I* n+ v% D - * y3 N' f. a& {5 U/ `' J8 E
- import javax.crypto.BadPaddingException;
7 S1 m2 f. E2 H - import javax.crypto.Cipher;
7 c' j- A; \$ i4 Z, O7 o - import javax.crypto.IllegalBlockSizeException;8 l' |4 X6 A' i6 b
- import javax.crypto.KeyGenerator;3 R4 P! r( p5 [6 g7 F
- import javax.crypto.NoSuchPaddingException;
! v- v- R9 L2 w - import javax.crypto.SecretKey;
+ j5 M2 M) m/ f6 @/ s2 ~- B; h - import javax.crypto.spec.SecretKeySpec;
" F. |4 F6 ~! \9 b+ d - ' _" ~2 H9 S& M% c7 v
- import sun.misc.BASE64Decoder;
+ `9 k4 ?% u2 J' Z. N - import sun.misc.BASE64Encoder;/ Z0 M8 z7 h: z" W* O9 u
; g7 g3 y! V5 F9 m3 E- public class SecurityAES {
; a: g: S v& P& n9 o$ ] - private final static String encoding = "UTF-8";
( ^) Q4 }; z. Y# C# Q - /**
' t4 y$ k: ~9 I1 C2 { - * AES加密
# `3 t7 K0 h$ E8 E - * # ]; o8 o: g3 t+ c2 f
- * @param content) u3 R1 q2 S5 r8 a
- * @param password: @0 E/ q" L2 u) u# T8 `7 K
- * @return. k3 @6 T& r/ _' |( L; G! ]
- */4 T; x; a2 c& `. I
- public static String encryptAES(String content, String password) {
4 J) w4 v" B$ ~, n0 _8 t8 w - byte[] encryptResult = encrypt(content, password);- a. W7 i/ G) P" a- Y) z {) N) X
- String encryptResultStr = parseByte2HexStr(encryptResult);4 W0 S8 o. y) w. r$ _1 |4 b
- // BASE64位加密
8 `* F8 A/ b9 j5 v - encryptResultStr = ebotongEncrypto(encryptResultStr);
) p- Y! \) l! M7 w& N - return encryptResultStr;
, j7 H' Y( C1 L: \ - }
4 F V" F; u" U8 }
7 L9 g; |6 }' \% D( v9 n- /**
& i& K9 b: a+ j6 g - * AES解密4 Y2 h0 U% K' E5 u4 I5 ?
- *
3 Y$ c2 r: y" n* T9 J. z( Q; u - * @param encryptResultStr1 s8 j+ l2 I& j' R3 n
- * @param password
' ]( f+ ^. e& y& L5 ~% S! W$ _ - * @return9 n" k2 F: Z( @. P) E7 z
- */
5 [" P, b6 d7 U2 E - public static String decrypt(String encryptResultStr, String password) {
% V$ I4 P, h$ t4 g) ? - // BASE64位解密
E1 i6 j1 w# }2 o7 y - String decrpt = ebotongDecrypto(encryptResultStr);$ u6 ~- Y8 r; I. p8 O
- byte[] decryptFrom = parseHexStr2Byte(decrpt);
3 n+ o7 ^$ z( P3 v+ _# |* P2 n - byte[] decryptResult = decrypt(decryptFrom, password);( n Q" m& n! r# I# w
- return new String(decryptResult);3 ^: R7 a y/ T: n% [4 Y+ _
- }
1 |! ]9 \+ d$ c/ y# n, q! L( k
2 r. _8 j3 }8 p/ t- /**3 |. U$ p R V# E m4 d' {( z
- * 加密字符串- ]/ Z; H- H u4 m" t( n: I
- */
2 Q1 e W/ c i - public static String ebotongEncrypto(String str) {
6 ~3 W9 j. }/ H5 W& o - BASE64Encoder base64encoder = new BASE64Encoder();
2 Y' ]% t6 N& Z - String result = str;
* U0 w7 G4 J& h- B' y% O - if (str != null && str.length() > 0) {
' f) ]' r6 Z+ ]- B - try {2 g- t) _4 Q& z- N, d
- byte[] encodeByte = str.getBytes(encoding);2 g# Z& e" m& {) l
- result = base64encoder.encode(encodeByte);
! N, K; K- U' f4 c3 q - } catch (Exception e) {% O3 b- ^8 o0 ^/ {: E* c
- e.printStackTrace();
& Z' ~6 q1 V; ~0 c3 F8 D% W - } T/ p% ?- V% u$ A
- }4 q" P" T9 Q% [) r
- //base64加密超过一定长度会自动换行 需要去除换行符0 d5 O; N/ f/ ?3 F4 B( B6 [) E' i$ Y7 N
- return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
0 K/ w2 y! M; T: ` M - }
* q9 o, P; s8 q4 E6 t& y/ d% \
4 W2 B* T" ?+ Y$ ^7 C- /**
a, z6 W+ H. q. ?% V* Q - * 解密字符串
2 A& ]6 q' ^1 U; o - */
. A9 q( P3 T+ {" L$ R- p4 A - public static String ebotongDecrypto(String str) {& T4 y& J& _' H$ x7 B" O5 K9 G
- BASE64Decoder base64decoder = new BASE64Decoder();
; n y) z8 O/ V! E - try {
5 {9 p# U5 Y+ x7 t# ^: U; i - byte[] encodeByte = base64decoder.decodeBuffer(str);
* e2 j7 @( L9 V) s9 w! N5 L - return new String(encodeByte);
7 |% L2 b9 T5 I# X - } catch (IOException e) {7 H% \6 B; l2 l8 `. R
- e.printStackTrace();% q3 c, F0 Q) g% u. s1 u. c2 T R
- return str;
) Z/ O" U3 _5 \, I/ X/ R - }
% ] C/ x0 o5 Q3 J) i - }: C$ l; r1 Z7 K Q( U$ f1 K
- /**
% _. e0 g" x7 d% n$ [5 x - * 加密
4 l5 P$ J n& I4 W2 c: d - *
& A# Z( Y/ R. E8 E: g" R6 W - * @param content 需要加密的内容
( L; }$ g" C5 |5 `. ]) n; } - * @param password 加密密码 ' _% `& f7 Q# w
- * @return
7 E& N* M; H% n - */ 2 j. j" s. r! c a
- private static byte[] encrypt(String content, String password) { $ [) N# Y, T, A- X6 z& g5 u
- try {
k2 X" }% Y( {0 n - KeyGenerator kgen = KeyGenerator.getInstance("AES"); ! X& F; J! M' p v. s/ S
- //防止linux下 随机生成key
0 d; I* Z: M" L2 \ - SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" ); 5 t+ p+ c- i A% s' X8 x0 ~! o0 c
- secureRandom.setSeed(password.getBytes());
4 m% `# H N, o% K7 @& e9 M, v0 t - kgen.init(128, secureRandom);
$ V/ z) V! c' D$ Y3 Z+ P* ~& z3 U; d - //kgen.init(128, new SecureRandom(password.getBytes())); * @) D$ f3 P1 H$ D$ w5 f2 y
- SecretKey secretKey = kgen.generateKey();
9 ?1 S4 K/ x. A2 X) h - byte[] enCodeFormat = secretKey.getEncoded();
7 ]- W5 i: E; {/ m - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
" K' K1 T) ~! f: I1 S2 u - Cipher cipher = Cipher.getInstance("AES");// 创建密码器 0 e7 J$ A& U0 r5 U0 {4 y6 Z/ f/ B
- byte[] byteContent = content.getBytes("utf-8"); % q* t- v! v+ m& `, D
- cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
; a0 [! G) j% p - byte[] result = cipher.doFinal(byteContent);
* P9 P# E; f7 K0 k; g9 E - return result; // 加密 * o7 \% q. u+ J9 |+ ]- J
- } catch (NoSuchAlgorithmException e) { ; {: e, r2 J v$ [4 u5 L
- e.printStackTrace(); - ?- o; T j. c. w1 X$ F3 X
- } catch (NoSuchPaddingException e) {
7 w) q; }/ S3 G. \ _ - e.printStackTrace(); 3 Y1 p( ~- K( Y0 o' o0 T) \2 T
- } catch (InvalidKeyException e) { 8 J, _0 X& ^' ^
- e.printStackTrace();
, G1 L' i) v1 s9 m' A - } catch (UnsupportedEncodingException e) { # h7 j6 Y0 {3 } c, \8 {% ]+ J1 j
- e.printStackTrace(); & ?6 ]) W( K' w3 J( r3 @
- } catch (IllegalBlockSizeException e) {
! b: f# ^" L7 V0 r0 }% l - e.printStackTrace(); . P* l* Q* i G
- } catch (BadPaddingException e) { 7 }" M/ w/ s+ i7 o' |
- e.printStackTrace(); - n/ ]7 M: f1 {
- } 1 f4 ~4 U( g) a
- return null;
4 M6 Y" |5 J! n% C+ k - }
. P* n: O& x4 q0 {, Z- ?
" M, }; C" i/ u/ i
+ _6 z0 t v- X5 o7 t- /**解密 ' @: m# Z8 _% S% J
- * @param content 待解密内容 : o/ K+ m) x0 b% [( Z
- * @param password 解密密钥 . X0 `( A% U+ s1 w# g" ]+ n+ E
- * @return 2 {: P; K7 O1 G( J1 A
- */
7 c4 \8 E) ^, u& V - private static byte[] decrypt(byte[] content, String password) { " t; w) u: @ W! l
- try { ( C, E! H' `1 d- t
- KeyGenerator kgen = KeyGenerator.getInstance("AES");
' B! k9 g/ Q/ q0 \, _ - //防止linux下 随机生成key( K8 L4 q: v0 G( e. W% Z: o
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
0 K8 s s1 E9 q' @" s8 y6 z; V" s - secureRandom.setSeed(password.getBytes()); ; |6 e: C- y V8 Z% P
- kgen.init(128, secureRandom);
. O. [4 j. z. m& { - //kgen.init(128, new SecureRandom(password.getBytes()));
( [( ^" n5 H9 G* J' V# c. x - SecretKey secretKey = kgen.generateKey();
/ n) }) F$ b( U+ o1 ^# a8 C& ]( _ - byte[] enCodeFormat = secretKey.getEncoded();
3 E7 Z$ N2 a# G; C - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
4 Y! X' W$ _3 ?- g4 r3 |) {) v - Cipher cipher = Cipher.getInstance("AES");// 创建密码器
* Z! ~* q6 ?, s, v2 p9 ? - cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
, \, c2 C. X2 v" ?# @, i V2 { - byte[] result = cipher.doFinal(content);
" p4 ?. W6 k1 z$ {7 e5 k9 M" y - return result; // 加密 - t& f$ _6 \& ~8 h
- } catch (NoSuchAlgorithmException e) {
0 l( `8 _; g3 W# S" v& u - e.printStackTrace(); 7 [( i( r) o. }8 c8 M; O' }
- } catch (NoSuchPaddingException e) { + x6 A, J% j+ @* M" u; _
- e.printStackTrace();
+ \% @' M% k6 l2 X+ y# A% ?' [% c - } catch (InvalidKeyException e) { * G) b! F* R3 {3 ~% c# l, V% ^' I7 n
- e.printStackTrace();
e% V# x/ R6 L% q! t! T! n - } catch (IllegalBlockSizeException e) { : I# ]+ g* b7 n. A# g: [: L) [5 H
- e.printStackTrace(); + Z/ k) Z9 G# @7 v3 _
- } catch (BadPaddingException e) { , C, M% T8 c4 u: m
- e.printStackTrace();
7 C6 {7 I8 J% L: _) | - }
+ }! c8 E! y; g2 } - return null; + C% A' R2 W" z8 ~( p& l9 z# o7 K
- }
6 W D" Z- K4 r: C6 | ^+ U - 5 M' W' {( W5 K( ^( W: i3 e
- /**将二进制转换成16进制 7 q" i3 l% ~* y) R" n- D7 v4 T
- * @param buf 9 E" D0 { X8 L0 ?. }5 z- c. h/ S
- * @return % y. q p$ ]3 Y% f
- */ 6 G4 k5 c. Y3 t- `+ z& Y+ u. C
- public static String parseByte2HexStr(byte buf[]) {
2 I: ?6 j g5 ]9 Q) o K - StringBuffer sb = new StringBuffer();
8 D Z: U7 W& a7 F5 E6 \: ] - for (int i = 0; i < buf.length; i++) { 2 |: ~8 A [. T4 J! U$ h
- String hex = Integer.toHexString(buf[i] & 0xFF); 2 }# R* f1 |; w1 }+ |5 j0 R% M
- if (hex.length() == 1) {
4 e& J& E O$ P+ n - hex = '0' + hex;
9 ^( t6 f4 s9 m, b6 l+ Q K0 j - } 9 }# o4 j2 w. e4 A5 q
- sb.append(hex.toUpperCase()); " b2 f, l! M4 e( E2 t& z, O3 S$ t
- }
6 ?+ \) d# A; x) B - return sb.toString();
9 t/ W2 m3 \5 v - } & _* V: {- r- O* }. Q# ~) g
- : t: M6 ]" E* |7 g) y5 Q) T
& A2 ~+ M# b M9 o1 @% y5 q- /**将16进制转换为二进制 * _% b# p7 }$ t3 x! M2 F
- * @param hexStr - A! A0 t+ d. e* ~* i5 W+ ^0 @0 G- K9 _( y
- * @return # m* d, Y' u9 G! l
- */
/ t/ K! j. U" N3 \8 ? - public static byte[] parseHexStr2Byte(String hexStr) {
) q% S4 M- l' h - if (hexStr.length() < 1) ( H5 y- N6 w7 s% f7 K1 H- I4 t6 F& v
- return null; ' v, g; L$ q/ r1 R9 F" s4 {
- byte[] result = new byte[hexStr.length()/2]; ) G, l4 X4 i: R# l: y1 d% d, \
- for (int i = 0;i< hexStr.length()/2; i++) { ' Y- b9 W) p, M
- int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
/ [2 |" J5 e9 a - int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
. v4 m% u7 t" C, Z& r0 E5 K - result[i] = (byte) (high * 16 + low);
: i9 _+ a7 W+ a, F8 \ - } 7 L6 h4 a0 |9 j1 P4 x8 r
- return result; ) p1 O. @- Q5 y$ A# a
- }
3 _" }4 j9 o/ e
" ?+ ^8 q( O3 T3 T" w- - L8 O8 u8 l0 s+ n
-
/ ~% p5 }# v; U7 j; V; e/ b - }
$ q, Z3 s: b/ u+ b+ v, d, Y4 P
复制代码
% ~0 U% Z9 |1 F& [1 G$ j* J6 l以上在linux测试ok 原文转自:http://konglx.iteye.com/blog/954085
+ S( D, X3 U O4 [: ^( s5 r) {! W# m3 `' O% V1 h9 h
& r$ q2 i3 ?4 A8 p; u
. o- j$ q" J$ ^( w- S4 L
|