RSA加密签名

加密算法

就问你美不美

对称加密算法

  • (1) 甲方选择某一种加密规则,对信息进行加密;  
  • (2) 乙方使用同一种规则,对信息进行解密。
  • (3) 加密和解秘使用同一种规则,对信息进行解谜

这种加密模式有一个最大弱点:甲方必须把加密规则告诉乙方,否则无法解密。保存和传递密钥,就成了最头疼的问题。

非对称加密算法

  • (1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
  • (2) 甲方获取乙方的公钥,然后用它对信息加密。
  • (3) 乙方得到加密后的信息,用私钥解密。

如果公钥加密的信息只有私钥解得开,那么只要私钥不泄漏,通信就是安全的。

  • 三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,这种算法用他门三个人的名字命名,叫做RSA算法。

你需要知道的数论知识

  • (1) 互质关系
    • 如果两个正整数,除了1以外,没有其他公因子,我们就称这两个数是互质关系(coprime)。
  • (2) 欧拉函数
  • (3) 欧拉定理
    • 如果两个正整数a和n互质,则n的欧拉函数 φ(n)可以让下面的等式成立:
    • image%7D%5Cequiv%5C1%20(mod%5C%20n)&chs=60)
  • 模反元素
    • 如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1被n整除,或者说ab被n除的余数是1。
    • image&chs=40)
    • 这时,b就叫做a的”模反元素”。

欧拉函数

  • 任意给定正整数n,请问在小于等于n的正整数之中,有多少个数与n构成互质关系,计算这个值的方法叫做欧拉函数
  • φ(n)表示。
    • 在1到8之中,与8形成互质关系的是1、3、5、7,所以 φ(8) = 4

  • 第一种情况:如果是1
    • 如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系。

  • 第二种情况:如果是质数
    • 如果n是质数,则 φ(n)=n-1
    • 因为质数与小于它的每一个数,都构成互质关系。比如51234都构成互质关系。

  • 第三种情况:如果是质数的某一个次方
    • 如果n是质数的某一个次方,即 n = p^kp为质数,k为大于等于1的整数),则
    • image%3Dp%5E%7Bk%7D-p%5E%7Bk-1%7D&chs=40)
    • 比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。
    • 这是因为只有当一个数不包含质数p,才可能与n互质。而包含质数p的数一共有p^(k-1)个,即1×p、2×p、3×p、…、p^(k-1)×p,把它们去除,剩下的就是与n互质的数。

第四种情况:如果n可以分解成两个互质的整数之积

+ 如果n可以分解成两个互质的整数之积

1
  n = p1 × p2
+ 则
1
 φ(n) = φ(p1p2) = φ(p1)φ(p2)
+ 积的欧拉函数等于各个因子的欧拉函数之积。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24。

模反元素

  • 如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1,也就是ab / n = 1
  • image&chs=40)
  • 欧拉定理可以用来证明模反元素必然存在。
  • image%7D%3Da%5Ctimes%20a%5E%7B%5Cphi(n)-1%7D%5Cequiv%5C%201%5C%20(mod%5C%20n)%20&chs=40)
  • 可以看到,a的 φ(n)-1 次方,就是a的模反元素。
  • 比如,3和11互质,那么3的模反元素就是4,因为 (3 × 4)-1 可以被11整除。显然,模反元素不止一个, 4加减11的整数倍都是3的模反元素 {…,-18,-7,4,15,26,…},即如果b是a的模反元素,则 b+kn 都是a的模反元素。
  • 欧拉定理可以用来证明模反元素必然存在。

RSA数学原理

  • RSA加密 密文 = 明文e次方 Mod (就是%取余的意思) N 公钥 (e,N)
  • RSA解密 明文 = 密文d次方 Mod (就是%取余的意思) N 私钥 (d,N)

jDK实现

算法 密钥长度 默认密钥长度 工作模式 填充方式
RSA 512~65536
(必须64的倍数)
1024 ECB NoPadding(常用)、
PKCS1Padding(常用)、
OAEPWITHMD5AndMGF1Padding
  • 公钥加密私钥解密–主要用来加密
  • 私钥加密公钥解密–主要用来数字签名

密钥生成步骤

