现象描述:
, G" u# W% Z1 n. c2 A; Q; q0 n M2 C, e1 {1 R4 V3 m
windows上加解密正常,linux上加密正常,解密时发生如下异常:' f" ~/ h+ ~5 M8 v6 Z
+ U3 I6 L0 m) |javax.crypto.BadPaddingException: Given final block not properly padded+ ^4 ` \4 g* N* }: U
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
4 _6 ~6 O8 H; [: R at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)/ Y( k1 J. M: m$ V
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
9 }% c. l5 ]- ^4 l+ Y' T% n# o at javax.crypto.Cipher.doFinal(DashoA13*..)2 m! H3 }! n+ G' f. T
at chb.test.crypto.AESUtils.crypt(AESUtils.java:386). ]! l) R7 p0 M! F. i0 Y
at chb.test.crypto.AESUtils.AesDecrypt(AESUtils.java:254)8 s1 {, |- O: V5 B2 u
at chb.test.crypto.AESUtils.main(AESUtils.java:40) 解决方法:4 d, K* y P4 A, A5 V* N$ U
. ~3 s9 ]6 _2 D" B" y4 e
经过检查之后,定位在生成KEY的方法上,如下:
) |/ \: [- }. D: N- public static SecretKey getKey (String strKey) {! S7 w0 i' J) b$ ~+ q( C
- try { # B: Y; b0 P( V' a8 U8 v- E5 [
- KeyGenerator _generator = KeyGenerator.getInstance( "AES" );, j* v. s/ C- F2 ?. y0 H5 u3 E
- _generator.init(128, new SecureRandom(strKey.getBytes()));
( N: F( j' w, a! D$ U - return _generator.generateKey();
# u! i% }) A) e+ b7 e* ^+ i0 V - } catch (Exception e) {
# A9 w8 C# N! T6 u) o* m - throw new RuntimeException( " 初始化密钥出现异常 " );
. O A& ~0 r: S6 q0 e5 C - }6 u) _* M: f% `4 t- i
- }
复制代码 修改到如下方式,问题解决:
. E. n5 m; u) `4 p, f1 @- public static SecretKey getKey(String strKey) {
# d1 B/ _9 w; U/ q z# s - try { 1 p1 S; r$ x2 [9 Y
- KeyGenerator _generator = KeyGenerator.getInstance( "AES" );4 T5 C# |. I* N/ V4 A, ]6 m5 X5 N
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
9 U9 I# H" P$ ], b% d2 ^ - secureRandom.setSeed(strKey.getBytes());
: t8 g8 N0 q$ W* O' O: K! `5 \ - _generator.init(128,secureRandom);) R; {% ~+ c& ?, o% P
- return _generator.generateKey();
1 h( z7 o" m& r# `4 i8 o - } catch (Exception e) {
" u2 D: ~8 t( J2 @& ~ - throw new RuntimeException( " 初始化密钥出现异常 " );' R5 k" @; Y/ u) h* h/ ?% u
- }0 m& u: B% k N2 N) c, O6 @( s
- }
复制代码 原因分析
5 o- n' _8 G4 B; ESecureRandom 实现完全隨操作系统本身的內部狀態,除非調用方在調用 getInstance 方法之後又調用了 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。
5 Q/ _2 v4 C. D. X* R! l+ W6 j8 ]( c ^; R- o2 U/ r
原因二:
; @ l! B p( ?2 q* q. u, S, R! `0 r5 t; X7 J- V3 L) K
1、加密完byte[] 后,需要将加密了的byte[] 转换成base64保存,如:
0 n8 s( S% w% g7 A& v0 i9 P8 F) R- l( ?BASE64Encoder base64encoder = new BASE64Encoder();
8 _8 B. w$ ~+ f9 B9 D) U/ FString encode=base64encoder.encode(bytes);
0 G; m& R% C6 h& J9 s
! w, u% [) b F0 {% q2、解密前,需要将加密后的字符串从base64转回来再解密,如: $ q! @, g2 [ Q1 W8 W% f# _ j
BASE64Decoder base64decoder = new BASE64Decoder();
[- R. L0 z: |byte[] encodeByte = base64decoder.decodeBuffer(str);
7 n- Z6 N4 _! S* i, M" q3 t0 @
) D6 g v! ~+ K. |& \1 l, A
6 o; w* |6 \0 F- r完整例子:
1 Z* U% F0 o) I3 a8 h& p5 ?- package com.travelsky.tdp.pkgStock.util;
$ x# ~" b. q3 U( e; D
, ]) N1 J" L0 I8 | u$ S3 ]- import java.io.IOException;
. H n" b) v4 t2 O7 L9 k8 f - import java.io.UnsupportedEncodingException;
/ O: i) d( R) l& X$ P( e5 ` - import java.security.InvalidKeyException;' O8 c0 b u; i( o
- import java.security.NoSuchAlgorithmException;
5 o. W) N+ a7 N! ~. V3 @' u1 E - import java.security.SecureRandom;% l/ q! r4 i9 x$ B5 ~" V
7 W5 j: H9 K% ^% P' ?- import javax.crypto.BadPaddingException;
4 P2 ?, M2 f2 f! p8 ?3 ` - import javax.crypto.Cipher;2 E0 T0 I$ R( j! x C
- import javax.crypto.IllegalBlockSizeException;6 r6 d; L5 G" p4 I
- import javax.crypto.KeyGenerator;+ ~# b3 S8 Y* Q' E
- import javax.crypto.NoSuchPaddingException;
. e' D; t" k* a- o$ j - import javax.crypto.SecretKey;
6 e( C& U* C, q, S/ F - import javax.crypto.spec.SecretKeySpec;* N& x0 b# a) ]- c% H( W. e9 P
- & o }! B& s7 W- I1 {& }. ~; S# h
- import sun.misc.BASE64Decoder;" }9 E3 J( g7 M9 U- a' ?
- import sun.misc.BASE64Encoder;
' y5 O9 @% K' N1 S. v- |( d - + ^$ s, o& K) N6 q
- public class SecurityAES {
' _/ t, e2 y( s9 m; n; U - private final static String encoding = "UTF-8";
d. `( |& C6 ?" @" y - /**
) R2 w/ ?( i; e - * AES加密
- l* \2 z9 f& ~% B9 V - *
+ p' v( }! r# o* A: g2 y' i - * @param content
4 ~) M" I; ]: M5 O% G* W - * @param password
, i( Z M, j% e* | - * @return
% L0 p6 P" p7 p0 Q( u S& C - */5 R5 g: L( V2 W: p; H$ R5 A% }
- public static String encryptAES(String content, String password) {; |2 U7 t+ u% \- p! C4 s
- byte[] encryptResult = encrypt(content, password);9 F$ `0 ^* ]2 U9 P$ ~; Q
- String encryptResultStr = parseByte2HexStr(encryptResult);
. @6 x) j) h( l1 E" {" }0 F2 H- ~ - // BASE64位加密/ {% `& H; ^1 h) b5 n- C
- encryptResultStr = ebotongEncrypto(encryptResultStr);6 y+ V1 F: a* E Q+ I- z
- return encryptResultStr;
/ [. Y8 R2 e- u3 Q - }
* O* O% A8 F1 x- U9 R8 v$ ] c
! ]/ h; f+ d% C$ v6 l! S' O" l2 W- /**
4 L9 d) ^& i& @ s$ r2 S - * AES解密
: @ e! P, E8 Y1 {- L" J - *
! Q: I) i0 G/ G1 l - * @param encryptResultStr
4 O- h& q* K. J1 _7 c - * @param password
! k. y/ }% W* p - * @return# r1 C) w' M1 c" u/ o' [
- */9 Q2 s5 p. l2 R% o
- public static String decrypt(String encryptResultStr, String password) {( V/ N& z C' K2 _' z: B
- // BASE64位解密9 I% B* c0 x* O7 {, k X
- String decrpt = ebotongDecrypto(encryptResultStr);: q {4 ^! u# A4 U3 b" A
- byte[] decryptFrom = parseHexStr2Byte(decrpt);
4 ?8 K8 w0 Z1 ` v - byte[] decryptResult = decrypt(decryptFrom, password);- r/ ^8 V* { N- N
- return new String(decryptResult);+ i/ r/ R* Q$ Z: f* a
- }
9 f5 x; v0 C3 U& y( h1 D! K( p& O+ S% l
0 n% ]5 H& r/ i$ p1 ?( `- /**
3 }9 t; h) G7 q - * 加密字符串# m$ n/ F2 h' |9 o
- */1 z& j4 T. L$ E! X4 t
- public static String ebotongEncrypto(String str) {) q ~9 h9 ~3 s% M. V! a
- BASE64Encoder base64encoder = new BASE64Encoder();7 L# b9 n7 B4 z8 r: ]
- String result = str;
! `7 w8 s# X3 Z% Q+ T - if (str != null && str.length() > 0) {
* a F2 D3 H" z# x1 t* h/ [" S; H - try {+ F. ^& ?' A: H" N& g+ ]
- byte[] encodeByte = str.getBytes(encoding);9 o! X7 L# A* |' T( ^
- result = base64encoder.encode(encodeByte);
4 ]# {" o8 |9 C1 \3 t1 c5 \3 _ - } catch (Exception e) {) ~: f2 B7 J2 x
- e.printStackTrace();
2 Q" m+ }+ ]- ^2 }( |$ E - }/ o: x# |+ e. c2 J
- }
. m6 l3 k/ x& o+ p0 k# W: I - //base64加密超过一定长度会自动换行 需要去除换行符* {* V* c- |' O5 Y
- return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
4 J6 Y5 O. M' C( w P# y4 A- @ - }
) G4 Q4 u/ Z3 J8 ~! Y5 p5 T
& ~9 G7 @: @. l- /**$ v0 y. N ?" C6 B' Z
- * 解密字符串
5 Q' b8 Q. \' r, O$ ` - */# W# J) V1 h% s
- public static String ebotongDecrypto(String str) {3 ]- m4 J) T' R3 L2 j! b7 @) [- e
- BASE64Decoder base64decoder = new BASE64Decoder();- A- F0 k' G$ Y6 T$ Z
- try {
2 g# v5 G4 m. \- M1 m! | - byte[] encodeByte = base64decoder.decodeBuffer(str);: O5 X/ K! X$ `3 _. N
- return new String(encodeByte);
$ {3 t2 E( w0 `4 h4 O3 C - } catch (IOException e) {
2 J% y3 T& ^8 v3 v/ i& N( W - e.printStackTrace();
9 s& G- K, o0 R( J9 l' ], V - return str;
: H7 _- t9 x/ C- _- R% x - }! G R e& e: ^+ t5 N2 N% I
- }3 {! D @3 M. } `. t
- /** 9 F. Q3 X c; z" t4 H3 k
- * 加密
1 e2 W# O" {8 ^$ W; H' S% c7 L9 B - * - ]) L t+ q0 h/ ]
- * @param content 需要加密的内容
x% z3 J C0 r) s- I) \: Z - * @param password 加密密码
% K8 h. s( K% e- ~ - * @return
# w; J4 N( X& |" A+ D - */ 7 k- o8 }+ D& a; [- y Y
- private static byte[] encrypt(String content, String password) {
( f+ V- S4 R, I - try { * X. x$ m+ Y2 E, y$ s: P1 G" i
- KeyGenerator kgen = KeyGenerator.getInstance("AES");
1 X+ B( L! _8 y, z - //防止linux下 随机生成key
; g; m, W0 p+ e) K0 o - SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
3 V; @* L# J2 A0 s9 z6 Y" e% l" { - secureRandom.setSeed(password.getBytes());
0 {* Z" |1 f- \: N. N - kgen.init(128, secureRandom);
, t9 _7 P/ ~/ t3 ~8 {2 q - //kgen.init(128, new SecureRandom(password.getBytes())); 6 x w5 e7 b U6 ?$ `
- SecretKey secretKey = kgen.generateKey(); 8 c7 k$ }4 ]) @! L% I
- byte[] enCodeFormat = secretKey.getEncoded(); ! R1 h6 O4 Z, V) D0 i4 }* b
- SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
/ {2 T( g& y! S8 [ - Cipher cipher = Cipher.getInstance("AES");// 创建密码器 $ @ _+ K3 y" y% ^6 P- o
- byte[] byteContent = content.getBytes("utf-8"); ) _5 J, [0 w( C; I
- cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 . e) O. k" c# G" ]
- byte[] result = cipher.doFinal(byteContent); ) H- f- j6 o: X
- return result; // 加密 * a% m) ?. u7 d% L4 u
- } catch (NoSuchAlgorithmException e) { % A, h5 \% d! Q: L
- e.printStackTrace(); 6 w- Y; `/ k8 P; w0 M
- } catch (NoSuchPaddingException e) {
$ j/ Q! T7 W, b; L - e.printStackTrace(); * f# A# s# N6 L* z$ q8 q+ R* O
- } catch (InvalidKeyException e) {
|) E% F I5 X Q+ h, S - e.printStackTrace(); ) ~5 ]) M( L+ Y2 Z7 U
- } catch (UnsupportedEncodingException e) {
1 X; F/ b: E8 f! x; v* v - e.printStackTrace();
8 N h: I* k% b: p3 b - } catch (IllegalBlockSizeException e) {
B# s# _4 O! p- d- Q3 }5 }; H - e.printStackTrace(); , T9 Y6 e6 I) t/ g
- } catch (BadPaddingException e) { 7 A2 @: g! c/ N* O
- e.printStackTrace();
+ G; }" e$ P) d( t/ t - }
j5 j* }1 k' S7 s: j - return null;
$ A0 y$ w. K5 j/ k8 O. T, Q( k - }
# @% ?" v8 _ j
s1 y% ?8 n1 N9 e" n- m' y
0 ]/ S4 F! H2 u! n; T- /**解密 # f- N% @9 b# U: I- d8 M; u' ^
- * @param content 待解密内容
& e% ~* x0 R1 F# t3 X9 i0 R- e - * @param password 解密密钥
' _1 P" l$ t: E8 T7 u# K: v - * @return ! e9 s4 t" |( \
- */
( `2 @1 [7 w& c7 U" l6 N8 R - private static byte[] decrypt(byte[] content, String password) { : p& h5 U; `2 {& H4 L8 W
- try { ( _ t; X+ ]2 m9 \9 I
- KeyGenerator kgen = KeyGenerator.getInstance("AES"); + E1 z6 `. b9 F/ `' o0 x: P
- //防止linux下 随机生成key& l: K' f/ \- ~6 O
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" ); - @7 r, z7 n' R: Q1 F
- secureRandom.setSeed(password.getBytes());
8 H- H; H7 j, [! j/ ~ - kgen.init(128, secureRandom);
9 m& s: n) {( W0 w - //kgen.init(128, new SecureRandom(password.getBytes())); # m" U2 k6 U: H' M _
- SecretKey secretKey = kgen.generateKey(); 0 o8 M, |- c: ^2 h
- byte[] enCodeFormat = secretKey.getEncoded(); $ i5 E. l' L0 O: F3 b1 j0 J- ?
- SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
5 y* i3 V, ?# ]* {5 Y - Cipher cipher = Cipher.getInstance("AES");// 创建密码器 ! \% c9 a; v5 p p' d& G
- cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
) t7 `% Q- S: T X: q4 i# t5 Q' y6 y1 ` - byte[] result = cipher.doFinal(content);
( @( Y- e( l/ K - return result; // 加密
. B: i2 _( z1 M+ m* |4 T+ r - } catch (NoSuchAlgorithmException e) { * r' u; V1 f7 z7 W. u) t1 J
- e.printStackTrace();
8 N& P3 g2 h& X) r& u) ]3 P - } catch (NoSuchPaddingException e) { / ]4 O9 n% n0 k# p; G
- e.printStackTrace();
`4 r5 _6 T6 R - } catch (InvalidKeyException e) {
H# S3 [# Q) T t3 d' I0 G6 | - e.printStackTrace();
& y, s. _5 b/ S0 I - } catch (IllegalBlockSizeException e) {
& Z( d v0 s: w* B - e.printStackTrace(); * M% Y' E. ~7 o. R
- } catch (BadPaddingException e) {
: E$ S' ^( F9 r" c! l% s - e.printStackTrace();
9 @! W6 r* G' @' k9 m - } 4 S$ F5 n0 O% a& D8 H5 H" i3 b6 S
- return null; ' F# a( z/ @" Y6 P0 P& r
- } 5 V. U8 g% y) H/ Q" K
- ; y* q( i2 G2 U* c. Z3 O
- /**将二进制转换成16进制 5 P8 Y& ^& z" x% E
- * @param buf ) U* B8 N2 E/ |- Q4 ^
- * @return + ^2 m. _$ T+ X# B) |) L$ q
- */
8 W" n. f* v3 o9 @1 z" W - public static String parseByte2HexStr(byte buf[]) {
) c! @) Q2 ~+ M* X - StringBuffer sb = new StringBuffer(); ' j' n7 K& I) r! g, {
- for (int i = 0; i < buf.length; i++) { 2 F8 j. m& g2 f9 ?$ v
- String hex = Integer.toHexString(buf[i] & 0xFF);
# n+ ~& B% ~& ? O% ~) c3 P - if (hex.length() == 1) {
+ I/ T8 M) @( w1 f - hex = '0' + hex; , b4 l( N; f9 E0 V! `' H
- }
* B/ B, [. N* n5 r4 A* ] - sb.append(hex.toUpperCase());
9 m6 d) `0 B. _4 [1 Z - } + M, x. Y! `- i% N
- return sb.toString();
' E6 R% E; n3 V% L; C, j8 s - } 5 T4 R; ?7 A x9 W
4 m# P' D3 w3 {- + V$ K* }6 ?8 d l* \
- /**将16进制转换为二进制
2 f1 k0 Y2 h: h# j/ E. E - * @param hexStr * O6 [, Q2 ~0 O. h8 g
- * @return
" Q# H& J( k! Y: `2 z- G8 u - */
3 Y# D' p1 A1 @- M, c4 K7 A) m- q1 R - public static byte[] parseHexStr2Byte(String hexStr) { % S& B% I( z! T1 e( `
- if (hexStr.length() < 1) ) m- L/ g: _1 V( c: k+ D. |9 C8 D
- return null;
5 ^$ }, M7 {. ` h; ?$ Z6 q" l5 @9 M - byte[] result = new byte[hexStr.length()/2];
8 @2 t5 V8 ?$ @% ]( ? - for (int i = 0;i< hexStr.length()/2; i++) { Q2 j7 `" C* Q" l; U1 ~
- int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
8 D" O) ^- D% s0 k4 ?% w: a - int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
7 i5 }( ^3 d" F+ |- E# c - result[i] = (byte) (high * 16 + low); `, k$ [% B# _( U
- } 1 ~+ [1 O) P" `% F) v
- return result; 6 j( Q5 |; v$ D* W7 B+ z
- }
6 F T3 x0 c) }* c! J. M% k# [ - * \4 z, _& g; E
- + `: C. Q* x' e5 |% L; u- z
-
! ?7 M: E4 s( k1 p# T+ }4 t3 O - }( V, r! }+ N* l. O8 W' x: I0 }4 ?
复制代码 & @( y; T" W# ?1 Z8 _9 i4 F6 L
以上在linux测试ok 原文转自:http://konglx.iteye.com/blog/954085$ x( k3 I8 t" n j" E- Y8 B u
( q3 L7 {! q q2 ` M
/ v! O; w. L5 q1 L$ U/ O* J: W
) w$ R2 J. w6 l9 z8 \3 M& E1 g |