代碼裏用Blowfish算法加解密,結果jdk升到1.7後算法初始化失敗 javascript
java.lang.RuntimeException: java.lang.RuntimeException: PANIC: Unreachable code reached.html
JCE不熟悉,更新到原來jdk6的security後報java
java.lang.SecurityException: The jurisdiction policy files are not signed by a trusted signernode
解決方法:git
單獨下載JCE 擴展:需單獨從Oracle官網下載對應JDK版本的Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 算法
JDK7對應的jurisdiction policy files:swift
http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
數組
JDK8安全
http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html服務器
jdk6
http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
JCE,Java Cryptography Extension,在早期JDK版本中,因爲受美國的密碼出口條例約束,Java中涉及加解密功能的API被限制出口,因此Java中安全組件被分紅了兩部分: 不含加密功能的JCA(Java Cryptography Architecture )和含加密功能的JCE(Java Cryptography Extension)。在JDK1.1-1.3版本期間,JCE屬於擴展包,僅供美國和加拿大的用戶下載,JDK1.4+版本後,隨JDK核心包一塊兒分發;
JCA和JCE的API體系架構
JCE的API都在javax.crypto包下,核心功能包括:加解密、密鑰生成(對稱)、MAC生成、密鑰協商,下面咱們就詳細介紹下這些功能。
一. 加解密
加解密功能由Cipher組件提供,其也是JCE中最核心的組件。
1. Cipher的幾個知識點:
——————————————————————————————–
a. Cipher在使用時需以參數方式指定transformation
b. transformation的格式爲algorithm/mode/padding,其中algorithm爲必輸項,如: DES/CBC/PKCS5Padding
c. 缺省的mode爲ECB,缺省的padding爲PKCS5Padding
d. 在block算法與流加密模式組合時, 需在mode後面指定每次處理的bit數, 如DES/CFB8/NoPadding, 如未指定則使用缺省值, SunJCE缺省值爲64bits
e. Cipher有4種操做模式: ENCRYPT_MODE(加密), DECRYPT_MODE(解密), WRAP_MODE(導出Key), UNWRAP_MODE(導入Key),初始化時需指定某種操做模式
2. 對稱加密的算法與密鑰長度選擇
算法名稱 | 密鑰長 | 塊長 | 速度 | 說明 |
---|---|---|---|---|
DES | 56 | 64 | 慢 | 不安全, 不要使用 |
3DES | 112/168 | 64 | 很慢 | 中等安全, 適合加密較小的數據 |
AES | 128, 192, 256 | 128 | 快 | 安全 |
Blowfish | (4至56)*8 | 64 | 快 | 應該安全, 在安全界還沒有被充分分析、論證 |
RC4 | 40-1024 | 64 | 很快 | 安全性不明確 |
通常狀況下,不要選擇DES算法,推薦使用AES算法。通常認爲128bits的密鑰已足夠安全,若是能夠請選擇256bits的密鑰。注意:
——————————————————————————————–
a. 密鑰長度是在生成密鑰時指定的,如:
KeyGenerator generator = KeyGenerator.getInstance("AES/CBC/PKCS5PADDING"); generator.init(256); SecretKey key = generator.generateKey();
b. 生成長度超128bits的密鑰,需單獨從Oracle官網下載對應JDK版本的Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files文件,例如JDK7對應的jurisdiction policy files
3. 加密示例代碼
/** * 根據密鑰{@link #getKey()}對指定的明文plainText進行加密. * * @param plainText 明文 * @return 加密後的密文. */ public static final String encrypt(String plainText) { Key secretKey = getKey(); try { Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] p = plainText.getBytes("UTF-8"); byte[] result = cipher.doFinal(p); BASE64Encoder encoder = new BASE64Encoder(); String encoded = encoder.encode(result); return encoded; } catch (Exception e) { throw new RuntimeException(e); } }
4. 解密示例代碼
/** * 根據密鑰{@link #getKey()}對指定的密文cipherText進行解密. * * @param cipherText 密文 * @return 解密後的明文. */ public static final String decrypt(String cipherText) { Key secretKey = getKey(); try { Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKey); BASE64Decoder decoder = new BASE64Decoder(); byte[] c = decoder.decodeBuffer(cipherText); byte[] result = cipher.doFinal(c); String plainText = new String(result, "UTF-8"); return plainText; } catch (Exception e) { throw new RuntimeException(e); } }
5. 帶算法參數的加解密
Cipher可能用到算法參數(AlgorithmParameterSpec或AlgorithmParameters)的情形:
——————————————————————————————–
a. DES, DES-EDE, and Blowfish使用feedback模式時(如CBC, CFB, OFB或PCBC), 將用到IV
b. PBEWithMD5AndDES將用到salt和iteration count
下面是採用PBE算法進行加解密的示例:
/** * 提供基於口令的加密功能. * * @param plainText 明文 * @return 加密後的密文. */ public static final String pbeEncrypt(String plainText) { Key pbeSecretKey = getPBEKey(); PBEParameterSpec pbeParamSpec = getParamSpec(); try { Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES"); cipher.init(Cipher.ENCRYPT_MODE, pbeSecretKey, pbeParamSpec); byte[] p = plainText.getBytes("UTF-8"); byte[] result = cipher.doFinal(p); BASE64Encoder encoder = new BASE64Encoder(); String encoded = encoder.encode(result); return encoded; } catch (Exception e) { throw new RuntimeException(e); } } /** * 提供基於口令的解密功能. * * @param cipherText 密文 * @return 解密後的明文. */ public static final String pbeDecrypt(String cipherText) { Key pbeSecretKey = getPBEKey(); PBEParameterSpec pbeParamSpec = getParamSpec(); try { Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES"); cipher.init(Cipher.DECRYPT_MODE, pbeSecretKey, pbeParamSpec); BASE64Decoder decoder = new BASE64Decoder(); byte[] c = decoder.decodeBuffer(cipherText); byte[] result = cipher.doFinal(c); String plainText = new String(result, "UTF-8"); return plainText; } catch (Exception e) { throw new RuntimeException(e); } } /** * 獲取PBE算法的密鑰. 注意PBE密鑰由用戶提供的口令構造出來的, * 用戶提供的口令務必使用char數組, 而不能使用字符串, 字符數 * 組用完即清空. * * @return PBE算法的密鑰. */ private static final Key getPBEKey() { // TODO come from db or System.in, NOTE: MUST be char array, not java.lang.String char[] pwd = {'%', '_', 'A', 's', '9', 'K'}; SecretKey pbeKey = null; PBEKeySpec pbeKeySpec = new PBEKeySpec(pwd); try { SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES"); pbeKey = keyFac.generateSecret(pbeKeySpec); return pbeKey; } catch (Exception e) { throw new RuntimeException(e); } finally { Arrays.fill(pwd, ' '); } } /** * 獲取PBE的算法參數, 涉及salt和iterate count兩個參數. * * @return PBE的算法參數. */ private static final PBEParameterSpec getParamSpec() { byte[] salt = { (byte) 0xab, (byte) 0x58, (byte) 0xa1, (byte) 0x8c, (byte) 0x3e, (byte) 0xc8, (byte) 0x9d, (byte) 0x7a }; int count = 20; PBEParameterSpec paramSpec = new PBEParameterSpec(salt, count); return paramSpec; }
測試代碼:
String pbePlainText = "127Kjk$%2^"; String pbeCipherText = pbeEncrypt(pbePlainText); String pbePlainText2 = pbeDecrypt(pbeCipherText); if (!pbePlainText.equals(pbePlainText2)) { System.out.println("PBE Something wrong"); }
二. 密鑰生成
非對稱密鑰的生成請參考java.security.KeyPairGenerator,樣例代碼請參考JCA中的示例,對稱密鑰生成的示例代碼以下:
KeyGenerator gen = KeyGenerator.getInstance("DES"); gen.init(56, new SecureRandom()); Key key= gen.generateKey();
三. MAC生成
MAC技術用於確認數據的完整性,Mac要求通信雙方共享一個secret key,示例代碼以下:
Key key = KeyGeneratorDemo.generateMac();
Mac mac = Mac.getInstance("HmacSHA256"); mac.init(key); String msg = "新莊楊渡10#"; byte[] result = mac.doFinal(msg.getBytes("UTF-8")); BASE64Encoder encoder = new BASE64Encoder(); System.out.println(encoder.encode(result));
MAC優於數據摘要的地方在於:MAC雙方要共享一個密鑰,因此其也有互相認證的功能,可有效防止數據摘要中明文和數據摘要被同時替換而沒法發現的問題。
四. 密鑰協商
密鑰協商就是在通信多方間不直接交換通信密鑰的狀況下而選擇一個你們達成一致的密鑰(session key),這個session key是對稱密鑰。
1. 密鑰協商能夠經過2種途徑實現:
——————————————————————————————–
a. 經過KeyAgreement組件完成,經常使用算法包括DH(Diffie-Hellman),ECDH(Elliptic Curve Diffie-Hellman),ECMQV(Elliptic Curve Menezes-Qu-Vanstone)等。
b. 經過數字信封完成,經常使用算法包括RSA等。
2. 經過KeyAgreement使用DH算法協商密鑰
a. DH算法由PKCS#3定義,DH算法需在多方間交換公鑰,大素數p,私鑰的基數g,和私鑰的長度l。設協商密鑰的雙方爲Alice和Bob,則協商共涉及5個階段:
——————————————————————————————–
i. Alice生成DH公私密鑰對
ii. Alice將公鑰和算法參數p,g和l發送給Bob
iii. Bob根據算法參數生成本身的公私密鑰對,並將公鑰發送給Alice
iv. Alice使用本身的私鑰和Bob的公鑰經過KeyAgreement獲得通信密鑰
v. Bob使用本身的私鑰和Alice的公鑰經過KeyAgreement獲得與Alice相同的通信密鑰
b. 下面的代碼演示了使用DH算法實現密鑰協商,設通信一方爲Alice,另外一方爲Bob,分別由兩個線程模擬,實際數據交換由共享內存模擬:
/* * @(#)KeyAgreementDemo.java 1.0 2012-4-24 * * Copyright 2010 Richard Chen(utopia_rabbi@sse.buaa.edu.cn) All Rights Reserved. * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package charpter.security.keyagreement; import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PublicKey; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; import javax.crypto.KeyAgreement; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.DHParameterSpec; /** * 演示Deffie-Hellman密鑰交換組件的用法. * * 密鑰交換就是在通信多方間不直接交換通信密鑰的狀況下而選擇一個你們達成一致的密鑰. * * @author Rich, 2012-4-24. * @version 1.0 * @since 1.0 */ public class KeyAgreementDemo implements Runnable { /** bob和alice的公鑰內容. */ byte bob[], alice[]; /** alice側是交換的發起方, 是否已啓動. */ boolean doneAlice = false; /** 使用計算出來的交換密鑰加密過的報文. */ byte[] ciphertext; /** DH算法涉及的算法參數, alice側的大素數p, alice側的私鑰基數g. */ BigInteger aliceP, aliceG; /** DH算法涉及的算法參數, alice的私鑰長度l. */ int aliceL; public synchronized void run() { if (!doneAlice) { doAlice(); doneAlice = true; } else doBob(); } public synchronized void doAlice() { try { // Step 1: Alice generates a key pair KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH"); kpg.initialize(1024); KeyPair kp = kpg.generateKeyPair(); // Step 2: Alice sends the public key and the // Diffie-Hellman key parameters to Bob Class dhClass = Class.forName("javax.crypto.spec.DHParameterSpec"); DHParameterSpec dhSpec = ((DHPublicKey) kp.getPublic()).getParams(); aliceG = dhSpec.getG(); aliceP = dhSpec.getP(); aliceL = dhSpec.getL(); alice = kp.getPublic().getEncoded(); notify(); // Step 4 part 1: Alice performs the first phase of the // protocol with her private key KeyAgreement ka = KeyAgreement.getInstance("DH"); ka.init(kp.getPrivate()); // Step 4 part 2: Alice performs the second phase of the // protocol with Bob's public key while (bob == null) { wait(); } KeyFactory kf = KeyFactory.getInstance("DH"); X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(bob); PublicKey pk = kf.generatePublic(x509Spec); ka.doPhase(pk, true); // Step 4 part 3: Alice can generate the secret key byte secret[] = ka.generateSecret(); // Step 6: Alice converts a secret key SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); DESKeySpec desSpec = new DESKeySpec(secret); SecretKey key = skf.generateSecret(desSpec); // Step 7: Alice encrypts data with the key and sends // the encrypted data to Bob Cipher c = Cipher.getInstance("DES/ECB/PKCS5Padding"); c.init(Cipher.ENCRYPT_MODE, key); ciphertext = c.doFinal("Stand and unfold yourself".getBytes()); notify(); } catch (Exception e) { e.printStackTrace(); } } public synchronized void doBob() { try { // Step 3: Bob uses the parameters supplied by Alice // to generate a key pair and sends the public key while (alice == null) { wait(); } KeyPairGenerator kpg = KeyPairGenerator.getInstance("DH"); DHParameterSpec dhSpec = new DHParameterSpec(aliceP, aliceG, aliceL); kpg.initialize(dhSpec); KeyPair kp = kpg.generateKeyPair(); bob = kp.getPublic().getEncoded(); notify(); // Step 5 part 1: Bob uses his private key to perform the // first phase of the protocol KeyAgreement ka = KeyAgreement.getInstance("DH"); ka.init(kp.getPrivate()); // Step 5 part 2: Bob uses Alice's public key to perform // the second phase of the protocol. KeyFactory kf = KeyFactory.getInstance("DH"); X509EncodedKeySpec x509Spec = new X509EncodedKeySpec(alice); PublicKey pk = kf.generatePublic(x509Spec); ka.doPhase(pk, true); // Step 5 part 3: Bob generates the secret key byte secret[] = ka.generateSecret(); // Step 6: Bob generates a DES key SecretKeyFactory skf = SecretKeyFactory.getInstance("DES"); DESKeySpec desSpec = new DESKeySpec(secret); SecretKey key = skf.generateSecret(desSpec); // Step 8: Bob receives the encrypted text and decrypts it Cipher c = Cipher.getInstance("DES/ECB/PKCS5Padding"); c.init(Cipher.DECRYPT_MODE, key); while (ciphertext == null) { wait(); } byte plaintext[] = c.doFinal(ciphertext); System.out.println("Bob got the string " + new String(plaintext)); } catch (Exception e) { e.printStackTrace(); } } public static void main(String args[]) { KeyAgreementDemo test = new KeyAgreementDemo(); new Thread(test).start(); // Starts Alice new Thread(test).start(); // Starts Bob } }
以上代碼參考了Java Cryptography Extension (JCE) Reference Guide(JDK5.0)中Appendix F的例子。
3. 經過數字信封使用RSA算法協商密鑰
數字信封的原理就是利用通信對方的公鑰加密目標密鑰(session key,對稱密鑰),使用目標密鑰對報文進行加密,而後將密鑰密文與報文密文一塊兒發送給接收方。接收方首先使用本身的私鑰對密鑰報文進行解密,這樣就獲得了協商後的密鑰,再使用解密後的密鑰解密報文,這樣就獲得了業務數據。過程圖示以下:
代碼示例以下,密鑰協商雙方由兩個線程模擬:
/* * @(#)DigitalEnvelopeDemo.java 1.0 2012-6-14 * * Copyright 2010 Richard Chen(utopia_rabbi@sse.buaa.edu.cn) All Rights Reserved. * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package charpter.security.keyagreement; import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.HashMap; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import charpter.security.key.KeyGeneratorDemo; /** * 演示使用Digital envelope技術進行密鑰交換. *
* 服務器端與客戶端分別由兩個獨立的線程模擬, 雙方指間的通信由共享內存實現. * * @author Rich, 2012-6-14. * @version 1.0 * @since 1.0 */ public final class DigitalEnvelopeDemo { /** * @param args */ public static void main(String[] args) { KeyPair pair = generatorKeyPair(); Thread client = new Client(pair); Thread server = new Server(pair.getPublic()); server.start(); client.start(); } /** * 生成RSA算法的公私密鑰對. * * @return 生成RSA算法的公私密鑰對. */ public static final KeyPair generatorKeyPair() { KeyPairGenerator keyGen = null; try { keyGen = KeyPairGenerator.getInstance("RSA"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } SecureRandom random = null; try { random = SecureRandom.getInstance("SHA1PRNG"); } catch (Exception e) { throw new RuntimeException(e); } random.setSeed(53); keyGen.initialize(1024, random); KeyPair pair = keyGen.generateKeyPair(); return pair; } /** * 模擬密鑰交換的服務器端, 服務器端與客戶端經過共享內存來交換Digital Envelope. * * @author Rich, 2012-6-14. * @version 1.0 * @since 1.0 */ static class Server extends Thread { /** * 實際中有多是客戶端在請求服務器端時上送了本身的公鑰, 也有多是在註冊 * 時就在服務器端登記了公鑰. * * @param clientPublicKey 客戶端的公鑰. */ public Server(PublicKey clientPublicKey) { this.clientPublicKey = clientPublicKey; } /* (non-Javadoc) * @see java.lang.Thread#run() */
KeyAgreement的DH與數字信封的RSA比較:
——————————————————————————————–
a. DH僅限於交換共享密鑰,而沒法對交換雙方的身份進行認證,易受中間人攻擊
b. RSA能夠用於交換共享密鑰也能夠用於身份認證
c. 建議:在雙方都有數字證書時,使用RSA,一方或兩方都沒有數字證書則使用Diffie-Hellman,SSL3.0就是採用的此策略
五. 總結
JCE中最經常使用和最核心的功能就是加解密,此功能由Cipher組件提供,在使用Cipher前需對加密算法及參數先作出選擇:
1. 算法選擇
對稱算法通常速度較快,非對稱算法速度較慢;對稱算法的密鑰管理比較困難,非對稱算法密鑰管理簡單;非對稱算法通常用於認證和加密會話密鑰,通信雙方大部分也就是在開啓會話時使用一次,對稱算法通常用來加密雙方之間的報文/交換的數據,使用頻度較高。
2. 塊/流模式選擇
塊(Block)模式加密以塊爲基本單位,適用於明文長度已知的情形;流(Stream)模式以bit或byte爲加解密單位, 適用於明文長度未知、內容較大的情形,如加密一個套接字管道或文件讀寫流等,通常僅適用於硬件實現。塊模式下不一樣算法的塊大小可能不同,通常都是2的次方數,大部分長度爲64bits,整個明文長度不是塊長度整倍數時,需在最後一個Block進行補長(Padding)
3. 反饋模式選擇
使用塊算法加密,若是明文有大量重複的內容,則對塊加密後獲得的密文也會存在大量的重複,這對密文分析、破解提供了極大的便利,爲消除這方面的威脅,有個思路就是對不一樣塊密文再進行運算,這樣就極大去除了塊密文與塊明文幾間的特徵關聯,這種作法稱爲塊反饋模式。常見的反饋模式有:ECB、CBC、CFB、OFB等。對於第1個block,因沒有其它塊密文可供運算,有的模式引入了初始矢量(Initialization Vector,IV,由用戶指定)做爲第1個block內容,這樣就進一步解決了第1個block密文的脆弱性。注意:儘可能不要使用ECB模式。
4. 補長方案選擇
下面是一些常見的補長(Padding)方案,以DES算法加密明文for爲例,因for不足8bytes,因此需補長5bytes(for ?? ?? ?? ?? ??),則這5bytes可能選擇(16進制):
——————————————————————————————–
i. 全部Padding以長度爲值:66 6F 72 05 05 05 05 05
ii. Padding以0×80開始後面所有爲0×00:66 6F 72 80 00 00 00 00
iii. 最後一個字節爲Padding長度, 其它爲0×00:66 6f 72 00 00 00 00 05
iv. 所有Padding爲0×00:66 6f 72 00 00 00 00 00
v. 所有Padding爲0×20(空格):66 6f 72 20 20 20 20 20
JCE中支持的補長方案包括:NoPadding、PKCS5Padding、ISO10126Padding、OAEPWithAndPadding和SSL3Padding,NoPadding即不補長,其中最經常使用的就是PKCS5Padding和ISO10126Padding。
PKCS5Padding,具體規範請參考RSA實驗室的文檔:PKCS #5 Password-Based Encryption Standard,Version 1.5。簡單的說PKCS5Padding就2個規則:
——————————————————————————————–
i. 補長的內容爲待補長字節數
ii. 補長的字節數爲:8 – 明文長度 % 8,即補長長度在1至8bytes之間
如前述的明文for將補長爲:66 6F 72 05 05 05 05 05
ISO10126Padding,具體規範請參考ISO 10126。簡單的說ISO10126Padding就是補長的長度做爲補長內容的最後一個byte,以前的補長內容爲隨機數。如前述的明文for可能補長爲:66 6F 72 2A 75 EF F8 05
7. 密鑰的選擇
密鑰可使用KeyGenerator/KeyPairGenerator生成,也能夠由外部導入,還能夠有密鑰參數構造KeySpec再轉換爲Key。
6. 密鑰長度選擇
對於對稱加密算法,128bits的密鑰足夠安全,條件許可請選擇256bits,注意密鑰長度大於128bits需單獨下載並安裝jurisdiction policy files;對於非對稱加密算法,1024bits的密鑰足夠安全。
最後,如選用基於口令的算法或在用戶輸入密碼時,請避免使用String來引用,使用char[],用完馬上置空char[],避免內存攻擊,如heap dump分析等。
參考連接:http://www.ithao123.cn/content-10346659.html