image

第一步,随机选择两个不相等的质数p和q。

  • 爱丽丝选择了61和53。(实际应用中,这两个质数越大,就越难破解。)

第二步,计算p和q的乘积n。

1
n = 61×53 = 3233

第三步,计算n的欧拉函数φ(n)。

  • 根据公式
1
 φ(n) = (p-1)(q-1)
  • 爱丽丝算出φ(3233)等于60×52,即3120。

第四步,随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质。

  • 爱丽丝就在1到3120之间,随机选择了17。(实际应用中,常常选择65537。)

第五步,计算e对于φ(n)的模反元素d。

  • 所谓”模反元素”就是指有一个整数d,可以使得ed被φ(n)除的余数为1。
1
ed ≡ 1 (mod φ(n))
  • 这个式子等价于
1
 ed - 1 = kφ(n)
  • 于是,找到模反元素d,实质上就是对下面这个二元一次方程求解。
1
 ex + φ(n)y = 1
  • 已知 e=17, φ(n)=3120
1
17x + 3120y = 1
  • 这个方程可以用”扩展欧几里得算法”求解,此处省略具体过程。总之,爱丽丝算出一组整数解为 (x,y)=(2753,-15),即 d=2753。

第六步,将n和e封装成公钥,n和d封装成私钥。

  • 在爱丽丝的例子中,n=3233,e=17,d=2753,所以公钥就是 (3233,17),私钥就是(3233, 2753)。
    实际应用中,公钥和私钥的数据都采用ASN.1格式表达(实例)。

七、RSA算法的可靠性

  • 回顾上面的密钥生成步骤,一共出现六个数字:
1
2
3
4
5
6
p
q
n
φ(n)
e
d
  • 这六个数字之中,公钥用到了两个(n和e),其余四个数字都是不公开的。其中最关键的是d,因为n和d组成了私钥,一旦d泄漏,就等于私钥泄漏。

八、加密和解密

  • (1)加密要用公钥 (n,e)

    • 假设鲍勃要向爱丽丝发送加密信息m,他就要用爱丽丝的公钥 (n,e) 对m进行加密。这里需要注意,m必须是整数(字符串可以取ascii值或unicode值),且m必须小于n。
    • 所谓”加密”,就是算出下式的c:

      1
        me ≡ c (mod n)
+ 爱丽丝的公钥是 (3233, 17),鲍勃的m假设是65,那么可以算出下面的等式:

1
6517 ≡ 2790 (mod 3233)
+ 于是,c等于2790,鲍勃就把2790发给了爱丽丝。
  • (2)解密要用私钥(n,d)

    • 爱丽丝拿到鲍勃发来的2790以后,就用自己的私钥(3233, 2753) 进行解密。可以证明,下面的等式一定成立:

      1
       cd ≡ m (mod n)
+ 也就是说,c的d次方除以n的余数为m。现在,c等于2790,私钥是(3233, 2753),那么,爱丽丝算出

1
27902753 ≡ 65 (mod 3233)
+ 因此,爱丽丝知道了鲍勃加密前的原文就是65。

九、私钥解密的证明

  • 最后,我们来证明,为什么用私钥解密,一定可以正确地得到m。也就是证明下面这个式子:

    1
      cd ≡ m (mod n)
    • 因为,根据加密规则  

      1
        me ≡ c (mod n)
    • 于是,c可以写成下面的形式:

      1
        c = me - kn
    • 将c代入要我们要证明的那个解密规则:

      1
      (me - kn)d ≡ m (mod n)
    • 它等同于求证

      1
      med ≡ m (mod n)
    • 由于

      1
      ed ≡ 1 (mod φ(n))
    • 所以

      1
      ed = hφ(n)+1
    • 将ed代入:

      1
      mhφ(n)+1 ≡ m (mod n)
  • 接下来,分成两种情况证明上面这个式子。

  • (1)m与n互质。

    • 根据欧拉定理,此时

      1
        mφ(n) ≡ 1 (mod n)
    • 得到

      1
      (mφ(n))h × m ≡ m (mod n)
    • 原式得到证明。

  • (2)m与n不是互质关系。
    此时,由于n等于质数p和q的乘积,所以m必然等于kp或kq。
    以 m = kp为例,考虑到这时k与q必然互质,则根据欧拉定理,下面的式子成立:

    1
    (kp)q-1 ≡ 1 (mod q)
    • 进一步得到

      1
        [(kp)q-1]h(p-1) × kp ≡ kp (mod q)
    • 1
        (kp)ed ≡ kp (mod q)
    • 将它改写成下面的等式

      1
        (kp)ed = tq + kp
    • 这时t必然能被p整除,即 t=t’p

      1
        (kp)ed = t'pq + kp
    • 因为 m=kp,n=pq,所以

      1
       med ≡ m (mod n)
    • 原式得到证明。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package rsa;
