1. 程式人生 > >Java中使用OpenSSL生成的RSA公私鑰進行資料加解密

Java中使用OpenSSL生成的RSA公私鑰進行資料加解密

RSA是什麼:RSA公鑰加密演算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美國麻省理工學院)開發的。RSA取名來自開發他們三者的名字。RSA是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的所有密碼攻擊,已被ISO推薦為公鑰資料加密標準。目前該加密方式廣泛用於網上銀行、數字簽名等場合。RSA演算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困難,因此可以將乘積公開作為加密金鑰。

OpenSSL是什麼:眾多的密碼演算法、公鑰基礎設施標準以及SSL協議,或許這些有趣的功能會讓你產生實現所有這些演算法和標準的想法。果真如此,在對你表示敬佩的同時,還是忍不住提醒你:這是一個令人望而生畏的過程。這個工作不再是簡單的讀懂幾本密碼學專著和協議文件那麼簡單,而是要理解所有這些演算法、標準和協議文件的每一個細節,並用你可能很熟悉的C語言字元一個一個去實現這些定義和過程。我們不知道你將需要多少時間來完成這項有趣而可怕的工作,但肯定不是一年兩年的問題。OpenSSL就是由Eric A. Young和Tim J. Hudson兩位絕世大好人自1995年就開始編寫的集合眾多安全演算法的演算法集合。通過命令或者開發庫,我們可以輕鬆實現標準的公開演算法應用。

我的一個假設應用背景:

隨著移動網際網路的普及,為移動裝置開發的應用也層出不窮。這些應用往往伴隨著使用者註冊與密碼驗證的功能。”網路傳輸“、”應用程式日誌訪問“中的安全性都存在著隱患。密碼作為使用者的敏感資料,特別需要開發者在應用上線之前做好安全防範。處理不當,可能會造成諸如商業競爭對手的惡意攻擊、第三方合作商的訴訟等問題。

RSA演算法雖然有這麼多好處,但是在網上找不到一個完整的例子來說明如何操作。下面我就來介紹一下:

一、使用OpenSSL來生成私鑰和公鑰

我使用的是Linux系統,已經安裝了OpenSSL軟體包,此時請驗證你的機器上已經安裝了OpenSSL,執行命令應當出現如下資訊:

[[email protected]
~]# openssl version -a OpenSSL 1.0.0-fips 29 Mar 2010 built on: Wed Jan 25 02:17:15 GMT 2012 platform: linux-x86_64 options: bn(64,64) md2(int) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx) compiler: gcc -fPIC -DOPENSSL_PIC -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -DKRB5_MIT -m64 -DL_ENDIAN -DTERMIO -Wall -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -Wa,--noexecstack -DMD32_REG_T=int -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DMD5_ASM -DAES_ASM -DWHIRLPOOL_ASM OPENSSLDIR: "/etc/pki/tls" engines: aesni dynamic

先來生成私鑰:

[[email protected] ~]# openssl genrsa -out rsa_private_key.pem 1024
Generating RSA private key, 1024 bit long modulus
.......................++++++
..++++++
e is 65537 (0x10001)
這條命令讓openssl隨機生成了一份私鑰,加密長度是1024位。加密長度是指理論上最大允許”被加密的資訊“長度的限制,也就是明文的長度限制。隨著這個引數的增大(比方說2048),允許的明文長度也會增加,但同時也會造成計算複雜度的極速增長。一般推薦的長度就是1024位(128位元組)。

我們來看一下私鑰的內容:

