copy from: http://www.cnblogs.com/midea0978/articles/790689.html
RSA PKCS#1 v1.5加密标准主要描述了如何使用RSA公钥密码体系加密、解密数据,以及数字签名的算法,在网上可以直接查找到RFC的定义标准文本:
[English]
http://www.faqs.org/rfcs/rfc2313.html
[中文版本]
http://man.chinaunix.net/develop/rfc/RFC2313.txt
根据标准的定义,我们可以了解几个方面的内容:RSA公钥与私钥的构成,计算方法;加密、解密过程、数字签名过程,下面分别予以阐述:
1、RSA公钥与私钥的构成,计算方法
RSA的核心是2个大素数,p,q,描述如下:选择两个不同的奇素数p和q,以便e和(p-1)*(q-1)互素。
公开模数n是私人的素数p,q的乘积:n=p*q 。私人指数是一个正整数d,以便d*e-1可
以被(p-1)*(q-1)整除。模数n的字节长为k,k满足2^(8(k-1)) <= n < 2^(8k)。模数长度
k必须是至少12个字节,使之适应此文档中的块格式。
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, — n
publicExponent INTEGER — e }
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, — n
publicExponent INTEGER, — e
privateExponent INTEGER, — d
prime1 INTEGER, — p
prime2 INTEGER, — q
exponent1 INTEGER, — d mod (p-1)
exponent2 INTEGER, — d mod (q-1)
coefficient INTEGER — (inverse of q) mod p }
对于根据p,q计算密钥,下面给出一段JAVA程序来实现该过程。
import java.math.BigInteger;2