/*
* --------------------------------------------**********--------------------------------------------
*
* 该算法于1977年由美国麻省理工学院MIT(Massachusetts Institute of Technology)的Ronal Rivest,Adi Shamir和Len Adleman三位年轻教授提出,并以三人的姓氏Rivest,Shamir和Adlernan命名为RSA算法,是一个支持变长密钥的公共密钥算法,需要加密的文件快的长度也是可变的!
*
*所谓RSA加密算法,是世界上第一个非对称加密算法,也是数论的第一个实际应用。它的算法如下:
*
1.找两个非常大的质数p和q(通常p和q都有155十进制位或都有512十进制位)并计算n=pq,k=(p-1)(q-1)。
2.将明文编码成整数M,保证M不小于0但是小于n。
3.任取一个整数e,保证e和k互质,而且e不小于0但是小于k。加密钥匙(称作公钥)是(e, n)。 【exponent】
4.找到一个整数d,使得ed除以k的余数是1(只要e和n满足上面条件,d肯定存在)。解密钥匙(称作密钥)是(d, n)。
加密过程: 加密后的编码C等于M的e次方除以n所得的余数。
解密过程: 解密后的编码N等于C的d次方除以n所得的余数。
只要e、d和n满足上面给定的条件。M等于N。
--------------------------------------------**********--------------------------------------------
*/
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import java.util.Map;
http://my.oschina.net/ousinka/blog/338099

加密算法

对称加密算法

  • (1) 甲方选择某一种加密规则,对信息进行加密;  
  • (2) 乙方使用同一种规则,对信息进行解密。
  • (3) 加密和解秘使用同一种规则,对信息进行解谜

这种加密模式有一个最大弱点:甲方必须把加密规则告诉乙方,否则无法解密。保存和传递密钥,就成了最头疼的问题。

非对称加密算法

  • (1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
  • (2) 甲方获取乙方的公钥,然后用它对信息加密。
  • (3) 乙方得到加密后的信息,用私钥解密。

如果公钥加密的信息只有私钥解得开,那么只要私钥不泄漏,通信就是安全的。

  • 三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,这种算法用他门三个人的名字命名,叫做RSA算法。

你需要知道的数论知识

  • (1) 互质关系
    • 如果两个正整数,除了1以外,没有其他公因子,我们就称这两个数是互质关系(coprime)。
  • (2) 欧拉函数
  • (3) 欧拉定理
    • 如果两个正整数a和n互质,则n的欧拉函数 φ(n)可以让下面的等式成立:
    • image%7D%5Cequiv%5C1%20(mod%5C%20n)&chs=60)
  • 模反元素
    • 如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1被n整除,或者说ab被n除的余数是1。
    • image&chs=40)
    • 这时,b就叫做a的”模反元素”。

欧拉函数

  • 任意给定正整数n,请问在小于等于n的正整数之中,有多少个数与n构成互质关系,计算这个值的方法叫做欧拉函数
  • φ(n)表示。
    • 在1到8之中,与8形成互质关系的是1、3、5、7,所以 φ(8) = 4

  • 第一种情况:如果是1
    • 如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系。

  • 第二种情况:如果是质数
    • 如果n是质数,则 φ(n)=n-1
    • 因为质数与小于它的每一个数,都构成互质关系。比如51234都构成互质关系。

  • 第三种情况:如果是质数的某一个次方
    • 如果n是质数的某一个次方,即 n = p^kp为质数,k为大于等于1的整数),则
    • image%3Dp%5E%7Bk%7D-p%5E%7Bk-1%7D&chs=40)
    • 比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。
    • 这是因为只有当一个数不包含质数p,才可能与n互质。而包含质数p的数一共有p^(k-1)个,即1×p、2×p、3×p、…、p^(k-1)×p,把它们去除,剩下的就是与n互质的数。

  • 第四种情况:如果n可以分解成两个互质的整数之积

    • 如果n可以分解成两个互质的整数之积

      1
        n = p1 × p2
