條理清晰的入門:使用Java實現RSA加密解密
條理清晰的入門:使用Java實現RSA加密解密
什麼是RSA
翻一下以前的密碼學筆記,找到了!
雖然字很潦草,但還是依稀可辨。簡單的說就是基於大數分解的困難性,造就了RSA的難破解性。不想打字,
太麻煩了。
使用Java
Java Doc裡面有很多有用的東西,想詳細瞭解可以直接看文件。可以直接線上看,也可以下載到本地看。
需要匯入的標頭檔案
如果你要直接複製貼上,那麼會有很多錯誤,懶得一個個點選匯入就直接複製匯入吧。
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.SecureRandom; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; import java.util.UUID; import javax.crypto.Cipher;
生成公鑰、私鑰
/* 生成公私鑰 */ KeyPairGenerator keyPairGenerator = null; try { keyPairGenerator = KeyPairGenerator.getInstance("RSA"); //NoSuchAlgorithmException } catch (Exception e) { e.printStackTrace(); } keyPairGenerator.initialize(2048); // 此處可以新增引數new SecureRandom(UUID.randomUUID().toString().getBytes()) KeyPair keyPair = keyPairGenerator.generateKeyPair(); RSAPublicKey rsaPublicKey = (RSAPublicKey)keyPair.getPublic(); RSAPrivateKey rsaPrivateKey = (RSAPrivateKey)keyPair.getPrivate();
上面幾行程式碼已經產生了可以直接使用的公鑰、私鑰了。
進行加密解密
好了,有了金鑰之後怎麼進行加密、解密呢?以下為:
- 私鑰加密 公鑰解密
- 公鑰加密 私鑰解密
/* 私鑰加密 公鑰解密 */
try {
Cipher cipher = Cipher.getInstance("RSA"); //NoSuchPaddingException
cipher.init(Cipher.ENCRYPT_MODE,rsaPrivateKey); //InvalidKeyException
byte[] b = cipher.doFinal("12345678".getBytes()); //BadPaddingException
System.out.println(new String(b));
cipher.init(Cipher.DECRYPT_MODE,rsaPublicKey);
b = cipher.doFinal(b);
System.out.println(new String(b));
}catch (Exception e) {
e.printStackTrace();
}
/* 公鑰加密 私鑰解密 */
try {
Cipher cipher = Cipher.getInstance("RSA"); //NoSuchPaddingException
cipher.init(Cipher.ENCRYPT_MODE,rsaPublicKey); //InvalidKeyException
byte[] b = cipher.doFinal("12345678".getBytes()); //BadPaddingException
System.out.println(new String(b));
cipher.init(Cipher.DECRYPT_MODE,rsaPrivateKey);
b = cipher.doFinal(b);
System.out.println(new String(b));
}catch (Exception e) {
e.printStackTrace();
}
這裡需要注意的是,根據金鑰位數不同,一次可被加密的長度也是不同的,在上面使用了2048的加密位數,那麼一次加密只能256位元組。
可能會看到有的加密長度是 “2048/8 - 11 ”,那麼這個“ - 11 ”是什麼意思呢?我們知道明文過長需要進行分組( padding ),既然要分組,就必然有分組標準:RSA_PKCS1_PADDING、RSA_PKCS1_OAEP_PADDING、RSA_NO_PADDING。而“ - 11 ”就是第一個標準的填充方式,詳細瞭解就網上搜索吧。
/* 根據長度判斷是否需要分組 */
System.out.println(rsaPrivateKey.getModulus().bitLength() / 8);
System.out.println(rsaPublicKey.getModulus().bitLength() / 8);
金鑰的儲存
儲存必然是使用檔案來儲存的,查詢文件,發現金鑰可以轉化為位元組,同時位元組也能轉化回金鑰。到這裡發現問題很簡單了。
但是這裡要注意的是,轉化為位元組儲存到檔案是簡單的,但是從檔案讀取到位元組( byte []),一次讀取可能不能讀取完整整個檔案,需要多次讀取,那麼多次讀取的位元組放到哪裡呢,在這裡我使用了將讀取的位元組轉換為字串( String ),進行拼接之後再還原回字節。
那麼問題就出現了,位元組轉到到字串再轉化回來,這個過程其實是會出現問題的,如圖可以看到byte []原長度和轉化後的長度是不一樣的。這裡的解決方式是,將位元組進行Base64編碼再轉為字串,轉回的時候則進行相應的Base64解碼操作。
/* 公私鑰與字串的轉換 */
try {
Base64.Decoder decoder = Base64.getDecoder();
Base64.Encoder encoder = Base64.getEncoder();
String publicKeyString = encoder.encodeToString(rsaPublicKey.getEncoded());
String privateKeyString = encoder.encodeToString(rsaPrivateKey.getEncoded());
System.out.println(publicKeyString);
System.out.println(privateKeyString);
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); //NoSuchAlgorithmException
byte[] keyBytes = decoder.decode(publicKeyString);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
keyFactory.generatePublic(x509EncodedKeySpec); //InvalidKeySepcException
keyBytes = decoder.decode(privateKeyString);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
keyFactory.generatePrivate(pkcs8EncodedKeySpec); //InvalidKeySepcException
} catch (Exception e) {
e.printStackTrace();
}
以上程式碼只進行了到字串的轉換,如果需要儲存到檔案中,那麼只需要
FileOutputStream fos = new FileOutputStream("C:\\Users\\XXX\\Desktop\\private");
fos.write(encoder.encodeToString(rsaPrivateKey.getEncoded()).getBytes());
密文的儲存、讀取
看到金鑰的儲存需要進行Base64的編碼解碼,那麼不禁想,密文是不是也同樣需要呢?
答案是可以需要,也可以不需要。
既然要對密文進行解密,那麼私鑰或者公鑰是一定已經有的,在獲取金鑰之後,我們可以計算出每段加密完成之後密文的位元組數,每次按照這個位元組數進行檔案的讀取,讀取後直接解密成字串即可。
/* 寫入 */
FileOutputStream fospass = new FileOutputStream("C:\\Users\\XXX\\Desktop\\password");
byte [] buffer = new byte[256];
b = cipher.doFinal( (“XXX" + "\r\n\r\n").getBytes());
fospass.write(b);
fospass.close();
/* 讀取 */
FileInputStream fis = new FileInputStream("..\\password");
byte [] buffer = new byte[256];
fis.read(buffer);
fis.close();
s = new String(cipher.doFinal(buffer));
OK,到這裡使用Java進行RSA加密、解密的整個流程就很清楚了。