[[email protected] ~]# cat rsa_private_key.pem 
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQChDzcjw/rWgFwnxunbKp7/4e8w/UmXx2jk6qEEn69t6N2R1i/L
mcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRhtVx1uOH/2U378fscEESEG8XDq
ll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNgXIlk3gdhnzh+uoEQywIDAQAB
AoGAaeKk76CSsp7k90mwyWP18GhLZru+vEhfT9BpV67cGLg1owFbntFYQSPVsTFm
U2lWn5HD/IcV+EGaj4fOLXdM43Kt4wyznoABSZCKKxs6uRciu8nQaFNUy4xVeOfX
PHU2TE7vi4LDkw9df1fya+DScSLnaDAUN3OHB5jqGL+Ls5ECQQDUfuxXN3uqGYKk
znrKj0j6pY27HRfROMeHgxbjnnApCQ71SzjqAM77R3wIlKfh935OIV0aQC4jQRB4
iHYSLl9lAkEAwgh4jxxXeIAufMsgjOi3qpJqGvumKX0W96McpCwV3Fsew7W1/msi
suTkJp5BBvjFvFwfMAHYlJdP7W+nEBWkbwJAYbz/eB5NAzA4pxVR5VmCd8cuKaJ4
EgPLwsjI/mkhrb484xZ2VyuICIwYwNmfXpA3yDgQWsKqdgy3Rrl9lV8/AQJAcjLi
IfigUr++nJxA8C4Xy0CZSoBJ76k710wdE1MPGr5WgQF1t+P+bCPjVAdYZm4Mkyv0
/yBXBD16QVixjvnt6QJABli6Zx9GYRWnu6AKpDAHd8QjWOnnNfNLQHue4WepEvkm
CysG+IBs2GgsXNtrzLWJLFx7VHmpqNTTC8yNmX1KFw==
-----END RSA PRIVATE KEY-----

內容都是標準的ASCII字元,開頭一行和結尾一行有明顯的標記,真正的私鑰資料是中間的不規則字元。

2015年3月24日補充:金鑰檔案最終將資料通過Base64編碼進行儲存。可以看到上述金鑰檔案內容每一行的長度都很規律。這是由於RFC2045中規定:The encoded output stream must be represented in lines of no more than 76 characters each。也就是說Base64編碼的資料每行最多不超過76字元,對於超長資料需要按行分割。

接下來根據私鑰生成公鑰:

[[email protected] ~]# openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
writing RSA key
再來看一下公鑰的內容:
[[email protected] ~]# cat rsa_public_ley.pem 
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w
/UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht
Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg
XIlk3gdhnzh+uoEQywIDAQAB
-----END PUBLIC KEY-----
這時候的私鑰還不能直接被使用,需要進行PKCS#8編碼
[[email protected] ~]# openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt
命令中指明瞭輸入私鑰檔案為rsa_private_key.pem,輸出私鑰檔案為pkcs8_rsa_private_key.pem,不採用任何二次加密(-nocrypt)

再來看一下,編碼後的私鑰檔案是不是和之前的私鑰檔案不同了:

[[email protected] ~]# cat pkcs8_rsa_private_key.pem 
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG
6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4
1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA
ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm
u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j
DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo
MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ
DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM
6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ
FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA
2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX
TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh
Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo
1NMLzI2ZfUoX
-----END PRIVATE KEY-----

至此,可用的金鑰對已經生成好了,私鑰使用pkcs8_rsa_private_key.pem,公鑰採用rsa_public_key.pem。

2014年5月20日補充:最近又遇到RSA加密的需求了,而且對方要求只能使用第一步生成的未經過PKCS#8編碼的私鑰檔案。後來檢視相關文獻得知第一步生成的私鑰檔案編碼是PKCS#1格式,這種格式Java其實是支援的,只不過多寫兩行程式碼而已:

RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(priKeyData));
RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());
KeyFactory keyFactory= KeyFactory.getInstance("RSA");
PrivateKey priKey= keyFactory.generatePrivate(rsaPrivKeySpec);
首先將PKCS#1的私鑰檔案讀取出來(注意去掉減號開頭的註釋內容),然後使用Base64解碼讀出的字串,便得到priKeyData,也就是第一行程式碼中的引數。最後一行得到了私鑰。接下來的用法就沒什麼區別了。

參考文獻:https://community.oracle.com/thread/1529240?start=0&tstart=0

二、編寫Java程式碼實際測試

2012年2月23日補充:在標準JDK中只是規定了JCE(JCE (Java Cryptography Extension) 是一組包,它們提供用於加密、金鑰生成和協商以及 Message Authentication Code(MAC)演算法的框架和實現。它提供對對稱、不對稱、塊和流密碼的加密支援,它還支援安全流和密封的物件。)介面,但是內部實現需要自己或者第三方提供。因此我們這裡使用bouncycastle的開源的JCE實現包,下載地址:http://bouncycastle.org/latest_releases.html,我使用的是bcprov-jdk16-146.jar,這是在JDK1.6環境下使用的。如果需要其他JDK版本下的實現,可以在之前的下載頁面中找到對應版本。