+ 则

1
 φ(n) = φ(p1p2) = φ(p1)φ(p2)
+ 积的欧拉函数等于各个因子的欧拉函数之积。比如,φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24。

模反元素

  • 如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1,也就是ab / n = 1
  • image&chs=40)
  • 欧拉定理可以用来证明模反元素必然存在。
  • image%7D%3Da%5Ctimes%20a%5E%7B%5Cphi(n)-1%7D%5Cequiv%5C%201%5C%20(mod%5C%20n)%20&chs=40)
  • 可以看到,a的 φ(n)-1 次方,就是a的模反元素。
  • 比如,3和11互质,那么3的模反元素就是4,因为 (3 × 4)-1 可以被11整除。显然,模反元素不止一个, 4加减11的整数倍都是3的模反元素 {…,-18,-7,4,15,26,…},即如果b是a的模反元素,则 b+kn 都是a的模反元素。
  • 欧拉定理可以用来证明模反元素必然存在。

RSA数学原理

  • RSA加密 密文 = 明文e次方 Mod (就是%取余的意思) N 公钥 (e,N)
  • RSA解密 明文 = 密文d次方 Mod (就是%取余的意思) N 私钥 (d,N)

jDK实现

算法 密钥长度 默认密钥长度 工作模式 填充方式
RSA 512~65536
(必须64的倍数)
1024 ECB NoPadding(常用)、
PKCS1Padding(常用)、
OAEPWITHMD5AndMGF1Padding
  • 公钥加密私钥解密–主要用来加密
  • 私钥加密公钥解密–主要用来数字签名

密钥生成步骤

image

第一步,随机选择两个不相等的质数p和q。

  • 爱丽丝选择了61和53。(实际应用中,这两个质数越大,就越难破解。)

第二步,计算p和q的乘积n。

1
n = 61×53 = 3233

第三步,计算n的欧拉函数φ(n)。

  • 根据公式
1
 φ(n) = (p-1)(q-1)
  • 爱丽丝算出φ(3233)等于60×52,即3120。

第四步,随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质。

  • 爱丽丝就在1到3120之间,随机选择了17。(实际应用中,常常选择65537。)

第五步,计算e对于φ(n)的模反元素d。

  • 所谓”模反元素”就是指有一个整数d,可以使得ed被φ(n)除的余数为1。
1
ed ≡ 1 (mod φ(n))
  • 这个式子等价于
1
 ed - 1 = kφ(n)
  • 于是,找到模反元素d,实质上就是对下面这个二元一次方程求解。
1
 ex + φ(n)y = 1
  • 已知 e=17, φ(n)=3120
1
17x + 3120y = 1
  • 这个方程可以用”扩展欧几里得算法”求解,此处省略具体过程。总之,爱丽丝算出一组整数解为 (x,y)=(2753,-15),即 d=2753。

第六步,将n和e封装成公钥,n和d封装成私钥。

  • 在爱丽丝的例子中,n=3233,e=17,d=2753,所以公钥就是 (3233,17),私钥就是(3233, 2753)。
    实际应用中,公钥和私钥的数据都采用ASN.1格式表达(实例)。

七、RSA算法的可靠性

  • 回顾上面的密钥生成步骤,一共出现六个数字:
1
2
3
4
5
6
p
q
n
φ(n)
e
d
  • 这六个数字之中,公钥用到了两个(n和e),其余四个数字都是不公开的。其中最关键的是d,因为n和d组成了私钥,一旦d泄漏,就等于私钥泄漏。

八、加密和解密

  • (1)加密要用公钥 (n,e)

    • 假设鲍勃要向爱丽丝发送加密信息m,他就要用爱丽丝的公钥 (n,e) 对m进行加密。这里需要注意,m必须是整数(字符串可以取ascii值或unicode值),且m必须小于n。
    • 所谓”加密”,就是算出下式的c:

      1
        me ≡ c (mod n)
