David.Turing's blog

 

RSA使用简述

RSA协议我不再描述,大家可以看http://www.di-mgt.com.au/rsa_alg.html
RSA的密钥对生成时间依赖于两个因素,
第一,密钥的长度
第二,素数的筛选质量

在整个密钥对生成过程中,RSA会随机选择两个大素数,事实上,计算机的聪明
程度还不足以判断某个随机选择的大素数是否真的不可分解,因此,你只能够通过
计算机程序来尽量将这个大随机数不是素数的几率降到某个界限值(如0.0001)以下。

RSA KeyPair分为公钥和私钥,你应该这样使用KeyPair:
1,你使用私钥来签名,别人用你的公钥来验证签名
2,别人用你的公钥加密信息M->M',你用私钥来解密信息M'->M

虽然RSA经受过多年深入的密码分析,但大家在使用RSA的时候还是要注意以下事项,
否则RSA的安全性会大打折扣:

1,合理的密钥长度(setKeyLength)
RSA1024至今是安全的,按照目前密码分析和计算机硬件条件的发展,估计在未来5-10年,
仍以难以破解。

2,素数确定性选择(setCertaintyOfPrime)
实际应用中,选择100就行了。

3,选择合理的padding(setRSAMode)
RSA有三种模式,RAW, PKCS和OAEP,日常应用中,我本人只使用PKCS(PKCS#1 v1.5)
和OAEP(PKCS#1 v2.0)这两种padding模式。
padding跟安全性其实是紧密挂钩的,有兴趣的朋友可以看看PKCS#1标准讨论。


我编写了一个RSAUtils的工具类,下面的该类的测试代码的一部分。

程序如下:
  RSAUtils utils =new RSAUtils();
  utils.setKeyLength(1024);
  utils.setCertaintyOfPrime(100);
  utils.setRSAMode(PKCS_RSA_MODE);   //RAW =1  PKCS=2  OAEP=3
  utils.initRSAKeyPair();
  
  //查看公钥
  RSAKeyParameters mypubkey=utils.getPublicKey();
  BigInteger mypubkey_modulus=mypubkey.getModulus();  
  BigInteger mypubkey_exponent=mypubkey.getExponent();
  System.out.println("##mypubkey的modulus长度="+mypubkey_modulus.bitLength());
  System.out.println("##mypubkey_modulus值="+mypubkey_modulus.toString());
  System.out.println("##mypubkey的exponent长度="+mypubkey.getExponent().bitLength());
  System.out.println("##mypubkey_exponent值="+mypubkey_exponent.toString());

  //查看私钥
  RSAKeyParameters myprivkey=utils.getPrivateKey();
  BigInteger myprivkey_modulus=myprivkey.getModulus();
  System.out.println("##myprivkey的modulus长度="+myprivkey_modulus.bitLength());
  System.out.println("##myprivkey的modulus值="+myprivkey_modulus.toString());
  System.out.println("##myprivkey.getExponent()长度="+myprivkey.getExponent().bitLength());
  System.out.println("##myprivkey.getExponent()值="+myprivkey.getExponent());

以下是输出:
##mypubkey的modulus长度=1024
##mypubkey_modulus值=93806062666699782638132820491933031482836826566660997927543724649365705443512121003172409185855121369631538039111403612211728268332662414248776212969019881724066055080327735965218365399595323200109436472147258110417469825748181131149217613806780318374365617984326523029965066348377550281908277056378455106547
##mypubkey的exponent长度=2
##mypubkey_exponent值=3

##myprivkey的modulus长度=1024
##myprivkey的modulus值=93806062666699782638132820491933031482836826566660997927543724649365705443512121003172409185855121369631538039111403612211728268332662414248776212969019881724066055080327735965218365399595323200109436472147258110417469825748181131149217613806780318374365617984326523029965066348377550281908277056378455106547
##myprivkey.getExponent()长度=1023
##myprivkey.getExponent()值=62537375111133188425421880327955354321891217711107331951695816432910470295674747335448272790570080913087692026074269074807818845555108276165850808646013241363962278455328383552959397735977285649455021534046301135296075808377308404258909132811288204167107604525033796313576612747649866739561523887875979483707

其中,要记住,公钥的exponent即RSA算法中的e, e通常是3,17和65537
X.509建议使用65537,PEM建议使用3,PKCS#1建议使用3或65537,一般来说,都是选择3。

私钥的Exponent就是私钥中最重要的部分,它就是私钥区别于公钥的地方!

接着,我们看看RSA的加密,解密过程。

通常,不要随便对某一个别人发过来的东西进行签名(有潜在危险),即使有这样的必要,请先将它的文件进行Digest或者HMAC
处理后,再做签名。
为了说明RSA是如何加密信息的,我先让大家脱离MD5/SHA1等辅助算法(没有人会单独使用RSA,RSAwithMD5,RSAwithSHA1才是常用的使用方法),来单独看看RSA本身:

大家习惯了DES/IDEA,再看RSA的加密,可能会有一些不习惯,因为RSA虽然也可以看成是基于Block的加密,但是,RSA的输入和输出的Block的大小是不一样的,Block的大小依赖于你所使用的RSA Key的长度和RSA的padding模式。
在RSAUtils测试用例中,分别对RSA设置三种长度的Key(768,1024,2048)和2种padding模式(PKCS 1.5和OAEP),结果如下:

RSA                InBlock大小   OutBlock大小  (单位,字节)
768bit/PKCS        85                96
1024bit/PKCS     117               128
2048bit/PKCS     245               256
768bit/OAEP        54                96
1024bit/OAEP     86               128
2048bit/OAEP     214               256

大家可以看到,相同密钥长度, 加密出来的密文长度要比明文要长,且OAEP的InBlock/OutBlock要比PKCS的InBlock/OutBlock要小,单从熵的角度,意味着OAEP padding模式引入更多的熵,OAEP要比PKCS更安全(事实上,为何提出OAEP代替PKCS,大家可以到RSA网站看看OAEP文档 http://www.rsasecurity.com/rsalabs/node.asp?id=2125)。


下面,RSAUtils是我写的针对BouncyCastle的一个工具类,它封装了BouncyCastle的crypto中的RSAEngine,基本上,我很少单独使用RSAUtils,我更多的是结合DiegestUtils来使用。

posted on 2006-01-11 11:24 david.turing 阅读(10453) 评论(4)  编辑  收藏 所属分类: Security领域

评论

# re: RSA使用简述 2006-09-28 13:22 aa

hello  回复  更多评论   

# re: RSA使用简述 2007-11-02 18:33 robie

大哥,能不能详细解释下PKCS#1 v1.5的签名算法padding过程,我现在做了SHA1,得出结果为:ADABC0DC670987EF47B557FB1554A32DDD5B545E,然后还要padding别的东西在前面吗?还是直接如下填充:
0001ffff......ffff00ADABC0DC670987EF47B557FB1554A32DDD5B545E????
先谢谢大哥了!!!  回复  更多评论   

# re: RSA使用简述 2007-11-02 18:37 robie

我的邮箱:robie◎126.com  回复  更多评论   

# re: RSA使用简述 2007-11-04 09:46 david.turing

如果你使用了SHA1并对散列值进行RSA签名、加密,则Padding的过程无需你本人去干预。
或者你可以看看下面的代码(RSAUtils.java):

package org.dev2dev.security.crypto.Asymmetric;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;

import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.encodings.OAEPEncoding;
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
import org.bouncycastle.crypto.engines.DESEngine;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.util.encoders.Hex;
import org.dev2dev.security.crypto.blockcipher.BlockCipherTool;


/**
* RSA工具类
* @author david.turing
* @copyright GuangZhou BEA Usergroup
* @version 0.7
* @modifyTime 22:30:34
*/
public class RSAUtils {

int keylength=1024;
int certainty=20;
RSAKeyGenerationParameters keyparam;
AsymmetricBlockCipher eng = null;
RSAKeyPairGenerator pGen = null;
AsymmetricCipherKeyPair pair = null;

public RSAUtils()
{

}

public String getName()
{
return "RSA";
}

/**
* 设置RSA的密钥长度
* @param rsakeylength
*/
public void setKeyLength(int rsakeylength)
{
if(rsakeylength==512||rsakeylength==768||rsakeylength==1024||rsakeylength==2048)
keylength=rsakeylength;
}

/**
* 设置RSA Key Pair的素数产生经度,该数值越大,理论产生的RSA Key安全性越高
* @param certaintyofprime
*/
public void setCertaintyOfPrime(int certaintyofprime)
{
certainty=certaintyofprime;
}

/**
* 生成RSA Keypair,如果你不是通过ImportPublicKey和ImportPrivateKey
* 来导入密钥对,则可通过此方法随机生成。
*
* @return RSAKeyGenerationParameters
*/
public void initRSAKeyPair()
{
/**
* 注意, 第一个参数被写死了,它是publicExponent, 即e
* e通常选3,17,65537
* X.509建议使用65537
* PEM建议使用3
* PKCS#1建议使用3或65537
* 在本算法中,它使用了3,即0x3
*/
RSAKeyGenerationParameters rsaparam=
new RSAKeyGenerationParameters(BigInteger.valueOf(0x3),
new SecureRandom(), this.keylength, this.certainty);
this.keyparam = rsaparam;
//RSA Keypair的生成依赖于rsaparam
RSAKeyPairGenerator pGen = new RSAKeyPairGenerator();
pGen.init(keyparam);
pair = pGen.generateKeyPair();
pair.getPublic();
}

/**
* 设置RSA密钥对,此方法用于从外部文件导入RSA密钥对
* @see ImportPublicKey(String)
* @see ImportPrivateKey(String)
* @param pubparam 公钥
* @param privparam 私钥
*/
public void setRSAKeyPair(RSAKeyParameters pubparam, RSAPrivateCrtKeyParameters privparam)
{
AsymmetricCipherKeyPair newpair=new AsymmetricCipherKeyPair(pubparam,privparam);
pair=newpair;

}

/**
* 该函数返回公钥
* @return
*/
public RSAKeyParameters getPublicKey()
{
return (RSAKeyParameters)pair.getPublic();
}

/**
* 该函数返回私钥
* 注意,RSAPrivateCrtKeyParameters其实继承了RSAKeyParameters
* @see getPublicKey()
* @return
*/
public RSAPrivateCrtKeyParameters getPrivateKey()
{
return (RSAPrivateCrtKeyParameters)pair.getPrivate();
}

/**
* RSA的padding模式,安全性依次递增
* mode=1 RAW RSA 安全性最差
* mode=2 PKCS1
* mode=3 OAEP
* @param mode
*/
public void setRSAMode(int mode)
{
eng = new RSAEngine(); //默认就是RAW模式, 安全性问题,已不再使用
if (mode==2)
eng = new PKCS1Encoding(eng);
else
eng = new OAEPEncoding(eng); //mode==3
}

/**
* 该RSAEngine的每次处理输入数据,以Block为单位,是32Bytes
* 因此,本函数的输入要严格控制在32Byte,即256bit数据
* 超出该长度会抛出异常。
* 本函数要求输入必须为16进制字符0-F。
*
* @see Decrypt(String input)
* @param input
* @return
*/
public String encrypt(String input)
{

byte[] inputdata=Hex.decode(input);

//用公钥加密
eng.init(true, pair.getPublic());

System.out.println(">>>加密参数");
System.out.println(">>>明文字节数:"+inputdata.length);
System.out.println(">>>RSA Engine Input Block Size="+this.eng.getInputBlockSize());
System.out.println(">>>RSA Engine Output Block Size="+this.eng.getOutputBlockSize());

try
{
inputdata = eng.processBlock(inputdata, 0, inputdata.length);
}
catch (Exception e)
{
e.printStackTrace();
}

return new String(Hex.encode(inputdata));
}

/**
* 该函数输入为字节,并规定其长度为32字节
* 超出该长度会抛出异常
* @see Decrypt(byte[] inputdata)
* @param inputdata
* @return
*/
public byte[] encrypt(byte[] inputdata)
{
byte[] outputdata=null;

//用公钥加密
eng.init(true, pair.getPublic());

try
{
inputdata = eng.processBlock(inputdata, 0, inputdata.length);
outputdata=new byte[eng.getOutputBlockSize()];
outputdata=inputdata;
}
catch (Exception e)
{
e.printStackTrace();
}

return outputdata;
}


/**
* 加密ByteArrayInputStream流,在该方法中,会对input做分段处理,每段都将会
* 以inBlockSize来划分明文,密文同样会设置换行,因此,将来在加密过程中,密文
* 需要分行读入,在明文、密文,唯一对应的是inBlock和outBlock,它们是换行对应的。
*
* 本函数已经处理结尾Block问题
* @param input
* @param Key
* @return
*/
public byte[] encryptPro(byte[] inputload)
{
ByteArrayInputStream inputstream=new ByteArrayInputStream(inputload);
ByteArrayOutputStream outputstream=new ByteArrayOutputStream();


//用公钥加密
eng.init(true, pair.getPublic());

int inBlockSize =this.eng.getInputBlockSize() ;
int outBlockSize = this.eng.getOutputBlockSize();

try {
System.out.println("加密的 InBlockSize="+inBlockSize);
System.out.println("加密的outBlockSize="+outBlockSize);

encryptPro(inputstream, outputstream);

} catch (DataLengthException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch(Exception e){
e.printStackTrace();
}

byte[] outputload=outputstream.toByteArray();


System.out.println("###[1]明文大小:"+inputload.length);
System.out.println("###[1]密文大小:"+outputload.length);

return outputload;
}

/**
* 加密BufferedInputStream流,在该方法中,会对input做分段处理,每段都将会
* 以inBlockSize来划分明文,密文同样会设置换行,因此,将来在加密过程中,密文
* 需要分行读入,在明文、密文,唯一对应的是inBlock和outBlock,它们是换行对应的。
*
*此函数未处理padding,如果你不想自己调整padding,请用encryptPro
*@see encryptPro(BufferedInputStream inputstream,BufferedOutputStream outputstream)
*
* @param input
* @param Key
* @return
*/
public void encrypt(BufferedInputStream inputstream,BufferedOutputStream outputstream)
{
//用公钥加密
eng.init(true, pair.getPublic());

int inBlockSize =this.eng.getInputBlockSize() ;
int outBlockSize = this.eng.getOutputBlockSize();

byte[] inblock = new byte[inBlockSize];
byte[] outblock = new byte[outBlockSize];

byte[] rv = null;
try {
while (inputstream.read(inblock, 0, inBlockSize) > 0)
{
outblock = eng.processBlock(inblock, 0, inBlockSize);
rv = Hex.encode(outblock, 0, outBlockSize);
outputstream.write(rv, 0, rv.length);
outputstream.write('\n');

}

} catch (DataLengthException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch(Exception e){
e.printStackTrace();
}

}


/**
* 加密BufferedInputStream流,在该方法中,会对input做分段处理,每段都将会
* 以inBlockSize来划分明文,密文同样会设置换行,因此,将来在加密过程中,密文
* 需要分行读入,在明文、密文,唯一对应的是inBlock和outBlock,它们是换行对应的。
*
* 该函数会在加密文件的头部注明padding_size
* padding_size即padding_size即明文流按照inBlockSize划分后
* 最后一个block的未占满的大小(字节数)
*
* @param input
* @param Key
* @return
*/
public void encryptPro(InputStream inputstream,OutputStream outputstream)
{
//用公钥加密
eng.init(true, pair.getPublic());

int inBlockSize =this.eng.getInputBlockSize() ;
int outBlockSize = this.eng.getOutputBlockSize();

byte[] inblock = new byte[inBlockSize];
byte[] outblock = new byte[outBlockSize];


byte[] rv = null;

try {

//System.out.println("stream length="+inputstream.available());
int padding_size=inBlockSize-(inputstream.available() % inBlockSize);
//System.out.println("padding_size="+padding_size);

/*写入padding_size,处理最后一个Block不够inBlockSize的情况
* 记住不要自己修改inBlockSize,因为RSA共有三种模式,每种模式
* 对Padding的处理方式都不一样,所以,不要修改对processBlock
* 的加密解密方式。总之,不要修改RSA的inBlock!
*/
outputstream.write((padding_size+"").getBytes());
outputstream.write('\n');

while (inputstream.read(inblock, 0, inBlockSize) > 0)
{
outblock = eng.processBlock(inblock, 0, inBlockSize);
rv = Hex.encode(outblock, 0, outBlockSize);

//System.out.println("Hex len="+rv.length);
outputstream.write(rv, 0, rv.length);

outputstream.write('\n');
}

} catch (DataLengthException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch(Exception e){
e.printStackTrace();
}

}

/**
* 解密字符串
* @param input
* @return
*/
public String decrypt(String input)
{

byte[] inputdata=Hex.decode(input);

eng.init(false,pair.getPrivate());
System.out.println(">>>加密参数");
System.out.println(">>>RSA Engine Input Block Size="+this.eng.getInputBlockSize());
System.out.println(">>>RSA Engine Output Block Size="+this.eng.getOutputBlockSize());

try
{
inputdata=eng.processBlock(inputdata,0,inputdata.length);
}
catch(Exception e)
{
e.printStackTrace();
}

return new String(Hex.encode(inputdata));

}

/**
* 解密字节数组
* @param inputdata
* @return
*/
public byte[] decrypt(byte[] inputdata)
{
byte[] outputdata=null;

//用公钥加密
eng.init(false, pair.getPrivate());

try
{
inputdata = eng.processBlock(inputdata, 0, inputdata.length);
outputdata=new byte[eng.getOutputBlockSize()];
outputdata=inputdata;
}
catch (Exception e)
{
e.printStackTrace();
}

return outputdata;
}

/**
* 解密流
*
* 本函数已经处理结尾Block问题
* @see encryptPro(byte[] inputload)
*
* @param inputstream 被加密过的流
* @param outputstream 解密输出的流
*/
public byte[] decryptPro(byte[] inputload)
{

ByteArrayInputStream inputstream=new ByteArrayInputStream(inputload);
ByteArrayOutputStream outputstream=new ByteArrayOutputStream();

//设置引擎
eng.init(false, pair.getPrivate());

int inBlockSize =this.eng.getInputBlockSize() ;
int outBlockSize = this.eng.getOutputBlockSize();

try {
System.out.println("解密的In BlockSize="+inBlockSize);
System.out.println("解密的out BlockSize="+outBlockSize);

this.decryptPro(inputstream, outputstream);
} catch (DataLengthException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}

//System.out.println("解密。。。outputload="+new String(outputload));

//byte[] outputload=new byte[outputstream.size()];
byte[] outputload=outputstream.toByteArray();

System.out.println("###[2]密文大小:"+inputload.length);
System.out.println("###[2]明文大小:"+outputload.length);

return outputload;
}



/**
* 解密RSA流,首先先读取RSA的流的padding_size(第一行)
*
* @param inputstream 被加密过的流
* @param outputstream 解密输出的流
*/
public void decryptPro(InputStream inputstream, OutputStream outputstream)
{
//设置引擎
eng.init(false, pair.getPrivate());

BufferedReader br = new BufferedReader(new InputStreamReader(inputstream));

int inBlockSize =this.eng.getInputBlockSize() ;
int outBlockSize = this.eng.getOutputBlockSize();

int lines;

byte[] outblock = new byte[outBlockSize];

String rv = null;
int inL=0;
byte[] last=null;

try {
int amout=inputstream.available();
lines=amout/(inBlockSize*2);
//System.out.println("lines="+lines);
rv=br.readLine();

//System.out.println("#########padding size="+rv);
int padding_size=Integer.parseInt(rv);

while ((rv = br.readLine()) != null)
{
lines--;
/* 要注意,Hex处理密文是将每个byte用2个16进制表示
* 一个Hex码其实只需4bit来表示,一个byte有8bit,因此
* 需要2个Hex码表示,所以,一个字符经Hex Encode会
* 变成两个Hex字符。
*/
inL=rv.length()/2;
last=new byte[inL];
last = Hex.decode(rv);

outblock = eng.processBlock(last, 0, inBlockSize);

if(lines>0)
{
outputstream.write(outblock, 0, outBlockSize);
}
else
outputstream.write(outblock, 0, outBlockSize-padding_size);

}

} catch (DataLengthException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}

}

/**
* 输出公钥到文件
* @param Filename
*/
public void ExportPublicKey(String Filename)
{
String outfile = Filename;
BufferedOutputStream outstream = null;

try
{
outstream = new BufferedOutputStream(new FileOutputStream(outfile));
}
catch (IOException fnf)
{
System.err.println("无法创建公钥文件 ["+outfile+"]");
System.exit(1);
}


RSAKeyParameters mypubkey=this.getPublicKey();
BigInteger mypubkey_modulus=mypubkey.getModulus();
BigInteger mypubkey_exponent=mypubkey.getExponent();
System.out.println("[ExportPublicKey]mypubkey_modulus="+mypubkey_modulus.toString());
System.out.println("[ExportPublicKey]mypubkey_exponent="+mypubkey_exponent);

try
{
outstream.write(mypubkey_modulus.toString().getBytes());
outstream.write('\n');
outstream.write(mypubkey_exponent.toString().getBytes());
outstream.flush();
outstream.close();
}
catch (IOException closing)
{
closing.printStackTrace();
}
System.out.println("公钥输出到文件:"+Filename);

}

/**
* 从文件中导入公钥到内存
* @param Filename
* @return
*/
public RSAKeyParameters ImportPublicKey(String Filename)
{
String infile = Filename;
BufferedInputStream instream = null;
RSAKeyParameters RSAParam=null;
try
{
instream = new BufferedInputStream(new FileInputStream(infile));
}
catch (FileNotFoundException fnf)
{
System.err.println("公钥文件没有找到 ["+infile+"]");
System.exit(1);
}

BufferedReader br = new BufferedReader(new InputStreamReader(instream));
BigInteger mypubkey_modulus=null;
BigInteger mypubkey_exponent=null;
String readstr=null;

try {

readstr = br.readLine();

mypubkey_modulus= new BigInteger(readstr);
System.out.println("[ImportPublicKey]mypubkey_modulus="+mypubkey_modulus.toString());

readstr = br.readLine();
mypubkey_exponent=new BigInteger(readstr);
System.out.println("[ImportPublicKey]mypubkey_exponent="+mypubkey_exponent);

RSAParam=new RSAKeyParameters(false,mypubkey_modulus,mypubkey_exponent);

} catch (DataLengthException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}
return RSAParam;

}

/**
* 输出私钥到指定的文件
* @param Filename
*/
public void ExportPrivateKey(String Filename)
{

String outfile = Filename;
BufferedOutputStream outstream = null;

try
{
outstream = new BufferedOutputStream(new FileOutputStream(outfile));
}
catch (IOException fnf)
{
System.err.println("输出文件无法创建 ["+outfile+"]");
System.exit(1);
}


RSAPrivateCrtKeyParameters myprivkey=this.getPrivateKey();

BigInteger myprivkey_modulus=myprivkey.getModulus();
BigInteger myprivkey_exponent=myprivkey.getExponent();
BigInteger e=myprivkey.getPublicExponent(); //e is public
BigInteger dP=myprivkey.getDP();
BigInteger dQ=myprivkey.getDQ();
BigInteger p=myprivkey.getP();
BigInteger q=myprivkey.getQ();
BigInteger qInv=myprivkey.getQInv();


try
{
outstream.write(myprivkey_modulus.toString().getBytes());
outstream.write('\n');
outstream.write(e.toString().getBytes());
outstream.write('\n');
outstream.write(myprivkey_exponent.toString().getBytes());
outstream.write('\n');
outstream.write(p.toString().getBytes());
outstream.write('\n');
outstream.write(q.toString().getBytes());
outstream.write('\n');
outstream.write(dP.toString().getBytes());
outstream.write('\n');
outstream.write(dQ.toString().getBytes());
outstream.write('\n');
outstream.write(qInv.toString().getBytes());
outstream.write('\n');

outstream.flush();
outstream.close();
}
catch (IOException closing)
{
closing.printStackTrace();
}
System.out.println("私钥输出到文件:"+Filename);

}

/**
* 输出私钥到指定的文件,私钥经过密码加密
* 强烈建议在生产环境中使用此方法而不要使用
* ExportPrivateKey(String Filename)
*
* @param Filename 私钥文件
* @param Password 私钥的保护密码
*/
public void ExportPrivateKeyWithPass(String Filename, String Password)
{

String outfile = Filename;

ByteArrayOutputStream outstream=null;
BufferedOutputStream keyoutstream=null;
//借助BlockCipherTool来加密私钥
BlockCipherTool cipherTool=new BlockCipherTool();

//暂时使用DES加密私钥
//cipherTool.setEngine(new AESEngine());
//cipherTool.setEngine(new IDEAEngine());
cipherTool.setEngine(new DESEngine());
//cipherTool.setEngine(new BlowfishEngine());

//cipherTool.setKeyLength(64); //AES 32(Hex)*4=128bit
//String keyStr="123456789012345678901234567890FF"; //16进制 128bit AES Key
//String keyStr="123456789012345678901234567890FF123456789012345678901234567890FF"; //16进制 256bit AES Key
//String keyStr="123456789012345678901234567890FF"; //16进制 128bit IDEA Key
//String keyStr="0123456789abcdef"; //16进制 64bit(56) DES Key
//String keyStr="0123456789ABCDEF"; //16进制 64bit(56) Blowfish Key


outstream = new ByteArrayOutputStream();
try
{
keyoutstream = new BufferedOutputStream(new FileOutputStream(outfile));

}
catch (Exception fnf)
{
System.err.println("输出文件无法创建 ["+outfile+"]");
System.exit(1);
}


RSAPrivateCrtKeyParameters myprivkey=this.getPrivateKey();

BigInteger myprivkey_modulus=myprivkey.getModulus();
BigInteger myprivkey_exponent=myprivkey.getExponent();
BigInteger e=myprivkey.getPublicExponent(); //e is public
BigInteger dP=myprivkey.getDP();
BigInteger dQ=myprivkey.getDQ();
BigInteger p=myprivkey.getP();
BigInteger q=myprivkey.getQ();
BigInteger qInv=myprivkey.getQInv();


try
{
// 产生正确的私钥流
outstream.write(myprivkey_modulus.toString().getBytes());
outstream.write('\n');
outstream.write(e.toString().getBytes());
outstream.write('\n');
outstream.write(myprivkey_exponent.toString().getBytes());
outstream.write('\n');
outstream.write(p.toString().getBytes());
outstream.write('\n');
outstream.write(q.toString().getBytes());
outstream.write('\n');
outstream.write(dP.toString().getBytes());
outstream.write('\n');
outstream.write(dQ.toString().getBytes());
outstream.write('\n');
outstream.write(qInv.toString().getBytes());
outstream.write('\n');


byte[] privatekey_withtoutpass=outstream.toByteArray();

ByteArrayInputStream keyinstream=new ByteArrayInputStream(privatekey_withtoutpass);

//加密私钥
cipherTool.init(true,Password);
//将outstream转型成keyinstream,将keyinstream执行DES加密
cipherTool.Encrypt(keyinstream,keyoutstream);

keyinstream.close();
keyoutstream.flush();
keyoutstream.close();


}
catch (IOException closing)
{
closing.printStackTrace();
}

System.out.println("私钥经过加密并输出到文件:"+Filename);

}

/**
* 从某个文件中导入私钥,假定私钥未被加密
* @see ExportPrivateKey(String Filename)
* @param Filename
* @return
*/
public RSAPrivateCrtKeyParameters ImportPrivateKey(String Filename)
{
String infile = Filename;
BufferedInputStream instream = null;
RSAPrivateCrtKeyParameters RSAPrivParam=null;

try
{
instream = new BufferedInputStream(new FileInputStream(infile));
}
catch (FileNotFoundException fnf)
{
System.err.println("私钥文件没有找到 ["+infile+"]");
System.exit(1);
}

BufferedReader br = new BufferedReader(new InputStreamReader(instream));


BigInteger myprivkey_modulus=null;
BigInteger myprivkey_exponent=null;
BigInteger e=null;
BigInteger p=null;
BigInteger q=null;
BigInteger dP=null;
BigInteger dQ=null;
BigInteger qInv=null;

String readstr=null;
try {

readstr = br.readLine();
myprivkey_modulus= new BigInteger(readstr);
readstr = br.readLine();
e= new BigInteger(readstr);
readstr = br.readLine();
myprivkey_exponent= new BigInteger(readstr);
readstr = br.readLine();
p= new BigInteger(readstr);
readstr = br.readLine();
q= new BigInteger(readstr);
readstr = br.readLine();
dP= new BigInteger(readstr);
readstr = br.readLine();
dQ= new BigInteger(readstr);
readstr = br.readLine();
qInv= new BigInteger(readstr);


RSAPrivParam=new RSAPrivateCrtKeyParameters(myprivkey_modulus, myprivkey_exponent,
e,p,q,dP,dQ,qInv);

} catch (DataLengthException ex) {
ex.printStackTrace();
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
catch(Exception ex)
{
ex.printStackTrace();
}
return RSAPrivParam;

}

/**
* 从私钥文件中导入私钥,并用保护密码通过DES解密该私钥
* 放入内存,该方法跟ExportPrivateKeyWithPass 对应
*
* @see ExportPrivateKeyWithPass(String Filename, String Password)
* @param Filename
* @param Password
* @return
*/
public RSAPrivateCrtKeyParameters ImportPrivateKeyWithPass(String Filename,String Password)
{
String infile = Filename;
InputStream instream = null;

ByteArrayInputStream keyinstream =null;
ByteArrayOutputStream keyoutstream = new ByteArrayOutputStream();

//借助BlockCipherTool来加密私钥
BlockCipherTool cipherTool=new BlockCipherTool();

//暂时使用DES加密私钥
//cipherTool.setEngine(new AESEngine());
//cipherTool.setEngine(new IDEAEngine());
cipherTool.setEngine(new DESEngine());
//cipherTool.setEngine(new BlowfishEngine());

//cipherTool.setKeyLength(64); //AES 32(Hex)*4=128bit
//String keyStr="123456789012345678901234567890FF"; //16进制 128bit AES Key
//String keyStr="123456789012345678901234567890FF123456789012345678901234567890FF"; //16进制 256bit AES Key
//String keyStr="123456789012345678901234567890FF"; //16进制 128bit IDEA Key
//String keyStr="0123456789abcdef"; //16进制 64bit(56) DES Key
//String keyStr="0123456789ABCDEF"; //16进制 64bit(56) Blowfish Key


RSAPrivateCrtKeyParameters RSAPrivParam=null;
try
{
instream = new BufferedInputStream(new FileInputStream(infile));
}
catch (FileNotFoundException fnf)
{
System.err.println("私钥文件没有找到 ["+infile+"]");
System.exit(1);
}

cipherTool.init(false,Password);

//keyinstream-->ByteArrayOutputStream,将keyinstream执行DES加密
cipherTool.Decrypt(instream,keyoutstream);

byte[] privatekey_withtoutpass=keyoutstream.toByteArray();

keyinstream=new ByteArrayInputStream(privatekey_withtoutpass);



BigInteger myprivkey_modulus=null;
BigInteger myprivkey_exponent=null;
BigInteger e=null;
BigInteger p=null;
BigInteger q=null;
BigInteger dP=null;
BigInteger dQ=null;
BigInteger qInv=null;

String readstr=null;
try {

BufferedReader br = new BufferedReader(new InputStreamReader(keyinstream));

readstr = br.readLine();
myprivkey_modulus= new BigInteger(readstr);
readstr = br.readLine();
e= new BigInteger(readstr);
readstr = br.readLine();
myprivkey_exponent= new BigInteger(readstr);
readstr = br.readLine();
p= new BigInteger(readstr);
readstr = br.readLine();
q= new BigInteger(readstr);
readstr = br.readLine();
dP= new BigInteger(readstr);
readstr = br.readLine();
dQ= new BigInteger(readstr);
readstr = br.readLine();
qInv= new BigInteger(readstr);


RSAPrivParam=new RSAPrivateCrtKeyParameters(myprivkey_modulus, myprivkey_exponent,
e,p,q,dP,dQ,qInv);


keyinstream.close();
keyoutstream.flush();
keyoutstream.close();

} catch (DataLengthException ex) {
ex.printStackTrace();
} catch (IllegalStateException ex) {
ex.printStackTrace();
}
catch(Exception ex)
{
ex.printStackTrace();
}

return RSAPrivParam;

}

/**
* 为一个Block清0
* @param block
*/
public void reset(byte[] block)
{
for(int i=0;i<block.length;i++)
block[i]=(byte)0;
}

/**
* 将某个Block自off后的字节清0
* @param block
* @param off
*/
public void padding(byte[] block, int off)
{
for(int i=off;i<block.length;i++)
block[i]=(byte)0;
}


}
  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(110)

我参与的团队

随笔分类(126)

随笔档案(155)

文章分类(9)

文章档案(19)

相册

搜索

积分与排名

最新随笔

最新评论

阅读排行榜

评论排行榜