1. 程式人生 > >條理清晰的入門:使用Java實現RSA加密解密

條理清晰的入門:使用Java實現RSA加密解密

條理清晰的入門:使用Java實現RSA加密解密

什麼是RSA

翻一下以前的密碼學筆記,找到了!

雖然字很潦草,但還是依稀可辨。簡單的說就是基於大數分解的困難性,造就了RSA的難破解性。不想打字, l a t

e x latex 太麻煩了。

使用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();

上面幾行程式碼已經產生了可以直接使用的公鑰、私鑰了。

進行加密解密

好了,有了金鑰之後怎麼進行加密、解密呢?以下為:

  1. 私鑰加密 公鑰解密
  2. 公鑰加密 私鑰解密
        /* 私鑰加密 公鑰解密 */
	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加密、解密的整個流程就很清楚了。