+ 爱丽丝的公钥是 (3233, 17),鲍勃的m假设是65,那么可以算出下面的等式:

1
65172790 (mod 3233)
+ 于是,c等于2790,鲍勃就把2790发给了爱丽丝。
  • (2)解密要用私钥(n,d)

    • 爱丽丝拿到鲍勃发来的2790以后,就用自己的私钥(3233, 2753) 进行解密。可以证明,下面的等式一定成立:

      1
       cd ≡ m (mod n)
+ 也就是说,c的d次方除以n的余数为m。现在,c等于2790,私钥是(3233, 2753),那么,爱丽丝算出

1
2790275365 (mod 3233)
+ 因此,爱丽丝知道了鲍勃加密前的原文就是65。

九、私钥解密的证明

  • 最后,我们来证明,为什么用私钥解密,一定可以正确地得到m。也就是证明下面这个式子:

    1
      cd ≡ m (mod n)
    • 因为,根据加密规则  

      1
        me ≡ c (mod n)
    • 于是,c可以写成下面的形式:

      1
        c = me - kn
    • 将c代入要我们要证明的那个解密规则:

      1
      (me - kn)d ≡ m (mod n)
    • 它等同于求证

      1
      med ≡ m (mod n)
    • 由于

      1
      ed ≡ 1 (mod φ(n))
    • 所以

      1
      ed = hφ(n)+1
    • 将ed代入:

      1
      mhφ(n)+1 ≡ m (mod n)
  • 接下来,分成两种情况证明上面这个式子。

  • (1)m与n互质。

    • 根据欧拉定理,此时

      1
        mφ(n) ≡ 1 (mod n)
    • 得到

      1
      (mφ(n))h × m ≡ m (mod n)
    • 原式得到证明。

  • (2)m与n不是互质关系。
    此时,由于n等于质数p和q的乘积,所以m必然等于kp或kq。
    以 m = kp为例,考虑到这时k与q必然互质,则根据欧拉定理,下面的式子成立:

    1
    (kp)q-11 (mod q)
    • 进一步得到

      1
        [(kp)q-1]h(p-1) × kp ≡ kp (mod q)
    • 1
        (kp)ed ≡ kp (mod q)
    • 将它改写成下面的等式

      1
        (kp)ed = tq + kp
    • 这时t必然能被p整除,即 t=t’p

      1
        (kp)ed = t'pq + kp
    • 因为 m=kp,n=pq,所以

      1
       med ≡ m (mod n)
    • 原式得到证明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package rsa;
