现象描述:
; I s# H" |* X+ G8 h) ]5 w6 v+ L( V8 G9 ]) B7 r, M: {
windows上加解密正常,linux上加密正常,解密时发生如下异常:
; v; W, t P7 N) C. E/ w4 U* O0 @5 ^7 l
javax.crypto.BadPaddingException: Given final block not properly padded$ @9 {9 B z$ @4 l% n, k- u
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)# g* ?2 x5 e' j1 C! r7 h
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
2 s. q8 W& C1 K at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
5 W( j6 f7 k' B0 S at javax.crypto.Cipher.doFinal(DashoA13*..)
, I8 J. R! _# a/ \: J* u- j6 d at chb.test.crypto.AESUtils.crypt(AESUtils.java:386)
( m; l' O) c4 ?. S& E+ S at chb.test.crypto.AESUtils.AesDecrypt(AESUtils.java:254)
% e, _. x: G5 |, G E# y at chb.test.crypto.AESUtils.main(AESUtils.java:40) 解决方法:
8 _0 `" o, d; b- }' M8 O0 Q! L0 W. F. f9 u5 W
经过检查之后,定位在生成KEY的方法上,如下:* X. \7 V' S8 l$ S9 ~
- public static SecretKey getKey (String strKey) {
/ v4 n$ [: G& A l Q - try {
2 E1 y8 ~/ N6 B2 T/ F9 W! y - KeyGenerator _generator = KeyGenerator.getInstance( "AES" ); ?5 t" A' s" _8 k/ q5 K& Z# v
- _generator.init(128, new SecureRandom(strKey.getBytes()));
4 v; l$ n6 {2 |/ _. @# s8 i - return _generator.generateKey();! q7 K% w! M- i, H; q6 J7 q
- } catch (Exception e) {
* `* g: Y8 E, { - throw new RuntimeException( " 初始化密钥出现异常 " );$ g( G" h. |' S" \* s/ v0 J. F- v
- }
& g7 ~3 f1 v0 C7 m7 ^; _ - }
复制代码 修改到如下方式,问题解决:8 t! l0 R+ o. g8 e% L
- public static SecretKey getKey(String strKey) {; x" s" U7 n& w( ?" j9 O1 k8 R
- try { 0 O2 l" ]7 y/ |* p# M! {5 ?
- KeyGenerator _generator = KeyGenerator.getInstance( "AES" );$ V% ^$ E" d, F3 G. [/ f. x* m0 r
- SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
) W) _. u( v. O/ ^. \# i. g: x' d - secureRandom.setSeed(strKey.getBytes());
7 P) H& }* ]% C" q. w - _generator.init(128,secureRandom);
* [/ |* |4 m2 ` - return _generator.generateKey();
5 |- A6 O9 x' x - } catch (Exception e) {
6 C2 P8 B' A/ K8 a ~; B - throw new RuntimeException( " 初始化密钥出现异常 " );3 f8 L4 |: _6 H- J* B E {" a
- }
5 K/ R# l6 h+ Z, X - }
复制代码 原因分析2 A' g4 N, G) c1 F
SecureRandom 实现完全隨操作系统本身的內部狀態,除非調用方在調用 getInstance 方法之後又調用了 setSeed 方法;该实现在 windows 上每次生成的 key 都相同,但是在 solaris 或部分 linux 系统上则不同。: Y: N5 e: f* b9 O" |4 Z: L
4 M; }5 o- W* a2 a1 i9 m! ^3 @3 q原因二: A# F/ U, G% H5 F1 P6 z$ A; w
. x" _) G3 ~0 }# v1、加密完byte[] 后,需要将加密了的byte[] 转换成base64保存,如: & z$ ?7 ]3 G/ ~; G
BASE64Encoder base64encoder = new BASE64Encoder();
1 D% A1 P6 b7 |" t2 s) X8 aString encode=base64encoder.encode(bytes); ! P5 ]* a# [% K& F8 `! A( i% H
: ?- P, J L2 O2 S* o
2、解密前,需要将加密后的字符串从base64转回来再解密,如:
) |! Y8 R! M1 cBASE64Decoder base64decoder = new BASE64Decoder(); , }! m2 C1 K9 V" l2 P& l! ?% ?
byte[] encodeByte = base64decoder.decodeBuffer(str);
& I3 K0 g- G- ~8 ?* V `; o1 {9 ]4 u2 ~/ ]# L5 D& Q' G2 v
4 x6 z% T4 r& l- I/ D
完整例子:
: q! Y% G% J! }+ a% M, a- package com.travelsky.tdp.pkgStock.util;8 J6 O$ {; ^6 X- n
1 W& z/ K! B9 k. o- import java.io.IOException;5 \4 ~" `+ f( p8 D+ x4 h# {
- import java.io.UnsupportedEncodingException;9 W( [# Q* N, i/ q
- import java.security.InvalidKeyException;9 ~- j/ o' t! L: ^2 L; P
- import java.security.NoSuchAlgorithmException;. B: g4 W+ v: q
- import java.security.SecureRandom;9 Q5 P4 y9 s; [% v( X9 u& Y
- 3 n! s5 X* S# V8 n& k- j
- import javax.crypto.BadPaddingException;9 q" {" g# H' V# n) C* F* C
- import javax.crypto.Cipher;
' `8 A+ n2 x8 X4 J" V; T - import javax.crypto.IllegalBlockSizeException;6 M' C3 `# Z3 b# r- o
- import javax.crypto.KeyGenerator;2 s/ {# O! w5 F
- import javax.crypto.NoSuchPaddingException;
8 x' P1 ?2 o) z. j5 V' F" u" { - import javax.crypto.SecretKey;
1 o8 s0 A+ E8 ?2 j, I8 Y- s9 Z ^ - import javax.crypto.spec.SecretKeySpec;
2 ?, h$ D+ N) C8 X" ~/ E5 i
1 o2 r* r% g2 a) Y- D- import sun.misc.BASE64Decoder;8 M5 Y! |( {/ L! B( a6 @! v9 q4 a. x
- import sun.misc.BASE64Encoder;
: X* C6 \6 U5 t8 q) ?. e$ e) Q
0 ~. h3 A& W# C* ~! C/ N% X$ U- public class SecurityAES {3 \+ g$ e5 m) m& O" f3 n2 ^+ U
- private final static String encoding = "UTF-8";
. S% M' ~' @9 B( h) |! Y - /**
7 }1 D' c/ w; ~8 Y+ l- S - * AES加密
8 [- [( L/ e9 }/ r# H8 c2 [ - *
/ N- ?# w; N p: b - * @param content
: y V0 U0 e9 ~6 a# h7 x - * @param password# E5 S; ]2 u- _# l2 f! A* P+ K
- * @return6 H+ A" N u) j/ B6 }
- */5 Z; l9 m1 B1 q5 m# ~5 y5 r8 o# R$ D
- public static String encryptAES(String content, String password) {" T# u8 K( s- x/ Y; [
- byte[] encryptResult = encrypt(content, password);" J$ y0 }1 j, L% n
- String encryptResultStr = parseByte2HexStr(encryptResult);
3 ^ q" c% [3 G. }, u. R - // BASE64位加密' F# X' X$ k/ \4 T+ k8 f/ c
- encryptResultStr = ebotongEncrypto(encryptResultStr);/ U: M L6 o) n' r5 `! L
- return encryptResultStr;
4 P1 h8 l% k& l, J. F+ V - }$ N4 ~) i6 a: R2 E
, |3 ^3 P' r, u1 f4 {9 ]/ j0 F. w- /**
3 M) Z4 k( Z4 \, W/ q! r" D - * AES解密
0 z7 {. {" V# D' A9 N0 g" ~, Z, v - * ( G+ T( [, I3 u) B2 Y
- * @param encryptResultStr3 X8 E/ E9 K/ o
- * @param password
; a4 S! W6 S& B! f& L& D8 v; V - * @return
2 S" h3 ?; R( o - */
3 i! o# X. G8 S( a' B - public static String decrypt(String encryptResultStr, String password) {* N# L/ A: D4 c$ A
- // BASE64位解密% F- L% S/ Q( s' ^# E+ T4 |
- String decrpt = ebotongDecrypto(encryptResultStr);
( y; O) F2 V7 L- L2 L+ [ - byte[] decryptFrom = parseHexStr2Byte(decrpt);
, Z& o$ u1 N8 P+ S - byte[] decryptResult = decrypt(decryptFrom, password);) k, s* W. W. x" i
- return new String(decryptResult);6 k Z3 E6 e. p' N8 r+ o1 E
- }. _# Y' `& [/ U$ t: Y: @" e
- 8 s* p9 p; A. D" w7 w
- /*** {: b+ h' K6 ^. a) O/ f- \
- * 加密字符串
) |* |) e7 _, P4 f; O - */
% O1 Z( k" d& _- p$ ^ - public static String ebotongEncrypto(String str) {
+ k. m; u4 {3 ~% d @) T - BASE64Encoder base64encoder = new BASE64Encoder();
/ F# @* m, [7 H4 O! d - String result = str;! ~' A5 K/ W6 K8 _4 G4 p I
- if (str != null && str.length() > 0) {
9 N0 {% X0 t9 g4 C! j% y, |- ~ - try {
/ G7 T9 @: e+ g/ t$ g - byte[] encodeByte = str.getBytes(encoding);, w/ p% G( j( _7 q# i6 q
- result = base64encoder.encode(encodeByte);& G" Z6 \: s5 g
- } catch (Exception e) {8 Q6 D$ b4 h1 g0 B% h7 y, k
- e.printStackTrace();! ]% `2 k" k' ^4 j' P6 M
- }
0 h9 y' Y& S! a - }3 w/ K6 d( B) W6 w
- //base64加密超过一定长度会自动换行 需要去除换行符
) G- S/ T9 p: S9 \4 O- n m4 E$ g - return result.replaceAll("\r\n", "").replaceAll("\r", "").replaceAll("\n", "");
9 r* |( E3 h% `' q - }
2 T: o5 [2 e: u& j! f) \; C - / n0 q; v5 M$ m4 ?" g8 W) Y8 K
- /**
# ~' d U, h3 z - * 解密字符串+ n [: k) v# A: G3 N: \
- */
$ `# C" H ?) W. S. U - public static String ebotongDecrypto(String str) {, K3 a7 j. w* M2 D+ \! i
- BASE64Decoder base64decoder = new BASE64Decoder();
3 J6 s) d9 b* O1 G9 A# V8 j - try {
g( D1 ~8 j: G7 i - byte[] encodeByte = base64decoder.decodeBuffer(str);
# _$ [! j) [% N$ C0 r K) b - return new String(encodeByte);( D, \2 o$ @9 |/ X& `& t( B7 c
- } catch (IOException e) {
2 n$ D! L/ O7 e' o, E3 I - e.printStackTrace();
/ k7 D `7 z7 j1 p- { - return str;6 r) n$ ~9 C+ k- N9 ~6 k% L, c
- }
2 M8 j1 n# W1 p H* p2 x" I8 e - }
1 `) w* l* H5 K, w: W% G7 L' Q- D - /**
! C6 {& S' G; U- s4 j8 N) q5 d: X - * 加密 ) x" B& o$ c7 y1 v1 E6 T
- *
' F* H# N2 z z* Q6 a" O# y9 B - * @param content 需要加密的内容 ' ]$ X* H; {8 ~& J4 W& z& ~% K
- * @param password 加密密码
. a. y0 D# B' ~8 ?" Y% g& q - * @return
6 H4 s$ W) L" D - */
: M/ ~7 l. Q. A8 O7 f7 j3 y - private static byte[] encrypt(String content, String password) { 5 N a) p) k( }3 \4 d- y0 J
- try { % a# s, o/ n z/ j0 r. y
- KeyGenerator kgen = KeyGenerator.getInstance("AES");
# F( n @7 K( W# [+ Z6 o0 {. j# w - //防止linux下 随机生成key
& O" f& i* P0 _. S2 l: k - SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" ); * [1 d3 { u) @0 ]0 j
- secureRandom.setSeed(password.getBytes()); 7 a. x( Q6 i }2 Z0 m A3 A$ W- G
- kgen.init(128, secureRandom);' _/ c. |4 X. X t2 W& I X, f$ b
- //kgen.init(128, new SecureRandom(password.getBytes()));
?# Z: p1 F7 \, ?+ J5 x% F - SecretKey secretKey = kgen.generateKey();
1 t: r' j( X) x, | - byte[] enCodeFormat = secretKey.getEncoded();
8 ]. c6 C# z" M - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES"); % A# y% s- E/ |& g/ u
- Cipher cipher = Cipher.getInstance("AES");// 创建密码器 2 F2 g2 G$ r" v4 J) Y4 A
- byte[] byteContent = content.getBytes("utf-8");
, B# d* L$ G7 {) q% @ - cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化 & f; d+ T* p4 p+ ?6 P4 H+ A/ b
- byte[] result = cipher.doFinal(byteContent);
4 W6 L* [$ n9 h: O - return result; // 加密 ( j( U. B' ?# _* P/ @
- } catch (NoSuchAlgorithmException e) {
- S5 l; G1 ?4 u4 u" s - e.printStackTrace(); 1 a* g7 m. w$ \, h2 n6 s8 K
- } catch (NoSuchPaddingException e) { 1 }; h2 a! g0 f( [
- e.printStackTrace();
: p2 z& u! Y T3 E; [9 U1 ^ - } catch (InvalidKeyException e) { U! g& E% `7 B" E4 U/ [* g% v
- e.printStackTrace(); : F* q b. t4 J# y6 x; v
- } catch (UnsupportedEncodingException e) { 5 k' l4 A' E8 h% _# B B; ~# A4 J
- e.printStackTrace(); & ]9 ~+ h! s& H: N' b S7 |1 A
- } catch (IllegalBlockSizeException e) { + [, x- u) V* L9 f2 k
- e.printStackTrace(); 8 m# j& a! K# w' q' k" e
- } catch (BadPaddingException e) { 9 I" H; s1 w: J* j
- e.printStackTrace();
- B3 N$ o9 X. f! f W/ D - }
6 r, Q- |8 H L4 j( ^) l - return null;
6 `2 s3 \* e: Z; g- F - }
/ p4 H/ Z4 L+ F5 q) m; i
0 C: l- e9 `6 @6 G" K) \
! U/ P# C J9 B/ u- /**解密
1 k) n6 D2 d0 G, }6 f4 W - * @param content 待解密内容 ]: d* i3 f1 y- h2 b7 B N
- * @param password 解密密钥 1 G4 K3 m4 H( P ^) w- {- B
- * @return
9 b0 C9 A) ~+ j3 W: x - */
8 a6 g5 ?' W6 h( I3 w8 w; }, q. W - private static byte[] decrypt(byte[] content, String password) {
0 [8 S$ F) B; H' E; H' a' F - try {
+ b" N" Q) Y5 P- y& I: e - KeyGenerator kgen = KeyGenerator.getInstance("AES");
+ S% x I: I0 P1 b) F8 K% U% |. c8 j( y7 n - //防止linux下 随机生成key
7 k6 L, ] f* j4 t' T1 d: ^$ U - SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG" );
0 b* _) p$ z% v" n5 Q. w# s - secureRandom.setSeed(password.getBytes()); ; H: J, S2 S) ]- [0 R
- kgen.init(128, secureRandom);3 r. s% e. q5 N+ f: F
- //kgen.init(128, new SecureRandom(password.getBytes())); ( k) P+ p$ x/ @# e! a0 b ^
- SecretKey secretKey = kgen.generateKey(); ( ]5 S; v* u8 c! S8 G
- byte[] enCodeFormat = secretKey.getEncoded();
2 c" d9 \# [- Q/ L4 v, n' D& p. l - SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
9 M# H+ z! r' F! N$ Y - Cipher cipher = Cipher.getInstance("AES");// 创建密码器 E! R8 {( x, n1 I5 O/ f2 h
- cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
' ^* Q- N6 J8 O" p0 L) N ]# p6 _ - byte[] result = cipher.doFinal(content); 9 k# g) _: v/ e" r, o4 _1 p5 N
- return result; // 加密 . J6 ]' e% r- b8 Z
- } catch (NoSuchAlgorithmException e) { , k0 Q( q; r) U
- e.printStackTrace(); # v/ o) T+ Y9 |+ q0 k% L
- } catch (NoSuchPaddingException e) { ) H. I- e% I7 [, w) T) i8 C5 b
- e.printStackTrace(); # L$ K4 G/ L- a5 [1 k% \# q/ P# p& b
- } catch (InvalidKeyException e) {
; u' O; m* t0 r0 D - e.printStackTrace(); ) O( B3 C8 X0 V7 X. d1 T. x
- } catch (IllegalBlockSizeException e) {
& ]" `0 g8 V% H" ]) u& Y9 D: }0 z - e.printStackTrace(); - H* J- M" q7 Q4 ]9 a0 L/ C
- } catch (BadPaddingException e) { ! {3 z$ g( ]9 ^$ }
- e.printStackTrace();
6 u Z9 b1 i" s9 D/ L - } ) V% m1 f' q5 y
- return null;
' F1 \# Q2 ]( y& O# L4 ? - }
) ^. p% |2 P/ [# E
2 g- Z8 f6 }7 V0 h2 Z. Z- /**将二进制转换成16进制 9 O6 A! H; l4 |/ e+ s* X
- * @param buf / K, r3 U6 ^% ^2 F- c
- * @return 8 J: h: y, o) w I+ N' {3 t/ C
- */
' E. M ]8 i( j) V% M, C) U - public static String parseByte2HexStr(byte buf[]) {
) d# O, x6 B4 ~+ C# `7 v6 s - StringBuffer sb = new StringBuffer(); 9 H! Y2 U6 Z" X( ^
- for (int i = 0; i < buf.length; i++) {
1 g# S* Y6 D; _& T- U' b: g- T - String hex = Integer.toHexString(buf[i] & 0xFF); O. f- S6 }: I, n' J
- if (hex.length() == 1) {
5 n7 n: _. N9 ~1 T: w; o" d( n - hex = '0' + hex;
! G( @% M+ p" t' e. c$ T" \ - } 3 ^4 h$ ^8 l0 C5 S' e1 h9 S
- sb.append(hex.toUpperCase());
& G1 x! K9 ~1 J5 F - }
5 M: L6 a5 N" z8 P' Q+ t( }7 |) Y; \ - return sb.toString(); 6 M1 ]$ ~: F9 U3 g# @
- } 9 E6 t7 i6 ?% i5 L* q* M* H1 d; W- O
0 F' Y' z; s- G$ |3 T b6 C$ v- - d1 k) S5 m! n4 n( n+ k
- /**将16进制转换为二进制
6 {$ ?7 ^: x! {: y. ~ - * @param hexStr " J! ~4 @0 n* S3 s
- * @return
8 |7 ]$ O5 Z: ]2 R( V% G - */ 0 T3 J! G# H: W/ N, [
- public static byte[] parseHexStr2Byte(String hexStr) {
1 i- j. s& i5 X: w/ q4 V4 Q - if (hexStr.length() < 1)
3 k; j3 a+ T* ? a' ? - return null;
; W- [3 k1 p; ]) T! B; } - byte[] result = new byte[hexStr.length()/2];
3 z0 s) M+ ~4 q5 s0 m7 U5 n - for (int i = 0;i< hexStr.length()/2; i++) { - e9 ?% q. s& z% E# y5 g# A
- int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16); % z" g0 l3 r+ e9 p! F' W% K. l& f
- int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16); 9 D7 Z7 Y7 h: R: R
- result[i] = (byte) (high * 16 + low);
! P5 A0 n" {, b: ~# K9 ~- y9 ^, b - } 1 e5 F- Y2 [4 [+ ~) j
- return result;
$ j0 f+ `+ ~& {* U& T# I - }
& r6 z5 r- |% ? - 0 c, |! p' B8 Q6 d: A( c
- , q* m1 [1 M+ H# f" b6 W
- ! \& U& K3 c N- G" C
- }, ~4 [* i# ^1 f& z. Q
复制代码
; T7 q2 K) n" A: n7 j" c以上在linux测试ok 原文转自:http://konglx.iteye.com/blog/954085) v& z4 Z" E; A4 @; s1 P
9 x: d! v: |/ x6 H- m& m
( |7 q: Z% a7 A* W4 E# x2 a% b1 ^
$ @& U3 o v1 v3 H; N |