现象描述:
9 B' v" V7 k9 d% w6 n/ W7 \
3 ]! f4 i* [5 Vwindows上加解密正常,linux上加密正常,解密时发生如下异常:( L: k* T( e7 p/ d5 `) p P$ N
5 R* I) d9 ^1 ~# m v7 b: i
javax.crypto.BadPaddingException: Given final block not properly padded
7 f* W% o P7 k) F* w6 z4 I* D at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
- A( ^ y# d' R- |( Y+ s at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)6 H1 E- W4 ^, ?5 M0 m& x
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..) h) R( w; \# |( P& S7 K8 G
at javax.crypto.Cipher.doFinal(DashoA13*..)
! l3 V3 q, \" \; t: X at chb.test.crypto.AESUtils.crypt(AESUtils.java:386)- c8 ^ c8 }) O) ?
at chb.test.crypto.AESUtils.AesDecrypt(AESUtils.java:254)
: @! J7 [8 O. q7 i: ?0 ]% g( k* ?/ I at chb.test.crypto.AESUtils.main(AESUtils.java:40) 解决方法:
1 `( W) |( k" K5 G$ y% d7 Y/ \0 i! }" v$ x4 m, T7 o
经过检查之后,定位在生成KEY的方法上,如下:
. P! z5 v4 `! d+ M6 s- public static SecretKey getKey (String strKey) {
! W* {$ v8 v- x8 U/ `% K% q( x - try {
6 y; l9 C$ G+ S: } - KeyGenerator _generator = KeyGenerator.getInstance( "AES" );
, Z5 u4 S& C, e3 O* ?, b5 r - _generator.init(128, new SecureRandom(strKey.getBytes()));
, Q- n2 \5 N. K; ~$ F; ?9 N - return _generator.generateKey();0 }) O1 @$ T+ j
- } catch (Exception e) {
' R2 Q; u n/ f! U6 P# H - throw new RuntimeException( " 初始化密钥出现异常 " );
. |% C! \3 q, w' S g - }# \7 k& j! Z+ O& l
- }
复制代码 修改到如下方式,问题解决:# p% a1 [4 A( o
- public static SecretKey getKey(String strKey) {
, o$ f& h) O; d' d2 `. {* A - try {
# |% e0 [0 e8 ^; P - KeyGenerator _generator = KeyGenerator.getInstance( "AES" );* f; t5 R z) Z$ T x# j3 w' C6 c
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
, T1 Q$ \( L }8 m - secureRandom.setSeed(strKey.getBytes());: j3 Y9 ^, v; t+ f. ?* l7 ?/ I* ~
- _generator.init(128,secureRandom);
4 r( `6 e$ S" M! g" I - return _generator.generateKey();
; o4 ]4 e8 e) n/ X% L0 C - } catch (Exception e) {
$ j$ l7 ]2 T- ^4 j0 Z - throw new RuntimeException( " 初始化密钥出现异常 " );
* F/ x! I' d9 ]. J- P - }
: I& h6 i& D5 M: T - }
复制代码 原因分析) {6 A1 q1 J$ T; i# D! p3 a+ g0 y
SecureRandom 实现完全隨操作系统本身的內部狀態,除非調用方在調用 getInstance 方法之後又調用了 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。) s( f# u5 z2 G3 N% G
( _9 R& P8 O; f h" D/ B
原因二:
8 W3 J6 D4 j* V7 v2 ^! _
4 N8 G+ s4 T$ ^1 g" g1、加密完byte[] 后,需要将加密了的byte[] 转换成base64保存,如:
. O8 R4 x4 d7 W# }* [# ?BASE64Encoder base64encoder = new BASE64Encoder(); 4 Z9 J" L( {& c+ s" u+ g& n* b
String encode=base64encoder.encode(bytes);
1 R+ O- C+ K: V( y
9 L/ o8 P* S4 z& c! R) i2、解密前,需要将加密后的字符串从base64转回来再解密,如:
1 r% [- p( q; P" fBASE64Decoder base64decoder = new BASE64Decoder(); 6 i$ C/ F/ t8 o- x* v
byte[] encodeByte = base64decoder.decodeBuffer(str);
1 K' ^$ [ R* F8 K1 y! V; K" S6 v+ v
8 y: D, N- D, @. L) s- w* i0 J; Z! _* K! F9 U
完整例子:0 E8 M1 q' h6 ~! z
- package com.travelsky.tdp.pkgStock.util;
3 P; W0 ^9 u2 \! w
, H7 Q& s F; j7 z+ `- p+ f8 _5 `- import java.io.IOException;
_3 V4 Y1 L+ D+ m - import java.io.UnsupportedEncodingException; ?- q6 r E7 z) v
- import java.security.InvalidKeyException;
- ~5 |) ^; M# s6 g - import java.security.NoSuchAlgorithmException;8 o4 _- p- ]; K7 ]4 b3 u C7 v
- import java.security.SecureRandom;
0 ?) V ]# q9 ?/ r
0 P$ R' K. ^6 K2 [; J, b6 U! x2 W- import javax.crypto.BadPaddingException;
. Y0 _& E; ^% T+ i1 n# o - import javax.crypto.Cipher;, x) l( a! y3 a y; ]3 H
- import javax.crypto.IllegalBlockSizeException;- n+ f* U1 t, C. Q+ ]! F3 b, Y
- import javax.crypto.KeyGenerator;
! i) S+ B: Q6 w) v5 S3 u - import javax.crypto.NoSuchPaddingException;2 w# Z* E; y* D# N
- import javax.crypto.SecretKey;# C6 X7 J7 p. J2 F/ R
- import javax.crypto.spec.SecretKeySpec;
+ U' E' F; A) f, p3 z- w: w - 2 V: L+ O- h( P+ o
- import sun.misc.BASE64Decoder;
4 v2 v$ V7 R$ F& ? - import sun.misc.BASE64Encoder;
. o* z7 L, K3 ~: d
4 @/ ? b& G Z3 U0 |- public class SecurityAES {2 Y! {- h- q: C" u
- private final static String encoding = "UTF-8"; c8 E: f0 e6 n
- /**
7 r M! o h6 p3 G - * AES加密0 k5 U9 K, I' Q# j! X
- * 6 ]& t! a% l5 u( j* ?6 z7 e8 X
- * @param content$ |! v8 I# e% m, X M6 w1 T: ]; d% G
- * @param password
, p$ I) A4 j- K - * @return
7 S' K& x g) m9 _4 ?4 c% @# C& _- y - */' F. K( z+ J M8 H8 N6 o( P
- public static String encryptAES(String content, String password) {
) j. C& A5 h0 ~ - byte[] encryptResult = encrypt(content, password);/ ]& K' \! \! `" i7 L
- String encryptResultStr = parseByte2HexStr(encryptResult);
5 x7 a% T( f( p* x0 C3 |0 F - // BASE64位加密
5 \1 Z4 C! z- L - encryptResultStr = ebotongEncrypto(encryptResultStr);7 N* q8 V! P$ A) p# _
- return encryptResultStr;- J5 [5 X, D. _
- }
& V U% t% \* N, |
* X/ b* e( X$ P- Z+ k9 E$ W- /**
3 o$ o2 I8 B/ s - * AES解密" k+ z6 f" N7 E6 R, ]; P( _; ~
- * / ~" c& \: E' r: {6 }0 q, I
- * @param encryptResultStr, A* ~0 b) D* E+ M
- * @param password
' ^& N0 ]- o2 x/ I - * @return
C2 s8 y# B- U. w8 B# C5 x - */
, d0 i' l. R: p1 o - public static String decrypt(String encryptResultStr, String password) {# F* J; _* i7 f+ S
- // BASE64位解密
4 k& A Q7 M3 j) I. {; T8 N* q - String decrpt = ebotongDecrypto(encryptResultStr);' u% m+ G5 ^" n6 H; M+ t1 C
- byte[] decryptFrom = parseHexStr2Byte(decrpt);' M2 r8 Y! k' B+ B7 X- [6 {: b
- byte[] decryptResult = decrypt(decryptFrom, password);9 ~0 ]$ v1 J7 s# A, _, i+ R# {
- return new String(decryptResult);6 u/ Q& N% k, I7 B2 S
- } ^+ D' R7 @. z. @4 ]# s
- & ^9 z; K" b1 @) D1 Q, e1 ^
- /**) y& X2 y ]. `" G% \4 ~1 k9 K
- * 加密字符串& o/ x7 @: D' j9 h, F8 U9 h, `
- */
; @- p# n, ~' m# [. V4 \+ W+ z7 C - public static String ebotongEncrypto(String str) {0 z" F- H% f- I1 J% u0 M9 x' J0 o
- BASE64Encoder base64encoder = new BASE64Encoder();
& a: m2 s; n/ |: y: L# q/ Y2 d$ Z - String result = str;
# |' c O1 x- T F. ~3 w8 Y; D T - if (str != null && str.length() > 0) {
+ N% J# @6 Z% p# I3 w. C3 C6 t - try {
2 F4 c3 v+ N! R& d; p - byte[] encodeByte = str.getBytes(encoding);0 Z: s: q9 A; c1 c
- result = base64encoder.encode(encodeByte);
0 E' e9 S3 q1 k" P+ z j - } catch (Exception e) {
5 q _9 O& V8 i( |- V - e.printStackTrace();3 d* H# H8 H- {/ j( s
- }
1 o- ^( ` b3 H1 G6 Y5 A" l - }: t! T* Z+ G" D* Q/ m- O2 a, @$ ]
- //base64加密超过一定长度会自动换行 需要去除换行符
& F) [5 m3 ~ q; y8 y - return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
0 d3 m/ N, d" U9 [ - }
; n* {0 O# @1 ]# U; m) o! J - $ r( `7 N4 u8 r' h
- /**
) ^2 B/ `" B4 X3 C - * 解密字符串/ x W9 G( ]+ c* E$ R
- */
; C" i2 B: X, `4 T - public static String ebotongDecrypto(String str) {
3 E5 Q2 W J" S0 L; Z: q - BASE64Decoder base64decoder = new BASE64Decoder();/ |+ Q5 e& ^* j( \& I$ V
- try {
& D+ |& \+ v% [$ J9 p - byte[] encodeByte = base64decoder.decodeBuffer(str);* H4 E' ~7 G, t% x; h/ f( [
- return new String(encodeByte);, Q! |5 i. s4 L8 [5 o. i/ w/ G1 a
- } catch (IOException e) {. z" Y: o/ {2 L$ z- P. {2 K# J& d
- e.printStackTrace();" d; v" |9 J, I: F3 g1 [
- return str;
% ~+ L: _4 [8 y- l - }' p- [9 k8 y" s; v+ m2 i3 q t3 G4 X3 {
- }9 [' Q W( x. d+ q$ ]# h# y0 j
- /**
* ^: k' K2 |- ^( [ - * 加密 ! `! |9 Y) T: q6 W* a% f
- *
6 L# E3 v7 [3 U+ v - * @param content 需要加密的内容 % X2 B" @& `! R# R* j" C4 S
- * @param password 加密密码 ' w7 }. d* q+ J: C
- * @return
/ [% v" z0 v8 z. D$ l& K - */
! |7 Y) ]* l6 O* @# j! N9 A% [ - private static byte[] encrypt(String content, String password) { 3 S3 h9 S; j" E0 v
- try {
- g4 \4 |- S2 `4 u4 p" ^# m8 v - KeyGenerator kgen = KeyGenerator.getInstance("AES");
! {0 V, T5 Z# a5 J - //防止linux下 随机生成key0 \+ Q$ n* W% U2 K0 R8 M) n- P: c
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
0 @3 G# Z$ V3 O+ ~5 K2 \5 h! i) D - secureRandom.setSeed(password.getBytes()); 0 S' c$ M, ]* }7 K2 a
- kgen.init(128, secureRandom);8 a |% J3 P8 I4 `/ q `7 O
- //kgen.init(128, new SecureRandom(password.getBytes()));
1 s- R; l6 { T/ I! v' U3 R/ n - SecretKey secretKey = kgen.generateKey();
3 B& _* K2 N9 J( [ - byte[] enCodeFormat = secretKey.getEncoded();
- U4 d3 M8 M3 C& {3 X; x - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); ( N) m' a0 s$ u& n! h3 K# B0 a& p
- Cipher cipher = Cipher.getInstance("AES");// 创建密码器 - A! B% _5 p# s$ @# P
- byte[] byteContent = content.getBytes("utf-8");
& @& T8 t' `' F/ T% U- c! X - cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
/ \- o f6 {0 B s$ ]! ~' ?8 I - byte[] result = cipher.doFinal(byteContent);
/ E4 A, t% L2 e3 Q: ` - return result; // 加密
2 k4 D0 a2 f0 T5 [% n- m# e) D - } catch (NoSuchAlgorithmException e) { ; ]+ E, H% ^7 `4 f" w
- e.printStackTrace(); ) R9 @4 G; O! D
- } catch (NoSuchPaddingException e) { 5 ], p0 S# \ x3 K& J z
- e.printStackTrace();
- Q- @' P1 |0 J# b. k9 L - } catch (InvalidKeyException e) {
/ ]) D2 L; K4 X7 Y4 t3 }5 r' s - e.printStackTrace();
8 }" a, }2 v5 q6 m( t. o - } catch (UnsupportedEncodingException e) {
' \8 ]! d9 X- {- I c8 f# e6 m - e.printStackTrace(); ( I, s- y; l C! Z3 ]
- } catch (IllegalBlockSizeException e) { / t. j, K& v( }+ i ~
- e.printStackTrace();
' j; c- W! D) r6 J - } catch (BadPaddingException e) {
3 t1 U# t+ ]" r0 o2 o - e.printStackTrace();
; d" m9 a( m& ^ - }
8 J2 a2 b) D* C - return null; 4 }9 \( k6 e* Q
- }
7 y5 S$ E/ l' U7 k* f$ L9 W
' `2 H4 l: s; R; ]6 ?
( O5 ^! |& p e7 u5 {0 S- /**解密
1 g. ?# {: R. x2 ] - * @param content 待解密内容 8 b; x6 @! L" M; O1 w) W8 j+ } r
- * @param password 解密密钥
# s: M+ ?# A1 E - * @return
2 h5 K: w# K; U3 q& I - */ ( N% _' P1 f* c( B& H
- private static byte[] decrypt(byte[] content, String password) {
# p- g* M' u& S" t1 Y1 [+ D - try {
& ?4 W3 \, m- c) j+ O9 X/ I - KeyGenerator kgen = KeyGenerator.getInstance("AES");
; l, l& h) o& I, h$ D - //防止linux下 随机生成key
1 s" E% e- K7 W8 K8 ?/ m2 g - SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" ); * Y h$ }5 W+ D- ~. v
- secureRandom.setSeed(password.getBytes());
, `, X; b1 E j z - kgen.init(128, secureRandom);' G+ S. K& F- z/ Q
- //kgen.init(128, new SecureRandom(password.getBytes()));
( L+ W' r+ h2 ^; K6 E2 j8 r; y - SecretKey secretKey = kgen.generateKey();
4 h' U6 U V. A- b4 {# F# Q/ O/ U - byte[] enCodeFormat = secretKey.getEncoded();
) P2 f- Q+ g4 n" l2 u6 o - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
) o3 T! J5 k( ?6 s - Cipher cipher = Cipher.getInstance("AES");// 创建密码器
% E# {! E- Z. s- ? W- L7 H+ x, n - cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 0 ~. E6 D! @+ j+ E& T
- byte[] result = cipher.doFinal(content);
8 Q* o: v1 U8 V* B g - return result; // 加密 + Z3 ]' x" @7 U9 }. G
- } catch (NoSuchAlgorithmException e) {
/ a; Z9 {5 {/ m9 {! l0 \8 O- B' o: Z - e.printStackTrace(); 9 \6 Q+ {0 p/ P7 l. i7 E
- } catch (NoSuchPaddingException e) {
+ l; R' D6 o+ f$ p) W - e.printStackTrace();
! {/ C/ z. N7 d+ G1 O - } catch (InvalidKeyException e) { : y6 `) l. ?; } b
- e.printStackTrace(); ; E; C4 Y j1 |/ h" J
- } catch (IllegalBlockSizeException e) { & f: v: i3 s: R b
- e.printStackTrace();
! g5 I9 S' Y8 _/ L3 c6 V' Z - } catch (BadPaddingException e) { 1 F. M" o: L7 a) {0 C
- e.printStackTrace(); 1 c4 a9 S# q E u5 E) J+ P
- }
0 I" F+ r* y2 Z6 N9 n6 z0 O6 { - return null;
' u1 z$ G y: y% y - }
! B: s. g4 A7 t9 A
" u; d3 o( [, r- /**将二进制转换成16进制
9 \ b8 C; t% z+ W, h! { - * @param buf B l0 u9 u2 M
- * @return
# A5 Z" \; u p - */
( E( z+ y0 X8 d6 L" @4 W; A - public static String parseByte2HexStr(byte buf[]) { ' L2 B4 a* } J+ ]( W7 B1 Q* j3 n3 `
- StringBuffer sb = new StringBuffer();
0 b7 Z& s( V- _3 ^; M( s - for (int i = 0; i < buf.length; i++) {
2 v4 l- G4 |% ~1 M) Q8 i9 s: h - String hex = Integer.toHexString(buf[i] & 0xFF);
( J# ~# e0 L: J" y - if (hex.length() == 1) { , J4 Q8 B, l% A( S+ \$ z- p
- hex = '0' + hex; , P( p7 W, [: f
- }
5 f% ?* n1 e# C: a/ u - sb.append(hex.toUpperCase()); ; _) g" J% T: x
- } 5 I2 q X8 }( J- W B
- return sb.toString(); ' e ~! J/ C4 h8 T" K' K- b. I7 g
- }
7 m7 _2 N3 k# ^ - ! \/ I1 @: B) \! M8 ^* S4 S& v+ y2 ^
: N4 P0 C2 K* I4 U) v# \/ Y- R- /**将16进制转换为二进制 ; k" }$ u% P& x$ |. h
- * @param hexStr 4 w( C# y9 w# R; |% r# e: P
- * @return ) L5 R( z( E P. e
- */
# B% h# |3 h3 W6 ~! J- [& V, ~- W% M - public static byte[] parseHexStr2Byte(String hexStr) {
. y( \: k; j# V& I - if (hexStr.length() < 1)
& I8 @: q* Y. l8 a, ] - return null;
& t* l9 k4 I$ Z: j/ g" T d - byte[] result = new byte[hexStr.length()/2]; " V# g: a; ]# n. P+ w+ v
- for (int i = 0;i< hexStr.length()/2; i++) {
+ R0 Q8 N$ l* @' @4 G - int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); $ v7 i# w' V! C( V! P( a0 q
- int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); , P8 X* l- ~" q& K& ]6 G/ C2 V
- result[i] = (byte) (high * 16 + low);
! ]) D# \8 s9 j) j7 I" Z: l - }
6 |' q) w3 Z: O0 J - return result;
5 u& x2 `& y2 q% ^0 I - } ) t* \, Y8 Y; m' M+ ?6 j% Y, {
- # I, c8 W" U5 \' Y* u
- 2 |; a/ {+ `8 ^. C
-
) S, t# `; P4 Z2 W( a- n7 ^ - }
* c' k1 A b4 x# B2 O0 l. [: Q
复制代码
# G2 W9 c- T, {" @9 N3 T以上在linux测试ok 原文转自:http://konglx.iteye.com/blog/954085" D& P4 I7 \9 H9 M( L6 b% K
1 _- F; @ ?6 N5 M
# W& K6 I& Z6 \% m: Q
L- S0 b$ f# s* D+ @, o |