现象描述:
6 F# P3 w1 \1 P* @) F$ R
0 M8 M: v j0 }+ N+ g$ Y! `: L) Awindows上加解密正常,linux上加密正常,解密时发生如下异常:. C8 ]4 r" {6 Q6 }& I$ W1 B' Q
1 L' ]) Q% J6 d. E- a: h
javax.crypto.BadPaddingException: Given final block not properly padded
6 u- V0 Z. j/ ^2 X& M7 |( q; B at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
7 x5 j/ s( o c8 d! C# h& W at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)8 n! V8 X3 y3 t( s
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
" C9 [. Q- U" F1 I9 ^" \- m3 Y at javax.crypto.Cipher.doFinal(DashoA13*..)
3 ~2 y; O) A" G Y at chb.test.crypto.AESUtils.crypt(AESUtils.java:386)$ K5 }4 v: Z% t/ }: Z! C6 s
at chb.test.crypto.AESUtils.AesDecrypt(AESUtils.java:254)- o, y( Y4 v2 P5 _
at chb.test.crypto.AESUtils.main(AESUtils.java:40) 解决方法:
/ W5 v& p6 ^" F2 c6 b; V% {
" ]% _& d6 A- p) H经过检查之后,定位在生成KEY的方法上,如下:
. Z) r& M4 T. {, a- public static SecretKey getKey (String strKey) {
/ o. @3 C) @8 z# o - try { . R( o" x$ S& q9 h1 [
- KeyGenerator _generator = KeyGenerator.getInstance( "AES" );: K: S5 P/ [, H' X0 U
- _generator.init(128, new SecureRandom(strKey.getBytes()));8 [# D+ U/ ]* H
- return _generator.generateKey();
% }: Z/ ?7 N, v3 a9 g - } catch (Exception e) {
: y9 r! C: n7 e* O9 h3 F. s/ m - throw new RuntimeException( " 初始化密钥出现异常 " );
$ U* j; u" y4 c @2 w; o0 x3 F - }, i" C5 H7 K% F& y+ i9 S
- }
复制代码 修改到如下方式,问题解决:
h+ V" C6 c C! r- public static SecretKey getKey(String strKey) {
9 `" i c/ o$ ^0 D, J: | - try { - x' Z4 c) }' }+ T/ g
- KeyGenerator _generator = KeyGenerator.getInstance( "AES" );7 Q9 C% c2 G1 J3 V/ W/ {. [4 y( ~" J
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
' v: G! u0 x/ z0 @' w0 {) I9 } - secureRandom.setSeed(strKey.getBytes());
5 K' E: @4 w: _% Y - _generator.init(128,secureRandom);
+ T$ T" c$ I" z" z' k) M0 ~# p - return _generator.generateKey();
5 w* O7 S- @% d: m; R y - } catch (Exception e) {9 d6 |, \: Z8 s4 z4 f) g; a. I
- throw new RuntimeException( " 初始化密钥出现异常 " );. {3 D$ f3 s0 M, R V2 t
- }
! W. {: B7 y) L5 } - }
复制代码 原因分析3 ~, J' b& K2 g& k! T% K9 n
SecureRandom 实现完全隨操作系统本身的內部狀態,除非調用方在調用 getInstance 方法之後又調用了 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。9 r* m, q3 Y: s2 m# o9 M+ K2 B% R
/ e+ o: ^0 P2 |- G; T! R原因二:
; f- h# {+ ]8 O6 _1 n* G/ `. j$ U" f4 J7 V7 ]( h
1、加密完byte[] 后,需要将加密了的byte[] 转换成base64保存,如:
* o/ H$ M$ y# h; T- m$ Q, ^, G5 uBASE64Encoder base64encoder = new BASE64Encoder();
9 c n+ g, A* y+ b. g0 K y4 k, KString encode=base64encoder.encode(bytes);
% q! j3 q5 M8 D7 y3 R. E, m) U: ~2 I) Z3 [. S4 A7 D
2、解密前,需要将加密后的字符串从base64转回来再解密,如:
( W+ G$ k" y" _' lBASE64Decoder base64decoder = new BASE64Decoder();
% @* j) F. J2 n; ]7 Q% fbyte[] encodeByte = base64decoder.decodeBuffer(str);
y- Y+ d% A! K! ^3 l% `- E0 n. j; ]( T7 T" r
" Q7 w+ \* s1 `4 G' W完整例子:
& S9 [+ b$ N9 f+ ~- package com.travelsky.tdp.pkgStock.util;1 V/ {$ f, d: L* S+ Y' t
7 G3 S4 f3 [" E* [1 q- import java.io.IOException;
Y4 q& K6 Y2 W% `* [/ | - import java.io.UnsupportedEncodingException;
) g9 T7 G& N+ L( H- x; Y( G - import java.security.InvalidKeyException;' M: T# L6 H. y
- import java.security.NoSuchAlgorithmException;) q% F, f4 O3 L: e
- import java.security.SecureRandom;
! P/ Q/ u! \7 K3 ?2 l - 3 }, T, }) {. L
- import javax.crypto.BadPaddingException;$ M; P6 r: x/ j2 p" w" k; g- p
- import javax.crypto.Cipher;
% |: g6 ` V; u) M, A - import javax.crypto.IllegalBlockSizeException;
5 h* T, @* i8 ?( l- c( B - import javax.crypto.KeyGenerator;* g! f, s: V* |. A x4 L- d& H
- import javax.crypto.NoSuchPaddingException;
# |# J7 p" i6 V# v l - import javax.crypto.SecretKey;7 b- q5 y0 b; u
- import javax.crypto.spec.SecretKeySpec;
' ]' u7 T# y1 B& \7 @& C - ! p) t$ C2 Q! Z" c7 J
- import sun.misc.BASE64Decoder;1 K7 `. U9 ]. k# A
- import sun.misc.BASE64Encoder;
% }+ B4 {& @5 ~( i3 x - / {: }" W" A1 M8 U- y9 I/ j
- public class SecurityAES {# g9 P# l: c# a8 _7 Z+ |
- private final static String encoding = "UTF-8";
) V, G- I% C9 P - /**
) k8 N! s0 _9 y2 \- { - * AES加密
: I+ A7 C% D& L: i3 r - * ; U) d& h6 \# S
- * @param content, p& R. \2 n$ h- n7 O6 d
- * @param password' E w6 L( m6 \! s1 v& _' g. a6 f
- * @return B+ v. Q) W6 H9 C8 c/ S: V5 M; r
- */
5 b: d' P1 U- n; Q8 D* X - public static String encryptAES(String content, String password) {' b f/ f0 Z" R! X' e9 k
- byte[] encryptResult = encrypt(content, password);
% N' F9 q) F7 v6 f! a' {+ o - String encryptResultStr = parseByte2HexStr(encryptResult);
$ m) q7 n% B" l; ?6 v, J, D - // BASE64位加密 C. j- ~6 d0 q8 M8 e4 Y; y/ b
- encryptResultStr = ebotongEncrypto(encryptResultStr);4 _/ B! y0 y0 A- _5 @. b
- return encryptResultStr;
& D* V" a' L4 {' X" n! k0 G - }
d& v2 a0 `/ ]! D
1 s; F6 {' }% X8 w2 o- /**& Y2 @0 L6 J: o, _$ T1 p
- * AES解密
4 ]- ^. E2 @& z- _' ~- { - * 9 m7 g, C1 w. r) e& e. |" u7 f
- * @param encryptResultStr
5 C8 i& }% u3 C" e% Y: B - * @param password
4 l. I/ e! B2 K - * @return
3 I" ?5 ^) p3 v1 k& z - */
4 j6 w1 y, J* w4 ` - public static String decrypt(String encryptResultStr, String password) {
$ y9 D8 @# o3 U6 L: H - // BASE64位解密, @ h b0 r6 t- `6 B, F5 l
- String decrpt = ebotongDecrypto(encryptResultStr);
- ~ o8 F, I) Y% ], n - byte[] decryptFrom = parseHexStr2Byte(decrpt);4 N+ W# `8 r& {7 n2 a. Y# X& T
- byte[] decryptResult = decrypt(decryptFrom, password);
+ K ?4 ^$ e& O. }' L( N; x6 O - return new String(decryptResult);0 @0 D [$ Q) E3 E
- }
+ [4 r3 x9 r; c' o% d$ s( ^ - 9 n# v, ?0 A z5 X8 J* |& A
- /**
( Y `+ f2 K; J4 E$ [, C# e9 d - * 加密字符串
$ x( m; G4 @1 o4 d - */1 M& h1 {8 G: \9 y# D6 r3 V
- public static String ebotongEncrypto(String str) {% `( E8 U9 G. e0 T. N8 s5 H: g) `6 |
- BASE64Encoder base64encoder = new BASE64Encoder();- y( C/ t4 x4 z* G
- String result = str;8 D1 @: P2 L/ P( s
- if (str != null && str.length() > 0) {0 h N2 r3 v( ?
- try {
z4 Y& Z2 `! h - byte[] encodeByte = str.getBytes(encoding);
4 |6 U2 `. N" j+ o - result = base64encoder.encode(encodeByte);7 p% n* `/ L8 L+ L" E+ j3 ~
- } catch (Exception e) {5 u# r' X. ^0 n2 Q
- e.printStackTrace();
9 [) v7 g8 ^" s5 r. k0 l( v - }
K. U5 b% M8 F5 m8 B - }
5 V8 m+ {2 S4 L" |$ j3 P - //base64加密超过一定长度会自动换行 需要去除换行符
" _' _) n+ b: K5 Y) N, T0 [0 v' K3 \ - return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");3 Y+ a* ^( g% i0 k0 f$ e/ X
- }
+ \! Y# [) h3 u: c% @% q: C# | - ; Z1 Y7 Y. \& O5 ]8 q0 A
- /**, L! W% h7 }* x
- * 解密字符串/ G0 F8 S7 l, f( h" B
- */8 s4 ?; i4 U: H0 a4 G. {" J3 z
- public static String ebotongDecrypto(String str) {
4 q* a/ b0 n6 _ - BASE64Decoder base64decoder = new BASE64Decoder();
$ g. Y( K+ s" F0 I# w - try {
1 u, U/ {0 q, n+ o$ F - byte[] encodeByte = base64decoder.decodeBuffer(str);: p& k' L/ T; e; V5 h+ n
- return new String(encodeByte);
9 F, ?8 ^2 Y6 H5 [+ U1 J" `6 m) F - } catch (IOException e) {
+ E ?1 F/ D, @' |) o - e.printStackTrace();% M' u. i8 }4 Q7 P% z7 r
- return str;
7 T0 j/ d9 x# ~8 P6 F - }4 r; c( r/ B! [6 R) n8 F, y
- }* y) f: h9 r1 t7 n8 U
- /** ( J! V" H! A& K, j4 N
- * 加密
+ u8 a4 p* ?' k G$ g" a - *
0 a$ L4 S' Z: a, J% H: R, A: _ - * @param content 需要加密的内容 , T: g" Q6 ~3 G3 w7 M" p
- * @param password 加密密码 * s4 E+ z* I! m- s, K- F
- * @return
- e$ B0 R' c7 W! t. y - */ # m5 |" O( p" h9 l8 s: f1 `' I+ f1 Y, C. q
- private static byte[] encrypt(String content, String password) { / `1 J6 J$ { o) M& N
- try {
% f1 E! \9 o9 I- ^ - KeyGenerator kgen = KeyGenerator.getInstance("AES"); ) S: s) J8 H( @$ H
- //防止linux下 随机生成key. Z1 ^3 ^$ c- O/ I1 N
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" ); 2 J2 T) U2 O) R. T9 z' W# \
- secureRandom.setSeed(password.getBytes()); + B, p. @2 L! i0 F
- kgen.init(128, secureRandom);/ u1 @: b4 J9 b i- G( I# f
- //kgen.init(128, new SecureRandom(password.getBytes()));
# f' l- } G# B6 ]3 T8 s9 _ - SecretKey secretKey = kgen.generateKey();
9 G; N* Y$ P5 I5 \" @ - byte[] enCodeFormat = secretKey.getEncoded(); 2 u7 Q# ^4 n0 X0 M) ~- S
- SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); $ j7 S/ P' t/ ]$ a! d& z
- Cipher cipher = Cipher.getInstance("AES");// 创建密码器
5 V8 M! W+ { a" A$ h K/ L& e8 z - byte[] byteContent = content.getBytes("utf-8"); . E- ^: h; ~8 y; F$ v: t+ p0 R& A
- cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
% h$ f4 A7 Z* y+ ]9 y; m6 o - byte[] result = cipher.doFinal(byteContent); 8 s% ^( k5 k; o! Y- l4 T
- return result; // 加密 4 ]! z7 Z3 ]8 q" Y2 h' D) g9 B+ t
- } catch (NoSuchAlgorithmException e) {
7 _3 [7 @, K2 w3 D! `9 Z: w; U: X! F - e.printStackTrace(); & M5 x6 L9 g6 k7 H
- } catch (NoSuchPaddingException e) { 0 L5 E: m* @2 V; A, F
- e.printStackTrace();
[" q& F" [- H - } catch (InvalidKeyException e) {
7 S* |4 ~* X* ?- T, r - e.printStackTrace(); ) j4 `1 A2 b' u3 o: t
- } catch (UnsupportedEncodingException e) {
9 W9 U' n$ X& H+ `% L - e.printStackTrace();
- ]+ `( ^( ^8 K$ S - } catch (IllegalBlockSizeException e) { " c2 t& Y! `& H( I. _( `
- e.printStackTrace(); * o% G: Z! i8 r, |$ k) s
- } catch (BadPaddingException e) { & | j" k& W i( w/ C* T) A0 v
- e.printStackTrace();
$ o1 m; [9 r. H+ v/ g - }
8 H. j1 W+ c/ F, V - return null;
$ A- k; _1 W) F. M; O" @) Q) l' N - } 3 Y# o3 b0 E7 D- {2 Z
- 2 A8 n# G) M9 \, V H
- 7 y& N! |" v$ Y; V O) x
- /**解密 3 }# K6 u8 e, x1 m
- * @param content 待解密内容 1 P$ m! S! N3 I+ A H2 s
- * @param password 解密密钥 " u+ v' a3 g$ M1 C- [
- * @return * A, q0 Y! w2 m- R% M4 n4 W
- */ & e$ Q( o4 S6 V, l5 q
- private static byte[] decrypt(byte[] content, String password) {
! |' E/ f2 [, r1 Z* e; y - try { $ @& W3 A% a5 E/ J* s/ N9 a
- KeyGenerator kgen = KeyGenerator.getInstance("AES");
" J; t- h& c7 h, K% B) \% i - //防止linux下 随机生成key
% s' Z9 w- h/ ~: T" w - SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
) T, `" M7 B& n1 f- Y. f2 F+ R { - secureRandom.setSeed(password.getBytes()); / k0 a) a; b: j3 X
- kgen.init(128, secureRandom);3 C& c, x* o9 L5 D G& K
- //kgen.init(128, new SecureRandom(password.getBytes())); 7 u* E5 _5 S. ?$ [2 x0 |
- SecretKey secretKey = kgen.generateKey(); $ V% H" Y9 }4 F$ W0 a
- byte[] enCodeFormat = secretKey.getEncoded(); $ @) U2 X. L# L* T3 Z
- SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
* k/ K' ^; U* y3 G/ t @ { - Cipher cipher = Cipher.getInstance("AES");// 创建密码器
2 B1 l- ?# f+ ]/ x - cipher.init(Cipher.DECRYPT_MODE, key);// 初始化 - j& M% L( h. Y: Y, U: F+ n
- byte[] result = cipher.doFinal(content); ! O" M4 H( q/ [8 }- g# P d! r
- return result; // 加密 . B- D& X! l; ?! ?' P, R
- } catch (NoSuchAlgorithmException e) {
# W7 Y5 ?/ I+ o$ ?: Y9 h& d - e.printStackTrace();
4 V4 F- _0 |0 `5 y& Y i4 {- j - } catch (NoSuchPaddingException e) { 9 z& Q2 e1 W2 T/ i7 H; ^
- e.printStackTrace(); $ a o% L# y& C
- } catch (InvalidKeyException e) {
: [. X! f+ M% d7 T0 S) I - e.printStackTrace(); + v& o1 [, i6 l) W' T
- } catch (IllegalBlockSizeException e) {
9 K. \2 \( `2 V; o# {9 C. s( L - e.printStackTrace(); ' g+ V9 a: v) Z* Y$ ]6 I& {
- } catch (BadPaddingException e) {
2 e/ Y. [# J6 Y - e.printStackTrace(); % ?$ k+ @/ ?0 X6 `
- } 8 o: m" _! u# r! F; h* l* v% [( ?( E
- return null; - ?% I/ ^& k- R, X1 T# N
- } 2 A$ T, d, y% M! }
- " O9 E; `( K$ Y1 y6 d, `) @
- /**将二进制转换成16进制 0 X! l! E8 _5 [
- * @param buf $ y6 w7 F! M% \* L Y
- * @return * s& g: F6 [* j* ]! K
- */
! k0 b. `, N; Y' A% _% c - public static String parseByte2HexStr(byte buf[]) { ) O- F% O9 z' J3 ?9 M% f9 X( _% _
- StringBuffer sb = new StringBuffer();
" E& O! P' ]: X0 x - for (int i = 0; i < buf.length; i++) { / u' F4 ^( k7 R5 d* i" N/ [( D
- String hex = Integer.toHexString(buf[i] & 0xFF); ' Z J( |/ a, ~% [ z; k. H
- if (hex.length() == 1) { , W4 O, j f e4 S! B5 H
- hex = '0' + hex; 5 G- E6 |; k c7 q j
- }
' E5 ~7 N; e. }* u L, `- c - sb.append(hex.toUpperCase());
: [2 t1 q: _8 ^8 U1 s - }
" R5 I' ]4 J/ P" S, M - return sb.toString(); ( P* u6 _5 A" d8 \5 J$ R
- } 9 M% M" d' l! G# w
- h9 ~; C( j/ q
1 x4 G( I2 u# q4 I, F8 l( D( U# Z7 W, h- /**将16进制转换为二进制
- F) D% u$ @7 v4 z5 H( p; y - * @param hexStr
7 s$ r. j! O r. [% c J - * @return
# K9 o! V8 F4 B* j' h V - */ / w6 t3 Q+ }2 B; M4 M+ l0 n% p
- public static byte[] parseHexStr2Byte(String hexStr) { 0 E4 Z# m, m7 c/ `' H* B" T0 q
- if (hexStr.length() < 1)
8 j2 W) X; l, C0 a# _. `0 \ - return null; " q) n% E& \/ q3 p( u# `
- byte[] result = new byte[hexStr.length()/2];
$ Y9 ]$ ?+ m9 g& G! ^ - for (int i = 0;i< hexStr.length()/2; i++) { " \) v& q: ~( }* l6 _& z x5 l% W
- int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); 2 a' c+ |& A" p# q2 a, z4 {1 N
- int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
2 k/ a- {! {$ p- _- Y |4 A - result[i] = (byte) (high * 16 + low); / |3 V E u& y' f. E- \
- } 8 [) \# s; `5 I( F1 x
- return result;
. J! A" z0 m0 Z, Z, ^' j - }
; U/ r! ?% ^& w! n# V, C
, F. a0 i b K: H1 x
/ U2 o! p) E$ Y, u+ v-
3 \0 Z5 m- A: `8 x& R# U7 V6 {) A% r - }8 G9 G# b4 I+ G; ?* E3 w
复制代码
! V0 N5 @, e8 l1 H' ?5 h+ e& B以上在linux测试ok 原文转自:http://konglx.iteye.com/blog/954085
k5 K( o- M$ r1 z: {3 v; l4 P7 l- }3 c
- P& N; @1 I+ o! S1 J1 @7 M* M' o8 g+ X9 z! D! p: m% {
|