转载请注明出处: 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,打开之后是一个命令行窗口:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI5AjMzMDMyMTMzIDOwQTMwIzLcRXZu5ibkN3Yuc2bsJmLn1Wavw1LcpDc0RHaiojIsJye.jpg)
然后通过如下命令生成私钥:
[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