java安全——加密
【1】對稱密碼(加密和解密都使用相同的密鑰) (干貨——對稱密碼定義:加密和解密都使用相同的密鑰) 1)Cipher類:?該類是所有加密算法的超類。
| (cipher—— ????-?n. 密碼;暗號;零 ????-?vi. 使用密碼;計算;做算術 ????-?vt. 計算;做算術;將…譯成密碼 ) |
1.1)獲取密碼對象:通過調用下面的getInstance方法可以獲得一個密碼對象: Cipher cipher = Cipher.getInstance(algorithName);
1.2)獲取密碼對象:或者調用下面這個方法:
Cipher cipher = Cipher.getInstance(algorithName, providerName);2)SunJCE:JDK中是由名為"SunJCE"的提供商提供密碼,如果沒有指定其他提供商,則會默認該提供商。
3)算法名稱:是一個字符串,比如"AES" 或者 "DES/CBC/PKCS5Padding"。 3.1)DES(data encoding standard):?即數據加密標準,是一個密鑰長度為56位的古老的分組密碼。 3.2)AES(advanced encoding standard):?更好的選擇是采用它的后續版本,即高級加密標準——AES;(干貨——高級加密標準——AES) 4)獲得一個密碼對象后,? step1)就可以通過設置模式和密鑰來對它初始化。 int mode = . . .; Key key = . . .; cipher.init(mode, key);Attention) A1)模式有以下幾種:Cipher.ENCRYPT_MODE Cipher.DECRYPT_MODE Cipher.WRAP_MODE Cipher.UNWRAP_MODEA2)wrap 和 unwrap模式用一個密鑰對另一個密鑰進行加密; step2)反復調用update方法來對數據塊進行加密。
step3)?完成上述操作后,還必須調用一次doFinal方法。
step3.1)如果有最后一個輸入數據塊(其字節數小于blockSize),那么就要調用:
outBytes = cipher.doFinal(inBytes, 0, inLength);step3.2)如果所有的輸入數據都已經加密,則用下面的方法調用來代替:
outBytes = cipher.doFinal();Attention)對doFinal的調用是必要的,以便對最后的塊進行"填充"。【2】密鑰生成(吧密鑰看做一個數學函數) 1)生成密鑰的steps step1)??為加密算法獲取KeyGenerator。
step2)?用隨機源來初始化密鑰發生器。如果密碼塊的長度是可變的,還需要指定期望的密碼塊長度。
step3)?調用generateKey方法。
2)看個荔枝:下面是如何生成AES密鑰的方法
keyGenerator keygen = KeyGenerator.getInstance("AES"); SecureRandom random = new SecureRandom(); keygen.init(random); Key key = keygen.generateKey(); 2.1)或者,可以從一組固定的原始數據(也許是由口令或者隨機擊鍵產生的)來生成一個密鑰,這時可以使用如下的SecretKeyFactory:(干貨——引入SecretKeyFactory) SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("AES"); byte[] keyData = . . .; // 16 bytes for AES SecretKeySpec keySpec = new SecretKeySpec(keyData, "AES"); Key key = keyFactory.generateSecret(keySpec);3)problem+solution:
3.1)problem:?如果要生成密鑰,必須使用"真正的隨機"數。例如,在Random類中的常規的隨機數發生器,是根據當前的日期和時間來產生隨機數的,因此它不夠隨機。例如,假設計算機時鐘可以精確到1/10秒,那么,每天最多存在864,000個種子。如果攻擊者知道發布密鑰的日期(通常可以由截止日期推算出來),那么就可以很容易地生成那一天所有可能的種子。
3.2)solution: SecureRandom類產生的隨機數,遠比由Random類產生的那些數字安全得多。一旦你在字節數組中收集到隨機位后,就可以將它傳遞給setSeed方法。(干貨-SecureRandom類產生的隨機數,遠比由Random類產生的那些數字安全得多。)?
SecureRandom secrand = new SecureRandom(); byte[] b = new byte[20]; // fill with truly random bits secrand.setSeed(b);Attention)?如果沒有為隨機數發生器提供種子,那么它將通過啟動線程,使它們睡眠,然后測量它們被喚醒的準確時間,來計算自己的20個字節的種子。4)代碼列表: package com.corejava.chapter9.cryption;import java.io.*; import java.security.*; import javax.crypto.*;public class AESTest {public static void main(String[] args) throws IOException, GeneralSecurityException, ClassNotFoundException{if (args[0].equals("-genkey")) // 產生密鑰{// 獲取密鑰生成器KeyGenerator keygen = KeyGenerator.getInstance("AES");// 創建隨機源SecureRandom random = new SecureRandom();// 用隨機源來初始化密鑰發生器keygen.init(random);// 生成密鑰SecretKey key = keygen.generateKey();try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(args[1]))){out.writeObject(key); // 寫出密鑰到文件}}else // 加密或者解密{int mode;// 加密(解密)模式if (args[0].equals("-encrypt")) mode = Cipher.ENCRYPT_MODE;else mode = Cipher.DECRYPT_MODE;// 帶資源的try 語句, args[3]==secret.keytry (ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[3]));InputStream in = new FileInputStream(args[1]);OutputStream out = new FileOutputStream(args[2])){Key key = (Key) keyIn.readObject();Cipher cipher = Cipher.getInstance("AES");cipher.init(mode, key); // 設置模式和密鑰對其初始化Util.crypt(in, out, cipher);}}} } package com.corejava.chapter9.cryption;import java.io.*; import java.security.*; import javax.crypto.*;public class Util {public static void crypt(InputStream in, OutputStream out, Cipher cipher) throws IOException,GeneralSecurityException{int blockSize = cipher.getBlockSize();int outputSize = cipher.getOutputSize(blockSize);byte[] inBytes = new byte[blockSize];byte[] outBytes = new byte[outputSize];int inLength = 0;boolean more = true;while (more){// inBytes 就是一個緩沖區inLength = in.read(inBytes);if (inLength == blockSize){// 只要輸入數據塊具有全長度(長度能夠被8整除): 就要調用update方法; int outLength = cipher.update(inBytes, 0, blockSize, outBytes);out.write(outBytes, 0, outLength);}else more = false;}// 而如果輸入數據塊不具有全長度(長度不能被8整除,此時需要填充): 就要調用 doFinal 方法;if (inLength > 0) outBytes = cipher.doFinal(inBytes, 0, inLength);elseoutBytes = cipher.doFinal();out.write(outBytes);} } 5)看個荔枝:
5.1)請按照如下 steps 使用上述程序:
step1)首先生成一個密鑰,運行如下命令行:
java AESTest -genkey secret.keystep2)密鑰就被保存在secret.key文件中了。現在可以用如下命令進行加密:
java AESTest -encrypt plaintextFile encryptedFile secret.keystep3)用如下命令進行解密:
java AESTest -decrypt encryptedFile decryptedFile secret.key E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>javac com/corejava/chapter9/cryption/AES Test.java E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST est -genkey com/corejava/chapter9/cryption/secret.key E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST est -encrypt com/corejava/chapter9/cryption/input.txt com/corejava/chapter9/cryption/output.txt com/ corejava/chapter9/cryption/secret.key E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST est -encrypt com/corejava/chapter9/cryption/input.txt com/corejava/chapter9/cryption/encrypted.txt c om/corejava/chapter9/cryption/secret.key E:\bench-cluster\cloud-data-preprocess\CoreJavaAdvanced\src>java com.corejava.chapter9.cryption.AEST est -decrypt com/corejava/chapter9/cryption/encrypted.txt com/corejava/chapter9/cryption/decrypted.t xt com/corejava/chapter9/cryption/secret.key (for full source code, please visit?https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter9/AES ) Conclusion) C1)該程序非常直觀。使用-genkey選項將產生一個新的密鑰,并且將其序列化到給定的文件中。 C2)該操作需要花費較長的時間,因為密鑰隨機生成器的初始化非常耗費時間。 C3)?-encrypt 和 -decrypt選項都調用相同的crypt方法,而crypt方法則調用密碼的update 和 doFinal方法。 Attention)?請注意update方法和doFinal方法是怎樣被調用的?(干貨——update方法和doFinal方法的被調用條件的區別) A1)只要輸入數據塊具有全長度(長度能夠被8整除):?就要調用update方法; A2)而如果輸入數據塊不具有全長度(長度不能被8整除,此時需要填充):?或者沒有更多額外的數據(以便生成一個填充字節),那么就要調用doFinal方法;【3】密碼流 1)?JCE(Java Cryptography Extension,java加密擴展)庫:?提供了一組使用非常方便的數據類,用于對流進行自動加密或解密。 2)看荔枝: 2.1)下面是對文件數據進行加密的方法: Cipher cipher = . . .; cipher.init(Cipher.ENCRYPT_MODE, key); CipherOutputStream out = new CipherOutputStream(new FileOutputStream(outputFileName), cipher); byte[] bytes = new byte[BLOCKSIZE]; int inLength = getData(bytes); // get data from data source while (inLength != -1) { out.write(bytes, 0, inLength); inLength = getData(bytes); // get more data from data source } out.flush(); 2.2)可以使用CipherInputStream,對文件的數據進行讀取和解密: Cipher cipher = . . .; cipher.init(Cipher.DECRYPT_MODE, key); CipherInputStream in = new CipherInputStream(new FileInputStream(inputFileName), cipher); byte[] bytes = new byte[BLOCKSIZE]; int inLength = in.read(bytes); while (inLength != -1) { putData(bytes, inLength); // put data to destination inLength = in.read(bytes); }Attention)?密碼流類能夠透明地進行update 和 doFinal方法的調用,所以非常方便。
【4】公共密鑰密碼(SSH免密碼登錄的加密解密形式) 1)problem+solution: 1.1)problem:?AES密碼是一種對稱密碼,加密和解密都使用相同的密鑰。對稱密碼的致命缺點在于密碼的分發。如果Alice給Bob發送一個加密方法,那么Bob需要使用與Alice相同的密鑰。如果Alice修改了密鑰,那么她必須在給Bob發送信息的同時,還要通過安全信道發送新的密鑰,但是也許她并不擁有到達Bob的安全信道,這就是為什么她必須首先對她發送給Bob的信息進行加密的原因。 1.2)solution:?公共密鑰密碼技術解決了這個問題。 2)公共密鑰技術的idea:?在公共密鑰密碼中,Bob擁有一個密鑰對,包括一個公共密鑰和一個相匹配的私有密鑰。Bob可以在任何地方公布公共密鑰,但是他必須嚴格保守他的私有密鑰。Alice只需要使用bob 的 公共密鑰對她發送給Bob的信息進行加密即可。(干貨——當Alice需要向Bob 發送加密文件時,那Alice就需要使用 Bob 的公共密鑰對她發送給Bob的信息進行加密) 3)solution+problem: 3.1)problem:實際上,加密過程并沒有那么簡單。所有已知的公共密鑰算法的操作速度都比對稱密鑰算法(比如DES或AES等)慢得多,使用公共密鑰算法對大量的信息進行加密是不切實際的。 3.2)solution:但是,如果像下面這樣,將公共密鑰密碼與快速的對稱密碼結合起來,這個問題就可以得到解決:(干貨——這里涉及到兩次加密——對明文加密 +?對對稱密鑰加密)
s1)?Alice生成一個隨機對稱加密密鑰,她用該密鑰對明文進行加密。(第一次加密:對明文加密)
s2)?Alice用Bob的公共密鑰給對稱密鑰進行加密。(第二次加密:對對稱密鑰加密)
s3)Alice將加密后的對稱密鑰和加密后的明文同時發送給Bob。
s4)?Bob用他的私有密鑰給對稱密鑰解密。
s5)?Bob用解密后的對稱密鑰給信息解密。
4)最普通的公共密鑰算法:?是Rivest, Shamir, 和 Adleman發明的RSA算法。
5)如何使用RSA算法?
step1)如果要使用RSA算法,需要一對公共/私有密鑰。你可以按如下方法使用KeyPairGenerator來獲得:
KeyPairGenerator pairgen = KeyPairGenerator.getInstance("RSA"); SecureRandom random = new SecureRandom(); pairgen.initialize(KEYSIZE, random); KeyPair keyPair = pairgen.generateKeyPair(); Key publicKey = keyPair.getPublic(); Key privateKey = keyPair.getPrivate(); step2)程序有三個選項。-genkey選項用于產生一個密鑰對,-encrypt選項用于生成AES密鑰,并且用公共密鑰對其進行包裝。 Key key = . . .; // an AES key Key publicKey = . . .; // a public RSA key Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.WRAP_MODE, publicKey); byte[] wrappedKey = cipher.wrap(key);step2.1)然后它便生成一個包含下列內容的文件(files):
f1)包裝過的密鑰的長度;
f2)包裝過的密鑰字節;
f3)用AES密鑰加密的明文;
Attention)-decrypt選項: ?用于對這樣的文件進行解密。
6)代碼列表
package com.corejava.chapter9.cryption;import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom;import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey;public class RSATest {private static final int KEYSIZE = 512;public static void main(String[] args) throws GeneralSecurityException, IOException, ClassNotFoundException {if(args[0].equals("-genkey")) // 生成密鑰對(公鑰+私鑰){// 密鑰對生成器KeyPairGenerator pairgen = KeyPairGenerator.getInstance("RSA");SecureRandom sr = new SecureRandom();pairgen.initialize(KEYSIZE, sr); // 密鑰對生成器初始化KeyPair pair = pairgen.generateKeyPair(); // 生成密鑰對(公鑰+私鑰)try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(args[1]))){out.writeObject(pair.getPublic()); // 寫入公鑰到文件}try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(args[2]))){out.writeObject(pair.getPrivate()); // 寫入私鑰到文件}}else if(args[0].equals("-encrypt")) // 加密模塊{// 生成密鑰KeyGenerator keygen = KeyGenerator.getInstance("AES");SecureRandom sr = new SecureRandom();keygen.init(sr);SecretKey key = keygen.generateKey();// wrap with RSA public key// args[3]==public.key,args[2]==encryptedFile,args[1]=inputFiletry(ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[3]));DataOutputStream dataOut = new DataOutputStream(new FileOutputStream(args[2]));InputStream in = new FileInputStream(args[1])){Key publicKey = (Key)keyIn.readObject();// 讀入公鑰Cipher cipher = Cipher.getInstance("RSA");// RSA密碼對象cipher.init(Cipher.WRAP_MODE, publicKey); // 通過設置打包模式和公鑰 來對RSA密碼對象進行初始化byte[] wrappedKey = cipher.wrap(key);// 通過帶有公鑰的RSA算法對象給密鑰加密dataOut.writeInt(wrappedKey.length); // 將加密后的密鑰寫入到輸出流 dataOutdataOut.write(wrappedKey);cipher = Cipher.getInstance("AES"); // AES 密碼對象cipher.init(Cipher.ENCRYPT_MODE, key); // 通過設置加密模式和密鑰 來對 AES 密碼對象進行初始化Util.crypt(in, dataOut, cipher); // 利用AES密碼對象對inFile 進行加密并寫入到輸出流 dataOut} }else // 解密模塊{//args[1]==encryptedFile,args[3]=private.key,args[2]=decryptedFile;try(DataInputStream dataIn = new DataInputStream(new FileInputStream(args[1]));ObjectInputStream keyIn = new ObjectInputStream(new FileInputStream(args[3]));OutputStream out = new FileOutputStream(args[2])){ int length = dataIn.readInt();byte[] wrappedKey = new byte[length];dataIn.read(wrappedKey, 0, length); // 讀入加密后的文件(經過公鑰加密后的密鑰 和 經過密鑰加密后的文件內容)// unwrap with RSA private keyKey privateKey = (Key)keyIn.readObject(); // 讀入private.key 到 wrappedKeyCipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.UNWRAP_MODE, privateKey); // 通過設置解包模式和私鑰 來對RSA密碼對象進行初始化// 通過帶有私鑰的RSA算法對象給密鑰解密Key key = cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);cipher = Cipher.getInstance("AES"); // AES 密碼對象cipher.init(Cipher.DECRYPT_MODE, key); // 通過設置解密模式和密鑰 來對 AES 密碼對象進行初始化 Util.crypt(dataIn, out, cipher); // 通過使用解密后的密鑰 對 加密后的文件內容 進行解密并寫入到輸出流 out} }} }<strong> </strong>
7)運行該程序的steps:
step1)首先生成RSA密鑰:
| java RSATest -genkey public.key private.key |
step2)然后對一個文件進行加密:
| java RSATest -encrypt plaintextFile encryptedFile public.key |
step3)最后,對文件進行解密,并且檢驗解密后的文件是否與明文相匹配。
| java RSATest -decrypt encryptedFile decryptedFile private.key |
Attention)但是我們沒有涉及許多高級和專有的話題,比如有:
A1)提供了對Kerberos協議進行支持的"通用安全服務"的GSS-API(原則上同樣支持其他安全信息交換協議)。
A2)對SASL的支持,SASL即簡單認證和安全層,可以為LDAP和IMAP協議所使用。
A3)對SSL的支持,SSL即安全套接層。在HTTP上使用SSL對應用程序的編程人員是透明的,只需要直接使用以https開頭的URL即可。
總結
以上是生活随笔為你收集整理的java安全——加密的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 打印头校准方法 具体怎么操作
- 下一篇: java平台脚本+java编译器API