3public class RSAParameter
{4
private BigInteger p, q, e, n, d, dp, dq, qinv;5

6 RSAParameter()
{7
super();8
}9

10 RSAParameter(String ps, String qs, String exps)
{11
init(new BigInteger(ps), new BigInteger(qs), new BigInteger(exps));12
}13

14 public void init(BigInteger prime1, BigInteger prime2, BigInteger exp)
{15
this.p = prime1;16
this.q = prime2;17
this.e = exp;18
//n=p*q19
n = prime1.multiply(prime2);20
//exp*d=1 mod (p-1)(q-1)21
d = exp.modInverse(prime1.subtract(BigInteger.ONE).multiply(prime2.subtract(BigInteger.ONE)));22
//dp*r=1 mod (p-1) or d mod (p-1)23
dp = d.mod(p.subtract(BigInteger.ONE));24
//dp=exp.modInverse(p.subtract(BigInteger.ONE)); 也可以这样计算dp25
//dq*r=1 mod (q-1) or d mod (q-1)26
dq = d.mod(q.subtract(BigInteger.ONE));27
//q*qinv=1 mod p28
qinv = q.modInverse(p);29
}30

31

32

public String toString()
{33
StringBuffer sb = new StringBuffer();34
sb.append(“RSA Keyinfo:\n”);35
sb.append(“PrivateKey:\n”);36
sb.append(“modulus:” + n.toString() + “\n”);37
sb.append(“public exponent:” + e.toString() + “\n”);38
sb.append(“private exponent:” + d.toString() + “\n”);39
sb.append(“prime p:” + p.toString() + “\n”);40
sb.append(“prime q:” + q.toString() + “\n”);41
sb.append(“prime exponent p:” + dp.toString() + “\n”);42
sb.append(“prime exponent q:” + dq.toString() + “\n”);43
sb.append(“crt coefficient:” + qinv.toString() + “\n”);44
sb.append(“PublicKey:\n”);45
sb.append(“modulus:” + n.toString() + “\n”);46
sb.append(“public exponent:” + e.toString() + “\n”);47
return sb.toString();48
}49
}2、加密、解密过程
首先需要知道密钥的长度,这是根据n的bits位来标识的,通常在java中密钥的长度k必须1024以上,也就是n应该是128 bytes.
加密,需要先将消息D格式化成EB的加密块,使EB长度=128 bytes,
EB的表述是EB = 00 || BT || PS || 00 || D
对于D的长度,不应该长于k-11个8位字节,其必为正数,因为模数的长度k是至少12个8位字节。这种限制保证了填充串PS的长度至少为8个8位字节,这是一项安全措施。
其中BT、PS值含义如下:
00 :一般不用,数据D必须以一个非0字节开始,或是必须知道长度,以便加密块能被清楚的解析。
01 :私钥操作标志,PS必须填充0xFF,通常为数字签名时使用,可以保证每次的签名数据唯一性
02 :公钥操作标志,PS必须填充随机非0数,保证每次加密的结果比一样,提高安全性
加密过程将EB转换成整数,利用公钥e,n进行加密,计算公式为
ED=EB modpow (e,n)
加密后输出的结果长度应该是固定的,长度为k/8个bytes
解密过程则刚好相反,利用私钥d,n进行解密,计算公式为
EB=ED modpow (d,n)
解密后的结果是EB,因此我们必须了解是否用公钥加密的数据,如果是,解密后数据应该是
00 01 XX … XX 00 D,其中XX是随机填充的,只要定位最后的00位置,将尾部的bytes分割下来就是明文了。
私钥加密的数据解密后数据应该是
00 02 FF …FF 00 D,原理就差不多类似了
3、数字签名
数字签名中采用如下步骤
1)计算明文hash值hashdata
2)根据ASN.1规则编码hash算法特征串+hashdata,得到签名明文数据C
3)采用RSA私钥加密C得到digest签名数据
验证过程如下
1)采用公钥解密digest,得到C
2)根据C,ASN.1解码得到hash算法特征串,获知采用什么hash算法,hashdata是什么
3)根据hash算法计算明文的hashdata1,与hashdata比较是否一致
4)根据比较结果验证签名的有效性
加解密的原理基本好理解,关键是digestAlgorithm 的特征串,根据文档定义
DigestInfo ::= SEQUENCE {
digestAlgorithm DigestAlgorithmIdentifier,
digest Digest }
MD5计算hash时,标识符就是1.2.840.113549.2.5,对于ASN.1编码,其实是一种设备独立的编码规则,在底层网络通讯中广泛被采用,一般我们可以使用
3方的包例如bouncycastle来解析或者编码
md5 OBJECT IDENTIFIER ::=
{ iso(1) member-body(2) US(840) rsadsi(113549)
digestAlgorithm(2) 5 }
下面是一段演示代码
import org.bouncycastle.asn1.ASN1InputStream;2
import org.bouncycastle.asn1.DERObject;3
public class DecodeASN14
{5
public static void main(String[] args)6
{7
//这是一段RSA签名数据DigestInfo,来源参数为8 /**//*
9
n =10

11
“1126288441035520443003769796322773655928605287701246250663364051306962980440339952276427024206780159897108678418675935944014341562043346815832675434767490146209224950830870620012

13
8872607905765651105670638572435826363983713767368815675341307594277511890573814685529893908327924650039275886811260735916324764564853″;14
d =15

16
“2992173588487334854073063448704672493915337391727787123350142895968007759349185789566942478060370380696180661297826681221483086033345547005578941970907543402182671613932570953217

18
742771186379941997345196185777640711503947179993281763887256221502624948138721356270847024512768717268039878195832083548155677542117″;19
e = “65537”;20
明文:s=”1234″21
算法:MD522
*/23

byte[] data=
{48,32,48,12,6,8,42,-122,72,-122,-9,13,2,5,5,0,4,16,-127,-36,-101,-37,82,-48,77,-62,0,54,-37,-40,49,62,-48,85};24
ASN1InputStream is=new ASN1InputStream(res);25
DERObject obj=is.readObject();26
System.out.println(“ASN1解码:”+obj.toString());27
//运行结果:28
//ASN1解码:[[1.2.840.113549.2.5, NULL], #81dc9bdb52d04dc20036dbd8313ed055]29
}30
}因此我们可以列出常用的hash算法标识数据备用,避免每次都要自行ASN.1编码、解码,下面列出了常见的hash标识数据
MD2: (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10 || H
MD5: (0x)30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 || H
SHA-1: (0x)30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H
SHA-256: (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H
SHA-384: (0x)30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00 04 30 || H
SHA-512: (0x)30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 || H
根据前面数据的就可以直接分析出hash算法是什么。