RSA算法的Java實現
在Java實現該算法前我們先完成Java開發環境的配置,筆者選擇的是JDK1.8版本,開發環境用Eclipse
開發環境配置
- JDK安裝
- Eclipse安裝
JDK安裝
(1)JDK下載鏈接,進入鏈接後找到以下所示界面:
選擇最後一列Windows x64位的下載
x86適用於32位操作系統,x64適用於64位以及32位操作系統
下載完成並放置於C盤中:
(2)配置環境變量
配置JAVA_HOME,Path,CLASSPATH
- JAVA_HOME
右鍵點擊文件夾中“此電腦”選項,選擇“屬性”,選擇其中的“高級系統設置”,
出現如圖所示
點擊環境變量選擇系統變量新建,配置如下:
變量名填:JAVA_HOME
變量值:填JDK的安裝目錄,及如圖所示
PS:點擊最上邊一欄即可得到JDK安裝目錄,然後複製該地址到變量值即可 - Path
同配置JAVA_HOME操作一樣,不過變量值不一樣
Path的值: %JAVA_HOME%\bin
%JAVA_HOME%\bin是Windows操作系統用來尋找可執行文件的路徑,當然在我們的JDK目錄下的jre中也有bin文件,其中也有部分執行文件,這個配不配置基本不影響的,如果需要,將Path設置爲%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin即可
- CLASSPATH
同上述環境變量的配置一樣
CLASSPATH的值爲: .;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar
CLASSPATH的路徑告訴類裝載器到哪裏去尋找第三方提供的類和用戶定義的類
dt.jar:運行環境類庫,主要是Swing包
tools.jar:工具類庫
到此爲止,JDK的配置即完成。查看是否配置成功:
打開cmd輸入java –version以及java ,javac命令無異常即代表成功
Eclipse安裝
下載鏈接
如正常下載軟件一樣,下載並安裝即可使用
RSA的算法原理
我簡單介紹一下RSA算法的基本原理
RSA算法基於大整數因子分解困難性問題
- 首先選擇兩個互異的大素數p和q(保密),計算n=p*q(公開),βn=(p-1)*(q-1)(保密)
- 選擇一個隨機整數 e (0<e<βn)(公開),滿足 gcd(e,βn)=1。計算選擇一個隨機整數 e (0<e<βn)(公開),滿足 gcd(e,βn)=1
- 計算d= e^-1 mod βn(保密)
- 確定: 公鑰KU={ e,n},私鑰KR={d,p,q},或KR={d,n}
- 加密:已知明文:M 計算密文:C=(M^e)mod n
- 解密:已知密文:C 和私鑰 KR={d,n} 計算明文:M=C^d mod n
密鑰生成時,如果要求 n 很大,攻擊者要將其成功地分解爲 p乘以q是困難的,這就是著名的大整數因子分解性問題,這保證了攻擊者不能得出 βn=(p-1)*(q-1),因此即使知道公鑰{ e,n},也不能通過解密算法將私鑰{d,n}推導出來。 --來自《應用密碼學》
算法實現:
Main類:
package RSAtest;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
public class Main {
public static void main(String[] args) throws Exception {
HashMap<String, Object> map=RSAUtils.getKeys();
RSAPublicKey publicKey=(RSAPublicKey)map.get("public");
RSAPrivateKey privateKey=(RSAPrivateKey) map.get("private");
//得到公開的N(PS:N=p*q,p和q是保密的
String modulus=publicKey.getModulus().toString();
//得到公開的E(PS:E是公鑰中中公開的隨機整數
String public_exponent=publicKey.getPublicExponent().toString();
//得到私鑰D
String private_exponent=privateKey.getPrivateExponent().toString();
String ming="128";
RSAPublicKey pubKey=RSAUtils.getPublicKey(modulus, public_exponent);
RSAPrivateKey priKey=RSAUtils.getPrivateKey(modulus, private_exponent);
String mi=RSAUtils.encrytByPublicKey(ming, pubKey);
System.out.println(mi);
ming=RSAUtils.decryptByPrivateKey(mi, priKey);
System.err.println(ming);
}
}
RSAUtils類;
package RSAtest;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import javax.crypto.Cipher;
public class RSAUtils {
public static HashMap<String,Object>getKeys() throws NoSuchAlgorithmException{
HashMap<String,Object>map=new HashMap<String,Object>();
//調用產生RSA算法
KeyPairGenerator KeyPairGen=KeyPairGenerator.getInstance("RSA");
//設置產生RSA算法的密鑰長度
KeyPairGen.initialize(Integer.parseInt("1024"));
//生成密鑰對
KeyPair keyPair=KeyPairGen.generateKeyPair();
//獲取公鑰
RSAPublicKey publicKey=(RSAPublicKey) keyPair.getPublic();
//獲取私鑰
RSAPrivateKey privateKey=(RSAPrivateKey) keyPair.getPrivate();
map.put("public", publicKey);
map.put("private", privateKey);
return map;
}
public static RSAPublicKey getPublicKey(String modulus,String exponent) throws Exception{
BigInteger b1=new BigInteger(modulus);
BigInteger b2=new BigInteger(exponent);
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
RSAPublicKeySpec keySpec=new RSAPublicKeySpec(b1, b2);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
}
public static RSAPrivateKey getPrivateKey(String modulus,String exponent) throws Exception{
BigInteger b1=new BigInteger(modulus);
BigInteger b2=new BigInteger(exponent);
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
RSAPrivateKeySpec keySpec=new RSAPrivateKeySpec(b1, b2);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
public static String encrytByPublicKey(String data,RSAPublicKey publicKey) throws Exception{
Cipher cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
int key_len=publicKey.getModulus().bitLength();
String []datas =splitString(data,key_len-11);
String mi="";
for(String s:datas) {
mi+=bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
}
public static String decryptByPrivateKey(String data,RSAPrivateKey privateKey) throws Exception{
Cipher cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
int key_len=privateKey.getModulus().bitLength();
byte []bytes=data.getBytes();
byte []bcd=ASCII_TO_BCD(bytes,bytes.length);
System.err.println(bcd.length);
String ming="";
byte[][] arrays =splitArray(bcd,key_len);
for(byte[]arr:arrays) {
ming+=new String(cipher.doFinal(arr));
}
return ming;
}
public static byte[] ASCII_TO_BCD(byte[] ascii,int asc_len) {
byte[] bcd=new byte[asc_len/2];
int j=0;
for(int i=0;i<(asc_len+1)/2;i++) {
bcd[i]=asc_to_bcd(ascii[j++]);
bcd[i]=(byte)(((j>=asc_len)?0x00:asc_to_bcd(ascii[j++]))+(bcd[i]<<4));
}
return bcd;
}
public static byte asc_to_bcd(byte asc) {
byte bcd;
if((asc>='0')&&(asc<='9'))
bcd=(byte)(asc-'0');
else if((asc>='A')&&(asc<='F'))
bcd=(byte)(asc-'A'+10);
else if((asc>='a')&&(asc<='f'))
bcd=(byte)(asc-'a'+10);
else
bcd=(byte)(asc-48);
return bcd;
}
public static String bcd2Str(byte[] bytes) {
char temp[]=new char[bytes.length*2],val;
for(int i=0;i<bytes.length;i++) {
val=(char)(((bytes[i]&0xf0)>>4)&0xf0);
temp[i*2]=(char)(val>9?val+'A'-10:val+'0');
val=(char)(bytes[i]&0x0f);
temp[i*2+1]=(char)(val>9?val+'A'-10:val+'0');
}
return new String(temp);
}
public static String[] splitString(String string,int len) {
int x=string.length()/len;
int y=string.length()%len;
int z=0;
if(y!=0) {
z=1;
}
String[] strings=new String[x+z];
String str="";
for(int i=0;i<x+z;i++) {
if(i==x+z-1&&y!=0) {
str=string.substring(i*len, i*len+y);
}else {
str=string.substring(i*len,i*len+len);
}
strings[i]=str;
}
return strings;
}
public static byte[][] splitArray(byte[] data,int len){
int x=data.length/len;
int y=data.length%len;
int z=0;
if(y!=0) {
z=1;
}
byte[][] arrays=new byte[x+z][];
byte[] arr;
for(int i=0;i<x+z;i++) {
arr=new byte[len];
if(i==x+z-1&&y!=0) {
System.arraycopy(data, i*len, arr, 0, y);
}else {
System.arraycopy(data, i*len, arr, 0, len);
}
arrays[i]=arr;
}
return arrays;
}
}
代碼參考來源:https://blog.csdn.net/centralperk/article/details/8558678
運行結果演示:
代碼運行的問題在於不管怎麼修改明文,解密後的明文輸出都是128,這是爲什麼呢?