/*
* --------------------------------------------**********--------------------------------------------
*
* 该算法于1977年由美国麻省理工学院MIT(Massachusetts Institute of Technology)的Ronal Rivest,Adi Shamir和Len Adleman三位年轻教授提出,并以三人的姓氏Rivest,Shamir和Adlernan命名为RSA算法,是一个支持变长密钥的公共密钥算法,需要加密的文件快的长度也是可变的!
*
*所谓RSA加密算法,是世界上第一个非对称加密算法,也是数论的第一个实际应用。它的算法如下:
*
1.找两个非常大的质数p和q(通常p和q都有155十进制位或都有512十进制位)并计算n=pq,k=(p-1)(q-1)。
2.将明文编码成整数M,保证M不小于0但是小于n。
3.任取一个整数e,保证e和k互质,而且e不小于0但是小于k。加密钥匙(称作公钥)是(e, n)。 【exponent】
4.找到一个整数d,使得ed除以k的余数是1(只要e和n满足上面条件,d肯定存在)。解密钥匙(称作密钥)是(d, n)。
加密过程: 加密后的编码C等于M的e次方除以n所得的余数。
解密过程: 解密后的编码N等于C的d次方除以n所得的余数。
只要e、d和n满足上面给定的条件。M等于N。

过程中遇到的问题总结

  • 加密后的数据需要用Base64将bytes转换成字符串不然会乱码
1
2
3
4
byte[] bytes = RSA.encryptByPrivateKey(b, privateKeyStr);
String s2 = Base64Util.encode2String(bytes);
String s = new String(bytes);
System.out.println("私钥加密的数据" + s2);
  • 解密后的数据不需要用Base64将bytes转换成字符串,直接new String(bytes)
1
2
3
byte[] bytes1 = RSA.decryptByPublicKey(bytes, publicKeyStr);
String s1 = new String(bytes1);
System.out.println("公钥解密的数据" + s1);

  • RSAUtil.java
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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
package rsa;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
/**
* @author by benny on 2016/7/23.
* @version 1.0
* @description 加密解密签名工具类 使用RSA算法
*/
public class RSAUtil {
//算法类型
private static final String KEY_ALGORITHM = "RSA";
//钥匙的长度
private static final int KEY_SIZE = 1024;
//RSA公钥
private static final String PUBLIC_KEY = "RSAPublicKey";
//RSA私钥
private static final String PRIVATE_KEY = "RSAPrivateKey";
//签名算法类型
private static String SIGN_ALGORITHM = "SHA1WithRSA";
/**
* 初始化密钥
*/
public static Map<String, Object> initKeyPair() {
Map<String, Object> map = new HashMap<>();
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
map.put(PUBLIC_KEY, publicKey);
map.put(PRIVATE_KEY, privateKey);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return map;
}
/**
* 获得公钥
*
* @return
*/
public static RSAPublicKey getPublicKey(Map<String, Object> keyMap) {
return (RSAPublicKey) initKeyPair().get(PUBLIC_KEY);
}
/**
* 获得公钥
*
* @return
*/
public static RSAPrivateKey getPrivateKey() {
return (RSAPrivateKey) initKeyPair().get(PRIVATE_KEY);
}
/**
* 取得公钥,并转化为String类型
* @param keyMap
* @return 公钥
*/
public static String getPublicKeyStr(Map<String, Object> keyMap) {
Key publicKey = (Key) keyMap.get(PUBLIC_KEY);
return Base64Util.encode2String(publicKey.getEncoded());
}
/**
* 取得私钥,并转化为String类型
* @param keyMap
* @return
*/
public static String getPrivateKeyStr(Map<String, Object> keyMap) {
Key privateKey = (Key) keyMap.get(PRIVATE_KEY);
return Base64Util.encode2String(privateKey.getEncoded());
}
/**
* 用公钥加密
* @param bytes 加密数据
* @param publicKeyStr 密钥
* @return
*/
public static byte[] encryptByPublicKey(byte[] bytes, String publicKeyStr) {
//对公钥解密
byte[] publicKeyBytes = Base64Util.decodeString2Base64(publicKeyStr);
byte[] result = new byte[0];
try {
// 根据字节数组取得公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//加密后的byte数组
result = cipher.doFinal(bytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return result;
}
/**
* 用私钥加密
* @param bytes 加密数据
* @param privateKeyStr 密钥
* @return
*/
public static byte[] encryptByPrivateKey(byte[] bytes, String privateKeyStr) {
//对私钥解密
byte[] privateKeyBytes = Base64Util.decodeString2Base64(privateKeyStr);
byte[] result = new byte[0];
try {
// 取得私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
result = cipher.doFinal(bytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return result;
}
/**
* 用公钥解密
* @param bytes 加密数据
* @param publicKeyStr 私钥
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] bytes, String publicKeyStr) {
//对公钥解密
byte[] publicKeyBytes = Base64Util.decodeString2Base64(publicKeyStr);
byte[] result = new byte[0];
try {
//取得公钥
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
//用公钥解密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey);
result = cipher.doFinal(bytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return result;
}
/**
* 用私钥解密
*/
public static byte[] decryptByPrivateKey(byte[] bytes, String privateKeyStr) {
//对私钥解密
byte[] privateKeyBytes = Base64Util.decodeString2Base64(privateKeyStr);
byte[] result = new byte[0];
try {
//根据字节数组获得私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//解密数据
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//解密后的byte数组
result = cipher.doFinal(bytes);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return result;
}
/**
* RSA签名
*
* @param bytes 待签名数据
* @param privateKeyStr 商户私钥
* @return 签名值
*/
public static String signByPrivateKey(byte[] bytes, String privateKeyStr) {
//base64解密私钥
byte[] privateKeyBytes = Base64Util.decodeString2Base64(privateKeyStr);
String result = "";
try {
//取得私钥
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
//指定加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
//用私钥对信息生成数字签名
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initSign(privateKey);
signature.update(bytes);
result = Base64Util.encode2String(signature.sign());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
return result;
}
/**
* RSA签名验证
*
* @param bytes 待签名数据
* @param signByte 签名值
* @param publicKeyStr 分配给开发商公钥
* @return 布尔值
*/
public static boolean checkSignature(byte[] bytes, byte[] signByte, String publicKeyStr) {
//base64解密公钥
byte[] publicKeyBytes = Base64Util.decodeString2Base64(publicKeyStr);
boolean result = false;
try {
//构造X509EncodedKeySpec对象
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
//指定加密算法
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
//取公钥匙对象
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initVerify(publicKey);
signature.update(bytes);
result = signature.verify(signByte);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
return result;
}
}
  • Base64Util.java
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package rsa;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.IOException;
/**
* @author by benny on 2016/7/23.
* @version 1.0
* @description Base64 编码工具
*/
public class Base64Util {
/**
* 加密成字符串
* @param key
* @return
*/
public static String encode2String(byte[] key){
return new BASE64Encoder().encode(key);
}
/**
* 解密成字节数组
* @param key
* @return
*/
public static byte[] decodeString2Base64(String key){
byte[] result = new byte[0];
try {
result = new BASE64Decoder().decodeBuffer(key);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}

RSAUtil使用说明

公钥加密 私钥解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
String resource = "我是加密的数据";
Map<String, Object> keyMap = RSAUtil.initKeyPair();
<!--获得公钥-->
String publicKeyStr = RSAUtil.getPublicKeyStr(keyMap);
<!--获得私钥-->
String privateKeyStr = RSAUtil.getPrivateKeyStr(keyMap);
<!--将要加密的字符串转成字节数组-->
byte[] bytes = resource.getBytes();
<!--公钥加密-->
byte[] encryptBytes = RSAUtil.encryptByPublicKey(bytes, publicKeyStr);
<!--加密后的字符串-->
String encryptStr = Base64Util.encode2String(encryptBytes);
<!--私钥解密-->
byte[] decryptBytes = RSAUtil.decryptByPrivateKey(bytes, privateKeyStr);
<!--解密后的字符串-->
String decryptStr = new String(decryptBytes);

私钥加密 公钥解密 (签名)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
String resource = "我是加密的数据";
Map<String, Object> keyMap = RSAUtil.initKeyPair();
<!--获得公钥-->
String publicKeyStr = RSAUtil.getPublicKeyStr(keyMap);
<!--获得私钥-->
String privateKeyStr = RSAUtil.getPrivateKeyStr(keyMap);
<!--将要加密的字符串转成字节数组-->
byte[] b = resource.getBytes();
<!--生成签名字符串-->
String s1 = RSAUtil.signByPrivateKey(b, privateKeyStr);
<!--验证签名-->
boolean result = RSAUtil.checkSignature(b12, Base64Util.decodeString2Base64(s1), publicKeyStr);
if true 签名正确 else 签名失败
Contents
  1. 1. 加密算法
    1. 1.1. 对称加密算法
    2. 1.2. 非对称加密算法
    3. 1.3. 你需要知道的数论知识
    4. 1.4. 欧拉函数
    5. 1.5. 模反元素
    6. 1.6. RSA数学原理
    7. 1.7. jDK实现
  2. 2. 密钥生成步骤
  3. 3. 加密算法
    1. 3.1. 对称加密算法
    2. 3.2. 非对称加密算法
    3. 3.3. 你需要知道的数论知识
    4. 3.4. 欧拉函数
    5. 3.5. 模反元素
    6. 3.6. RSA数学原理
    7. 3.7. jDK实现
  4. 4. 密钥生成步骤
    1. 4.1. 过程中遇到的问题总结
  5. 5. RSAUtil使用说明
    1. 5.1. 公钥加密 私钥解密
    2. 5.2. 私钥加密 公钥解密 (签名)