下面來看一下我實現的程式碼:

package net.csdn.blog.chaijunkun;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import sun.misc.BASE64Decoder;

public class RSAEncrypt {
	
	private static final String DEFAULT_PUBLIC_KEY= 
		"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChDzcjw/rWgFwnxunbKp7/4e8w" + "\r" +
		"/UmXx2jk6qEEn69t6N2R1i/LmcyDT1xr/T2AHGOiXNQ5V8W4iCaaeNawi7aJaRht" + "\r" +
		"Vx1uOH/2U378fscEESEG8XDqll0GCfB1/TjKI2aitVSzXOtRs8kYgGU78f7VmDNg" + "\r" +
		"XIlk3gdhnzh+uoEQywIDAQAB" + "\r";
	
	private static final String DEFAULT_PRIVATE_KEY=
		"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKEPNyPD+taAXCfG" + "\r" +
		"6dsqnv/h7zD9SZfHaOTqoQSfr23o3ZHWL8uZzINPXGv9PYAcY6Jc1DlXxbiIJpp4" + "\r" +
		"1rCLtolpGG1XHW44f/ZTfvx+xwQRIQbxcOqWXQYJ8HX9OMojZqK1VLNc61GzyRiA" + "\r" +
		"ZTvx/tWYM2BciWTeB2GfOH66gRDLAgMBAAECgYBp4qTvoJKynuT3SbDJY/XwaEtm" + "\r" +
		"u768SF9P0GlXrtwYuDWjAVue0VhBI9WxMWZTaVafkcP8hxX4QZqPh84td0zjcq3j" + "\r" +
		"DLOegAFJkIorGzq5FyK7ydBoU1TLjFV459c8dTZMTu+LgsOTD11/V/Jr4NJxIudo" + "\r" +
		"MBQ3c4cHmOoYv4uzkQJBANR+7Fc3e6oZgqTOesqPSPqljbsdF9E4x4eDFuOecCkJ" + "\r" +
		"DvVLOOoAzvtHfAiUp+H3fk4hXRpALiNBEHiIdhIuX2UCQQDCCHiPHFd4gC58yyCM" + "\r" +
		"6Leqkmoa+6YpfRb3oxykLBXcWx7DtbX+ayKy5OQmnkEG+MW8XB8wAdiUl0/tb6cQ" + "\r" +
		"FaRvAkBhvP94Hk0DMDinFVHlWYJ3xy4pongSA8vCyMj+aSGtvjzjFnZXK4gIjBjA" + "\r" +
		"2Z9ekDfIOBBawqp2DLdGuX2VXz8BAkByMuIh+KBSv76cnEDwLhfLQJlKgEnvqTvX" + "\r" +
		"TB0TUw8avlaBAXW34/5sI+NUB1hmbgyTK/T/IFcEPXpBWLGO+e3pAkAGWLpnH0Zh" + "\r" +
		"Fae7oAqkMAd3xCNY6ec180tAe57hZ6kS+SYLKwb4gGzYaCxc22vMtYksXHtUeamo" + "\r" +
		"1NMLzI2ZfUoX" + "\r";

	/**
	 * 私鑰
	 */
	private RSAPrivateKey privateKey;

	/**
	 * 公鑰
	 */
	private RSAPublicKey publicKey;
	
