轉載請注明出處: http://blog.csdn.net/bbld_/article/details/38777491
概述
rsa是目前最有影響力的公鑰加密算法,該算法基于一個十分簡單的數論事實:将兩個大素數相乘十分容易,但那時想要對其乘積進行因式分解卻極其困 難,是以可以将乘積公開作為加密密鑰,即公鑰,而兩個大素數組合成私鑰。公鑰是可釋出的供任何人使用,私鑰則為自己所有,供解密之用。關于rsa其它需要了解的知識,參考維基百科:http://zh.wikipedia.org/zh-cn/rsa%e5%8a%a0%e5%af%86%e6%bc%94%e7%ae%97%e6%b3%95
在項目開發中對于一些比較敏感的資訊需要對其進行加密處理,我們就可以使用rsa這種非對稱加密算法來對資料進行加密處理。
使用
秘鑰對的生成
1、我們可以在代碼裡随機生成密鑰對
[java] view
plaincopy
/**
* 随機生成rsa密鑰對
*
* @param keylength
* 密鑰長度,範圍:512~2048<br>
* 一般1024
* @return
*/
public static keypair generatersakeypair(int keylength)
{
try
{
keypairgenerator kpg = keypairgenerator.getinstance(rsa);
kpg.initialize(keylength);
return kpg.genkeypair();
} catch (nosuchalgorithmexception e)
e.printstacktrace();
return null;
}
}
通過這方法我們得到keypair後就可以調用 keypair.getprivate() 和 pair.getpublic()得到 私鑰 和 公鑰了。不過實際我們一般是使用2的方法把生成的密鑰對儲存起來,密鑰自己儲存,比如服務端,把公鑰給用戶端(android)用于加密需要發送的資料,最後服務端根據私鑰吧用戶端加密的資訊解密再處理。
2、通過openssl工具生成密鑰對
openssl工具下載下傳:openssl工具 (64位的也可使用)使用openssl工具生成密鑰對的過程如下:
首先輕按兩下打開bin檔案夾下的openssl.exe,打開之後是一個指令行視窗:
然後通過如下指令生成私鑰:
[plain] view
genrsa -out rsa_private_key.pem 1024
這條指令是讓openssl随機生成了一份私鑰,加密長度是1024位, 密鑰長度,範圍:512~2048。執行完指令後就可在bin檔案夾下看到rsa_private_key.pem檔案了。
用文本類工具打開可看到裡面的内容:
-----begin rsa private key-----
miicxqibaakbgqcfrtdcpih10gt9f31rquiinlwe7fl2dtej93gtmje9c2h+klve
nwgecijvq5sonqnfwtomkdo0b3olf4pgbkelthraz/l3nyjylbqjhc3jtjunzc0l
uumpxgsox62+pusgblfb8zjo6hix4gv/vhyqvcpg9ayqge7zytrzyx9byqidaqab
aogao9+syrtkc9xjdfcocfmxv+ut/1ic6edgcqu6uzwq+jvwod9klxqyqjqcr6t7
pjfodc3razotx4gczjverbvz053rh5gawcdocegaqbxaawjoha+9ieu0nuud7ckf
ydko0qxlogp9tanrmet5zmqt8qxdyl6xcij3mk8rivogbjecqqdnto6dzx8xcozc
ne0gzc53gv/kqxanbbhmr7wkkub2i5+txkej5z3abx2ppeqxdr4agjh8gtbm6k7t
ehv4ov4fakeaxppd/iit1/svqq20be8csihpsjtpiestwqwdm1qn/y2nakgkpcfp
yeduvvdptqhrn9eqnggnanwg5kmvsuwn9qjafhbhqe4/hk5kyz+0l+iruw6afoxn
ktaio3ttuk98x/yjsoastaacmecgli9vrjqdwfiwjcvwlu38mz0cvx8usqjbalzt
m5er+lipkw5rqcd0jzrfpnkqu/3xgyque4gv5pshlccvwxebcafcc3hez9jfpypi
dk2ocvg6lphfkbkfbaecqqcodckx6dbwiyvxmpajoocf63kpcydpkjeoviuhro1x
elr2grqcc/9q4c4vruohbq+vx8nmpno6nby5tlgdwmyc
-----end rsa private key-----
這裡面的内容是标準的ascii字元,中間的一大串字元就是私鑰資料了。
然後通過如下指令生成公鑰:
rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
打開檔案看下裡面的内容:
-----begin public key-----
migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqcfrtdcpih10gt9f31rquiinlwe
7fl2dtej93gtmje9c2h+klvenwgecijvq5sonqnfwtomkdo0b3olf4pgbkelthra
z/l3nyjylbqjhc3jtjunzc0luumpxgsox62+pusgblfb8zjo6hix4gv/vhyqvcpg
9ayqge7zytrzyx9byqidaqab
-----end public key-----
可以看到是跟私鑰的檔案類似的。
這樣密鑰就基本生成了,不過這樣密鑰對的私鑰是無法在代碼中直接使用的,要想使用它需要借助rsaprivatekeystructure這個類,java是不自帶的。是以為了友善使用,我們需要對私鑰進行pkcs#8編碼,指令如下:
pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt
這條指令的結果依然是在bin檔案夾生成了pkcs8_rsa_private_key.pem檔案,打開内容如下:
-----begin private key-----
miicdwibadanbgkqhkig9w0baqefaascamewggjdageaaogbaj9fn1w8gfxsbp1/
fwtc4gicvb7t+xz20qn3eboamt1zyf6qtuq1aaqkilvdmyida1/bogwp07rvc6v/
imaep4togtrp8vedglivuqmclenonsdlzsw66alcayjhrb4+5iygv9vzmk7qglhg
zx++hjbukkb1piqatvpjnflhf1vjagmbaaecgya736xhg0ol3ekn9yhx8zg/5rp/
wjzoqobyq7ptpcr4m/ch30qverjamokvppumn+h1zdebk5phiajkm96sg/ptndef
kzraj2hwsbqptcabyk6ed70grtq1s53tyqxiosjrbcugy/21qesws3nmyq3xdepk
xpdykpeatyuk86aekqjbam1m7p1lfzekjnw17sdmlnca/8pbca0eecyvtaqprval
n61eqqnnpdpvhamkrbcovgcakfwa1uboru0qdxii/gucqqdgmkp+kjpx9jvcrbrt
7wkyiemynm+j6y1zbz2bvcf9jaccqaskiwnir1s9um+1cfe30so2ca0cfcdmqy+y
7a31akb8cgfb7j+gtkrlp7sx6ktrboau7e0q1oijdo24r3xf/imw4cy0aaix4kau
l29gop1ywjykjxcvtfyznrxxhxsxakeavo0zksv4ui8rdmtaipqllf8+erbt/ded
jbr7ga/k+wctwk/bd4fxp9xzeetp0l8/i+iotagk+dos8d8ogqufoqjbai4nwpfo
mfaljxgy9ok45wxrcqkjgm+sn6i8hqeujxesvhyatail/1dgli+u46efd69fw0w+
c7o0hllmsypazjw=
-----end private key-----
可以看到中間的私鑰内容有所變化了,這樣的私鑰我們在代碼裡就友善使用了。
以上的密鑰檔案使用時需要注意吧頭和尾的字元串去掉,我們隻取中間的内容。
代碼中的使用
首先我們需要封裝寫個rsa的工具類,友善加密解密的操作。
package com.example.rsa;
import java.io.bufferedreader;
import java.io.ioexception;
import java.io.inputstream;
import java.io.inputstreamreader;
import java.math.biginteger;
import java.security.keyfactory;
import java.security.keypair;
import java.security.keypairgenerator;
import java.security.nosuchalgorithmexception;
import java.security.privatekey;
import java.security.publickey;
import java.security.interfaces.rsaprivatekey;
import java.security.interfaces.rsapublickey;
import java.security.spec.invalidkeyspecexception;
import java.security.spec.pkcs8encodedkeyspec;
import java.security.spec.rsapublickeyspec;
import java.security.spec.x509encodedkeyspec;
import javax.crypto.cipher;
* @author mr.zheng
* @date 2014年8月22日 下午1:44:23
public final class rsautils
private static string rsa = "rsa";
/**
* 随機生成rsa密鑰對(預設密鑰長度為1024)
*
* @return
*/
public static keypair generatersakeypair()
return generatersakeypair(1024);
* 随機生成rsa密鑰對
* @param keylength
* 密鑰長度,範圍:512~2048<br>
* 一般1024
public static keypair generatersakeypair(int keylength)
try
{
keypairgenerator kpg = keypairgenerator.getinstance(rsa);
kpg.initialize(keylength);
return kpg.genkeypair();
} catch (nosuchalgorithmexception e)
e.printstacktrace();
return null;
}
* 用公鑰加密 <br>
* 每次加密的位元組數,不能超過密鑰的長度值減去11
* @param data
* 需加密資料的byte資料
* @param pubkey
* 公鑰
* @return 加密後的byte型資料
public static byte[] encryptdata(byte[] data, publickey publickey)
cipher cipher = cipher.getinstance(rsa);
// 編碼前設定編碼方式及密鑰
cipher.init(cipher.encrypt_mode, publickey);
// 傳入編碼資料并傳回編碼結果
return cipher.dofinal(data);
} catch (exception e)
* 用私鑰解密
* @param encrypteddata
* 經過encrypteddata()加密傳回的byte資料
* @param privatekey
* 私鑰
public static byte[] decryptdata(byte[] encrypteddata, privatekey privatekey)
cipher.init(cipher.decrypt_mode, privatekey);
return cipher.dofinal(encrypteddata);
* 通過公鑰byte[](publickey.getencoded())将公鑰還原,适用于rsa算法
* @param keybytes
* @throws nosuchalgorithmexception
* @throws invalidkeyspecexception
public static publickey getpublickey(byte[] keybytes) throws nosuchalgorithmexception,
invalidkeyspecexception
x509encodedkeyspec keyspec = new x509encodedkeyspec(keybytes);
keyfactory keyfactory = keyfactory.getinstance(rsa);
publickey publickey = keyfactory.generatepublic(keyspec);
return publickey;
* 通過私鑰byte[]将公鑰還原,适用于rsa算法
public static privatekey getprivatekey(byte[] keybytes) throws nosuchalgorithmexception,
pkcs8encodedkeyspec keyspec = new pkcs8encodedkeyspec(keybytes);
privatekey privatekey = keyfactory.generateprivate(keyspec);
return privatekey;
* 使用n、e值還原公鑰
* @param modulus
* @param publicexponent
public static publickey getpublickey(string modulus, string publicexponent)
throws nosuchalgorithmexception, invalidkeyspecexception
biginteger bigintmodulus = new biginteger(modulus);
biginteger bigintprivateexponent = new biginteger(publicexponent);
rsapublickeyspec keyspec = new rsapublickeyspec(bigintmodulus, bigintprivateexponent);
* 使用n、d值還原私鑰
* @param privateexponent
public static privatekey getprivatekey(string modulus, string privateexponent)
biginteger bigintprivateexponent = new biginteger(privateexponent);
* 從字元串中加載公鑰
* @param publickeystr
* 公鑰資料字元串
* @throws exception
* 加載公鑰時産生的異常
public static publickey loadpublickey(string publickeystr) throws exception
byte[] buffer = base64utils.decode(publickeystr);
keyfactory keyfactory = keyfactory.getinstance(rsa);
x509encodedkeyspec keyspec = new x509encodedkeyspec(buffer);
return (rsapublickey) keyfactory.generatepublic(keyspec);
throw new exception("無此算法");
} catch (invalidkeyspecexception e)
throw new exception("公鑰非法");
} catch (nullpointerexception e)
throw new exception("公鑰資料為空");
* 從字元串中加載私鑰<br>
* 加載時使用的是pkcs8encodedkeyspec(pkcs#8編碼的key指令)。
* @param privatekeystr
public static privatekey loadprivatekey(string privatekeystr) throws exception
byte[] buffer = base64utils.decode(privatekeystr);
// x509encodedkeyspec keyspec = new x509encodedkeyspec(buffer);
pkcs8encodedkeyspec keyspec = new pkcs8encodedkeyspec(buffer);
return (rsaprivatekey) keyfactory.generateprivate(keyspec);
throw new exception("私鑰非法");
throw new exception("私鑰資料為空");
* 從檔案中輸入流中加載公鑰
* @param in
* 公鑰輸入流
public static publickey loadpublickey(inputstream in) throws exception
return loadpublickey(readkey(in));
} catch (ioexception e)
throw new exception("公鑰資料流讀取錯誤");
throw new exception("公鑰輸入流為空");
* 從檔案中加載私鑰
* @param keyfilename
* 私鑰檔案名
* @return 是否成功
public static privatekey loadprivatekey(inputstream in) throws exception
return loadprivatekey(readkey(in));
throw new exception("私鑰資料讀取錯誤");
throw new exception("私鑰輸入流為空");
* 讀取密鑰資訊
* @throws ioexception
private static string readkey(inputstream in) throws ioexception
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');
}
return sb.tostring();
* 列印公鑰資訊
* @param publickey
public static void printpublickeyinfo(publickey publickey)
rsapublickey rsapublickey = (rsapublickey) publickey;
system.out.println("----------rsapublickey----------");
system.out.println("modulus.length=" + rsapublickey.getmodulus().bitlength());
system.out.println("modulus=" + rsapublickey.getmodulus().tostring());
system.out.println("publicexponent.length=" + rsapublickey.getpublicexponent().bitlength());
system.out.println("publicexponent=" + rsapublickey.getpublicexponent().tostring());
public static void printprivatekeyinfo(privatekey privatekey)
rsaprivatekey rsaprivatekey = (rsaprivatekey) privatekey;
system.out.println("----------rsaprivatekey ----------");
system.out.println("modulus.length=" + rsaprivatekey.getmodulus().bitlength());
system.out.println("modulus=" + rsaprivatekey.getmodulus().tostring());
system.out.println("privateexponent.length=" + rsaprivatekey.getprivateexponent().bitlength());
system.out.println("privatecexponent=" + rsaprivatekey.getprivateexponent().tostring());
上面需要注意的就是加密是有長度限制的,過長的話會抛異常!!!
代碼中有些需要使用base64再轉換的,而java中不自帶,android中自帶,是以自己寫出一個來,友善java背景使用
import java.io.unsupportedencodingexception;
* @date 2014年8月22日 下午9:50:28
public class base64utils
private static char[] base64encodechars = new char[]
{ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', '+', '/' };
private static byte[] base64decodechars = new byte[]
{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1,
-1, -1, -1 };
* 加密
public static string encode(byte[] data)
stringbuffer sb = new stringbuffer();
int len = data.length;
int i = 0;
int b1, b2, b3;
while (i < len)
b1 = data[i++] & 0xff;
if (i == len)
sb.append(base64encodechars[b1 >>> 2]);
sb.append(base64encodechars[(b1 & 0x3) << 4]);
sb.append("==");
break;
b2 = data[i++] & 0xff;
sb.append(base64encodechars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64encodechars[(b2 & 0x0f) << 2]);
sb.append("=");
b3 = data[i++] & 0xff;
sb.append(base64encodechars[b1 >>> 2]);
sb.append(base64encodechars[((b1 & 0x03) << 4) | ((b2 & 0xf0) >>> 4)]);
sb.append(base64encodechars[((b2 & 0x0f) << 2) | ((b3 & 0xc0) >>> 6)]);
sb.append(base64encodechars[b3 & 0x3f]);
* 解密
* @param str
public static byte[] decode(string str)
return decodeprivate(str);
} catch (unsupportedencodingexception e)
return new byte[]
{};
private static byte[] decodeprivate(string str) throws unsupportedencodingexception
byte[] data = null;
data = str.getbytes("us-ascii");
int b1, b2, b3, b4;
do
b1 = base64decodechars[data[i++]];
} while (i < len && b1 == -1);
if (b1 == -1)
b2 = base64decodechars[data[i++]];
} while (i < len && b2 == -1);
if (b2 == -1)
sb.append((char) ((b1 << 2) | ((b2 & 0x30) >>> 4)));
b3 = data[i++];
if (b3 == 61)
return sb.tostring().getbytes("iso8859-1");
b3 = base64decodechars[b3];
} while (i < len && b3 == -1);
if (b3 == -1)
sb.append((char) (((b2 & 0x0f) << 4) | ((b3 & 0x3c) >>> 2)));
b4 = data[i++];
if (b4 == 61)
b4 = base64decodechars[b4];
} while (i < len && b4 == -1);
if (b4 == -1)
sb.append((char) (((b3 & 0x03) << 6) | b4));
return sb.tostring().getbytes("iso8859-1");
最後就是真正使用它們了:
import android.app.activity;
import android.os.bundle;
import android.util.base64;
import android.view.view;
import android.view.view.onclicklistener;
import android.widget.button;
import android.widget.edittext;
public class mainactivity extends activity implements onclicklistener
private button btn1, btn2;// 加密,解密
private edittext et1, et2, et3;// 需加密的内容,加密後的内容,解密後的内容
/* 密鑰内容 base64 code */
private static string puclic_key = "migfma0gcsqgsib3dqebaquaa4gnadcbiqkbgqcfrtdcpih10gt9f31rquiinlwe"
+ "\r" + "7fl2dtej93gtmje9c2h+klvenwgecijvq5sonqnfwtomkdo0b3olf4pgbkelthra" + "\r"
+ "z/l3nyjylbqjhc3jtjunzc0luumpxgsox62+pusgblfb8zjo6hix4gv/vhyqvcpg" + "\r"
+ "9ayqge7zytrzyx9byqidaqab" + "\r";
private static string private_key = "miicdwibadanbgkqhkig9w0baqefaascamewggjdageaaogbaj9fn1w8gfxsbp1/"
+ "\r" + "fwtc4gicvb7t+xz20qn3eboamt1zyf6qtuq1aaqkilvdmyida1/bogwp07rvc6v/" + "\r"
+ "imaep4togtrp8vedglivuqmclenonsdlzsw66alcayjhrb4+5iygv9vzmk7qglhg" + "\r"
+ "zx++hjbukkb1piqatvpjnflhf1vjagmbaaecgya736xhg0ol3ekn9yhx8zg/5rp/" + "\r"
+ "wjzoqobyq7ptpcr4m/ch30qverjamokvppumn+h1zdebk5phiajkm96sg/ptndef" + "\r"
+ "kzraj2hwsbqptcabyk6ed70grtq1s53tyqxiosjrbcugy/21qesws3nmyq3xdepk" + "\r"
+ "xpdykpeatyuk86aekqjbam1m7p1lfzekjnw17sdmlnca/8pbca0eecyvtaqprval" + "\r"
+ "n61eqqnnpdpvhamkrbcovgcakfwa1uboru0qdxii/gucqqdgmkp+kjpx9jvcrbrt" + "\r"
+ "7wkyiemynm+j6y1zbz2bvcf9jaccqaskiwnir1s9um+1cfe30so2ca0cfcdmqy+y" + "\r"
+ "7a31akb8cgfb7j+gtkrlp7sx6ktrboau7e0q1oijdo24r3xf/imw4cy0aaix4kau" + "\r"
+ "l29gop1ywjykjxcvtfyznrxxhxsxakeavo0zksv4ui8rdmtaipqllf8+erbt/ded" + "\r"
+ "jbr7ga/k+wctwk/bd4fxp9xzeetp0l8/i+iotagk+dos8d8ogqufoqjbai4nwpfo" + "\r"
+ "mfaljxgy9ok45wxrcqkjgm+sn6i8hqeujxesvhyatail/1dgli+u46efd69fw0w+" + "\r" + "c7o0hllmsypazjw="
+ "\r";
@override
protected void oncreate(bundle savedinstancestate)
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
initview();
private void initview()
btn1 = (button) findviewbyid(r.id.btn1);
btn2 = (button) findviewbyid(r.id.btn2);
btn1.setonclicklistener(this);
btn2.setonclicklistener(this);
et1 = (edittext) findviewbyid(r.id.et1);
et2 = (edittext) findviewbyid(r.id.et2);
et3 = (edittext) findviewbyid(r.id.et3);
public void onclick(view v)
switch (v.getid())
// 加密
case r.id.btn1:
string source = et1.gettext().tostring().trim();
try
// 從字元串中得到公鑰
// publickey publickey = rsautils.loadpublickey(puclic_key);
// 從檔案中得到公鑰
inputstream inpublic = getresources().getassets().open("rsa_public_key.pem");
publickey publickey = rsautils.loadpublickey(inpublic);
// 加密
byte[] encryptbyte = rsautils.encryptdata(source.getbytes(), publickey);
// 為了友善觀察吧加密後的資料用base64加密轉一下,要不然看起來是亂碼,是以解密是也是要用base64先轉換
string afterencrypt = base64utils.encode(encryptbyte);
et2.settext(afterencrypt);
} catch (exception e)
e.printstacktrace();
break;
// 解密
case r.id.btn2:
string encryptcontent = et2.gettext().tostring().trim();
// 從字元串中得到私鑰
// privatekey privatekey = rsautils.loadprivatekey(private_key);
// 從檔案中得到私鑰
inputstream inprivate = getresources().getassets().open("pkcs8_rsa_private_key.pem");
privatekey privatekey = rsautils.loadprivatekey(inprivate);
// 因為rsa加密後的内容經base64再加密轉換了一下,是以先base64解密回來再給rsa解密
byte[] decryptbyte = rsautils.decryptdata(base64utils.decode(encryptcontent), privatekey);
string decryptstr = new string(decryptbyte);
et3.settext(decryptstr);
default:
我把密鑰放到assest資源檔案夾裡了,也可以直接使用字元串得到,上面注釋掉了。
最後我們來看下效果吧:
源碼下載下傳位址:http://download.csdn.net/detail/bbld_/7806673