	/**
	 * 位元組資料轉字串專用集合
	 */
	private static final char[] HEX_CHAR= {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	

	/**
	 * 獲取私鑰
	 * @return 當前的私鑰物件
	 */
	public RSAPrivateKey getPrivateKey() {
		return privateKey;
	}

	/**
	 * 獲取公鑰
	 * @return 當前的公鑰物件
	 */
	public RSAPublicKey getPublicKey() {
		return publicKey;
	}

	/**
	 * 隨機生成金鑰對
	 */
	public void genKeyPair(){
		KeyPairGenerator keyPairGen= null;
		try {
			keyPairGen= KeyPairGenerator.getInstance("RSA");
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		keyPairGen.initialize(1024, new SecureRandom());
		KeyPair keyPair= keyPairGen.generateKeyPair();
		this.privateKey= (RSAPrivateKey) keyPair.getPrivate();
		this.publicKey= (RSAPublicKey) keyPair.getPublic();
	}

	/**
	 * 從檔案中輸入流中載入公鑰
	 * @param in 公鑰輸入流
	 * @throws Exception 載入公鑰時產生的異常
	 */
	public void loadPublicKey(InputStream in) throws Exception{
		try {
			BufferedReader br= new BufferedReader(new InputStreamReader(in));
			String readLine= null;
			StringBuilder sb= new StringBuilder();
			while((readLine= br.readLine())!=null){
				if(readLine.charAt(0)=='-'){
					continue;
				}else{
					sb.append(readLine);
					sb.append('\r');
				}
			}
			loadPublicKey(sb.toString());
		} catch (IOException e) {
			throw new Exception("公鑰資料流讀取錯誤");
		} catch (NullPointerException e) {
			throw new Exception("公鑰輸入流為空");
		}
	}


	/**
	 * 從字串中載入公鑰
	 * @param publicKeyStr 公鑰資料字串
	 * @throws Exception 載入公鑰時產生的異常
	 */
	public void loadPublicKey(String publicKeyStr) throws Exception{
		try {
			BASE64Decoder base64Decoder= new BASE64Decoder();
			byte[] buffer= base64Decoder.decodeBuffer(publicKeyStr);
			KeyFactory keyFactory= KeyFactory.getInstance("RSA");
			X509EncodedKeySpec keySpec= new X509EncodedKeySpec(buffer);
			this.publicKey= (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("無此演算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("公鑰非法");
		} catch (IOException e) {
			throw new Exception("公鑰資料內容讀取錯誤");
		} catch (NullPointerException e) {
			throw new Exception("公鑰資料為空");
		}
	}

	/**
	 * 從檔案中載入私鑰
	 * @param keyFileName 私鑰檔名
	 * @return 是否成功
	 * @throws Exception 
	 */
	public void loadPrivateKey(InputStream in) throws Exception{
		try {
			BufferedReader br= new BufferedReader(new InputStreamReader(in));
			String readLine= null;
			StringBuilder sb= new StringBuilder();
			while((readLine= br.readLine())!=null){
				if(readLine.charAt(0)=='-'){
					continue;
				}else{
					sb.append(readLine);
					sb.append('\r');
				}
			}
			loadPrivateKey(sb.toString());
		} catch (IOException e) {
			throw new Exception("私鑰資料讀取錯誤");
		} catch (NullPointerException e) {
			throw new Exception("私鑰輸入流為空");
		}
	}

	public void loadPrivateKey(String privateKeyStr) throws Exception{
		try {
			BASE64Decoder base64Decoder= new BASE64Decoder();
			byte[] buffer= base64Decoder.decodeBuffer(privateKeyStr);
			PKCS8EncodedKeySpec keySpec= new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory= KeyFactory.getInstance("RSA");
			this.privateKey= (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("無此演算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("私鑰非法");
		} catch (IOException e) {
			throw new Exception("私鑰資料內容讀取錯誤");
		} catch (NullPointerException e) {
			throw new Exception("私鑰資料為空");
		}
	}

	/**
	 * 加密過程
	 * @param publicKey 公鑰
	 * @param plainTextData 明文資料
	 * @return
	 * @throws Exception 加密過程中的異常資訊
	 */
	public byte[] encrypt(RSAPublicKey publicKey, byte[] plainTextData) throws Exception{
		if(publicKey== null){
			throw new Exception("加密公鑰為空, 請設定");
		}
		Cipher cipher= null;
		try {
			cipher= Cipher.getInstance("RSA", new BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			byte[] output= cipher.doFinal(plainTextData);
			return output;
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("無此加密演算法");
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
			return null;
		}catch (InvalidKeyException e) {
			throw new Exception("加密公鑰非法,請檢查");
		} catch (IllegalBlockSizeException e) {
			throw new Exception("明文長度非法");
		} catch (BadPaddingException e) {
			throw new Exception("明文資料已損壞");
		}
	}

	/**
	 * 解密過程
	 * @param privateKey 私鑰
	 * @param cipherData 密文資料
	 * @return 明文
	 * @throws Exception 解密過程中的異常資訊
	 */
	public byte[] decrypt(RSAPrivateKey privateKey, byte[] cipherData) throws Exception{
		if (privateKey== null){
			throw new Exception("解密私鑰為空, 請設定");
		}
		Cipher cipher= null;
		try {
			cipher= Cipher.getInstance("RSA", new BouncyCastleProvider());
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			byte[] output= cipher.doFinal(cipherData);
			return output;
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("無此解密演算法");
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
			return null;
		}catch (InvalidKeyException e) {
			throw new Exception("解密私鑰非法,請檢查");
		} catch (IllegalBlockSizeException e) {
			throw new Exception("密文長度非法");
		} catch (BadPaddingException e) {
			throw new Exception("密文資料已損壞");
		}		
	}

	
	/**
	 * 位元組資料轉十六進位制字串
	 * @param data 輸入資料
	 * @return 十六進位制內容
	 */
	public static String byteArrayToString(byte[] data){
		StringBuilder stringBuilder= new StringBuilder();
		for (int i=0; i<data.length; i++){
			//取出位元組的高四位 作為索引得到相應的十六進位制識別符號 注意無符號右移
			stringBuilder.append(HEX_CHAR[(data[i] & 0xf0)>>> 4]);
			//取出位元組的低四位 作為索引得到相應的十六進位制識別符號
			stringBuilder.append(HEX_CHAR[(data[i] & 0x0f)]);
			if (i<data.length-1){
				stringBuilder.append(' ');
			}
		}
		return stringBuilder.toString();
	}


	public static void main(String[] args){
		RSAEncrypt rsaEncrypt= new RSAEncrypt();
		//rsaEncrypt.genKeyPair();

		//載入公鑰
		try {
			rsaEncrypt.loadPublicKey(RSAEncrypt.DEFAULT_PUBLIC_KEY);
			System.out.println("載入公鑰成功");
		} catch (Exception e) {
			System.err.println(e.getMessage());
			System.err.println("載入公鑰失敗");
		}

		//載入私鑰
		try {
			rsaEncrypt.loadPrivateKey(RSAEncrypt.DEFAULT_PRIVATE_KEY);
			System.out.println("載入私鑰成功");
		} catch (Exception e) {
			System.err.println(e.getMessage());
			System.err.println("載入私鑰失敗");
		}

		//測試字串
		String encryptStr= "Test String chaijunkun";

		try {
			//加密
			byte[] cipher = rsaEncrypt.encrypt(rsaEncrypt.getPublicKey(), encryptStr.getBytes());
			//解密
			byte[] plainText = rsaEncrypt.decrypt(rsaEncrypt.getPrivateKey(), cipher);
			System.out.println("密文長度:"+ cipher.length);
			System.out.println(RSAEncrypt.byteArrayToString(cipher));
			System.out.println("明文長度:"+ plainText.length);
			System.out.println(RSAEncrypt.byteArrayToString(plainText));
			System.out.println(new String(plainText));
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
	}
}

程式碼中我提供了兩種載入公鑰和私鑰的方式。

按流來讀取:適合在android應用中按ID索引資源得到InputStream的方式;

按字串來讀取:就像程式碼中展示的那樣,將金鑰內容按行儲存到靜態常量中,按String型別匯入金鑰。

執行上面的程式碼,會顯示如下資訊:
載入公鑰成功
載入私鑰成功
密文長度:128
35 b4 6f 49 69 ae a3 85 a2 a5 0d 45 75 00 23 23 e6 70 69 b4 59 ae 72 6f 6d d3 43 e1 d3 44 85 eb 04 57 2c 46 3e 70 09 4d e6 4c 83 50 c7 56 75 80 c7 e1 31 64 57 c8 e3 46 a7 ce 57 31 ac cd 21 89 89 8f c1 24 c1 22 0c cb 70 6a 0d fa c9 38 80 ba 2e e1 29 02 ed 45 9e 88 e9 23 09 87 af ad ab ac cb 61 03 3c a1 81 56 a5 de c4 79 aa 3e 48 ee 30 3d bc 5b 47 50 75 9f fd 22 87 9e de b1 f4 e8 b2
明文長度:22
54 65 73 74 20 53 74 72 69 6e 67 20 63 68 61 69 6a 75 6e 6b 75 6e
Test String chaijunkun

在main函式中我註釋掉了”rsaEncrypt.genKeyPair()“,這個方法是用來隨機生成金鑰對的(只生成、使用,不儲存)。當不使用檔案金鑰時,可以將載入金鑰的程式碼註釋,啟用本方法,也可以跑通程式碼。

載入公鑰與載入私鑰的不同點在於公鑰載入時使用的是X509EncodedKeySpec(X509編碼的Key指令),私鑰載入時使用的是PKCS8EncodedKeySpec(PKCS#8編碼的Key指令)。

2012年2月22日補充:在android軟體開發的過程中,發現上述程式碼不能正常工作,主要原因在於sun.misc.BASE64Decoder類在android開發包中不存在。因此需要特別在網上尋找rt.jar的原始碼,至於JDK的src.zip中的原始碼,這個只是JDK中的部分原始碼,上述的幾個類的程式碼都沒有。經過尋找並新增,上述程式碼在android應用中能夠很好地工作。其中就包含這個類的對應程式碼。另外此類還依賴於CEFormatException、CEStreamExhausted、CharacterDecoder和CharacterEncoder類和異常定義。

2012年2月23日補充:起初,我寫這篇文章是想不依賴於任何第三方包來實現RSA的加密與解密,然而後續遇到了問題。由於在加密方法encrypt和解密方法decrypt中都要建立一個Cipher物件,這個物件只能通過getInstance來獲取例項。它有兩種:第一個是隻指定演算法,不指定提供者Provider的;第二個是兩個都要指定的。起初沒有指定,程式碼依然能夠跑通,但是你會發現,每次加密的結果都不一樣。後來分析才知道Cipher物件使用的公私鑰是內部自己隨機生成的,不是程式碼中指定的公私鑰。奇怪的是,這種不指定Provider的程式碼能夠在android應用中跑通,而且每次加密的結果都相同。我想,android的SDK中除了系統的一些開發函式外,自己也實現了JDK的功能,可能在它自己的JDK中已經提供了相應的Provider,才使得每次加密結果相同。當我像網上的示例程式碼那樣加入了bouncycastle的Provider後,果然每次加密的結果都相同了。

參考文獻:

金鑰對生成:http://www.howforge.com/how-to-generate-key-pair-using-openssl

私鑰編碼格式轉換:http://shuany.iteye.com/blog/730910

相關推薦

Java使用OpenSSL生成RSA公私進行資料解密

RSA是什麼:RSA公鑰加密演算法是1977年由Ron Rivest、Adi Shamirh和LenAdleman在(美國麻省理工學院)開發的。RSA取名來自開發他們三者的名字。RSA是目前最有影響力的公鑰加密演算法,它能夠抵抗到目前為止已知的所有密碼攻擊,已被ISO推薦為公

Java使用OpenSSL RSA公私進行資料解密儲存

使用openSSL 生成公私鑰可以參考作者 JackCousins 的文章 @http://blog.csdn.net/chaijunkun/article/details/7275632 寫的非常好,鼓掌 但在使用的過程中發現 ,例如 我在手機中可以使用公鑰簽名,伺服器

Java使用OpenSSL生成RSA公私資料進行解密

Java基於OpenSSL的公私鑰對資料進行加解密 使用OpenSSL來生成私鑰和公鑰 RSA加密解密工具類 簽名及校驗類 測試類 使用OpenSSL來生成私鑰和公鑰

java生成RSA公私字串,簡單易懂

java生成RSA公私鑰字串,簡單易懂   解決方法: 1.下載bcprov-jdk16-140.jar包,參考:http://www.yayihouse.com/yayishuwu/chapter/1537 2.java程式碼 KeyPairGenerator

RSA加密解密(直接使用openssl生成公私)

期初做加密的時候就是想要直接使用openssl生成的公鑰和私鑰,搗鼓了好久才弄出來,昨天將檔案的方式做出來以後反覆比較加密過程。最終了解到,解密的過程中需要的私鑰是需要一個“頭”的。而這個所謂的“頭”其實就是我們生成的pem檔案的一些配置項。 在上一篇文章RS

openssl生成rsa

openssl rsa 生成私鑰openssl genrsa -out rsa_1024_pri.pem 1024生成公鑰openssl rsa -pubout -in rsa_1024_pri.pem -out rsa_1024_pub.pem用openssl生成rsa密鑰對

如何使用openssl生成RSA和私

在ubuntu上要使用openssl的話需要先進行安裝,命令如下: sudo apt-get install openssl 安裝完成就可以使用openssl了。 首先需要進入openssl的互動介面,在命令行了輸入openssl即可; 1)生成RSA私鑰:

使用openssl生成RSA和私

在ubuntu上要使用openssl的話需要先進行安裝,命令如下: #sudo apt-get install openssl #openssl 首先需要進入openssl的互動介面,在命令行

生成rsa 公私

生成私鑰 openssl genrsa -out rsa_private.key 1024 私鑰(pkcs1) openssl rsa -in rsa_private.key -pubou

NetCore 生成RSA公私對,公加密私解密,私加密公解密

using Newtonsoft.Json; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Encodings; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastl

使用OpenSSL生成RSA和私

Windows下生成RSA公鑰和私鑰 開啟 openssl 資料夾下的 bin 資料夾,執行 openssl.exe 檔案 生成 RSA 私鑰,出現圖中提示說明生成成功 genrsa -out

java程式碼生成相容openssl可用的rsa公私,pkcs8轉換pkcs1,

java預設的KeyPairGenerator生成的rsa 公私鑰不能直接被openssl來使用 java生成的私鑰是pkcs8 格式 公鑰是x.509格式 openssl生成和使用的是pkcs1格式,pem公私鑰檔案,所以需要轉換 本專案可以動態生成openssl相容的,o

Python rsa公私生成 rsa加密(分段加密)私簽實戰

you port pen man length comment 數據加密 自己 keygen 一般現在的SAAS服務提供現在的sdk或api對接服務都涉及到一個身份驗證和數據加密的問題。一般現在普遍的做法就是配置使用非對稱加密的方式來解決這個問題,你持有SAAS公司的公鑰,

JAVA代碼-數字證書公私生成-公cer ,私jks, pfx格式

else generator actor issue 自己 bytearray 私鑰 公私鑰 throws import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStre

java驗證openssl生成的ssl證書和私是否匹配

      最近有一個需求上傳ssl證書和私鑰,但是上傳之前需要驗證ssl證書和私鑰是否正確,其中的業務邏輯涉及到以下幾點: 一、讀取ssl證書,讀取ssl證書公鑰       要實現該功能比較簡單,java裡面有現成的api支援。 

RSA公私生成RSA證書建立”利器”

一.建立pkcs8格式的RSA私鑰和和公鑰 步驟如下: 1.生成 RSA 私鑰 openssl genRSA -out RSAprivatekey.pem 1024 2.生成對應的公鑰 openssl RSA -in RSAprivatekey.pem -pubout -out RSApublic

openssl證書初始生成證書公私的方法

公鑰字尾:pem(firefox支援此格式),crt(微軟支援此格式),key。 私鑰字尾:pfx,p12,pem,key。 OpenSSL:安全套接層協議。 pfx字尾的意思: 包含公鑰和私鑰

java隨機生成一個無序且長度不大於10的字符串

image ack con 技術 exti pen collect tint span package xiangmu; import java.util.ArrayList; import java.util.Collections; import java.ut

Java動態生成當前日期的文件

java;文件名自動生成;1.Java中動態生成當前日期的文件名稱並且將控制臺的輸出信息輸入到文件中 public static void SaveClonseToFile() throws IOException, FileNotFoundException { File f =

java正則表達式要進行轉義的字符。

ace {} 則表達式 cap str AC stat exp 正則表達 /** * 轉義正則特殊字符 ($()*+.[]?\^{},|) * * @param keyword * @return */public static String escapeExprSpec