RSA原理

463 阅读39分钟

RSA原理和实现

RSA算法的发明者是:Ron Rivest、Adi Shamir、Leonard Adleman,算法提出于1977年。

  RSA是一种非对称加密算法,是因为加密和解密使用的是不同的密钥。RSA算法利用公钥来加密,用私钥来解密;公钥可以随意公布,任何人都可以利用公钥来加密一条信息,只有私钥持有者才能对信息解密。

  RSA另有一个特点:用私钥加密的信息,用公钥可以解密。这让基于RSA的数字签名成为可能。假如我写了一篇文章,我可以用私钥加密这一篇文本的md5值,把密文(也就是签名)附在文章后面。读者从可信的渠道得到我的公钥,利用公钥解密签名,发现与这篇文章的md5值一致,于是可以确信这篇文章确实是我写的。

  RSA是极其巧妙的算法,而它的数学基础却出奇地简单,代码实现也不复杂。RSA同时是一个难以破解的密码体系,破解RSA的速度取决于大整数分解的速度

RSA的数学基础

RSA的数学基础是一个数论函数(定义域为正整数集,值域为复数集),定义是:φ(n) 为 {1,2,3,⋯,n} 中与 n 互质的数的个数。

  举几个例子:φ(6) = 2,因为6以内与6互质的数只有1和5. 而 φ(10) = 4 ,因为10以内与10互质的数有1、3、7、9,共4个。另外,不难发现,对于质数p,有 φ(p) = p−1.

  欧拉函数的通项公式是什么?先给出结论,再谈谈是如何找到这个结论的。

image-20220521193809617

想求出φ(n),那就得统计出 n 以内有多少个与 n 互质的数。那么,与 n 互质的数 r 有什么特点呢?

关键在于: n 拥有的质因子, r 全都没有【质因子素因数质因数)在数论里是指能整除给定正整数的质数【指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数】。除了1以外,两个没有其他共同质因子的正整数称为互质。因为 1 没有质因子,1与任何正整数(包括1本身)都是互质。正整数的因数分解可将正整数表示为一连串的质因子相乘,质因子如重复可以用指数表示。根据算术基本定理,任何正整数皆有独一无二的质因子分解式只有一个质因子的正整数为质数。】。

每个合数都可以写成几个质数(也可称为素数)相乘的形式,这几个质数就都叫做这个合数的质因数。如果一个质数是某个数的因数,那么就说这个质数是这个数的质因数;而这个因数一定是一个质数。

换句话讲:假设 n 的质因数分解为

image-20220521201015521

则 r 必然满足 r % p1 = 0, r % p2 =0, ⋯既然如此,研究 r 模掉 n 的质因子们所得到的结果,就可以看出 r 的一些性质。构造一个函数:

image-20220521201158979

有性质如下:

  r 与 n 互质,当且仅当 G(r) 生成的元组中,对于第 i 位,总有它不能被 pi 整除。我们把这种元组称为合法元组。

  这是一条很平凡的性质,但它可以成为计数的一把钥匙。直接统计 n 以内与 n 互质的数是困难的,但统计有多少个合法元组则简单得多。合法元组的第 i 位不能是 pi 的倍数或0,那么这一位有 piki⋅(1−1pi) 种取值,故依据乘法原理,合法元组的总数是:

image-20220521201233156

  这个式子要成为欧拉函数的通项公式,还有最后一步路要走:G目前只是一个普通映射,既有可能多个 r 对应同一个合法元组,也可能一个合法元组对应不了任何 r. 从而合法元组的数量未必恰好就是与 n 互质的数的数量。接下来的任务是:证明 元组G:{1,2,3,⋯,n}→(元组) 是一个双射。

  中国剩余定理(CRT) 通过一个精妙的构造,指出了 元组(元组)→{1,2,3,⋯,n}是一个双射(当然了,这个结论只是CRT的一个副产品)。中国剩余定理在这里不再赘述,感兴趣的读者可以去查阅相关资料。总之,既然元组与 n 以内的整数是一一对应的,那么合法元组的数量就是 n 以内与 n 互质的数的个数。从而我们得到了欧拉函数的通项公式:

image-20220521201320214

  现在我们手上有欧拉函数的通项公式了。可以随便挑几个数验证一下:

image-20220521201344636

  利用欧拉函数的通项公式,可以得到两条性质:

φ 是积性函数。若 a,b 互质,则有

image-20220521201414883

欧拉定理。若 a,m 互质,则有

image-20220521201448217

不难发现,费马小定理是欧拉定理在 m 为质数下的情形。

  以上这堆性质的证明、欧拉函数通项的求法、线性筛欧拉函数、扩展欧几里得算法,是接下来代码实现的基础。

  欧拉定理是欧拉函数最大的作用。利用欧拉定理可以得到结论:若a,m互质,则

image-20220521201719854

这不仅大大降低了计算复杂度(可以利用快速幂算法),也是 RSA 算法的基石。

RSA算法

RSA算法的过程如下:

  1. 随便选择两个极大的质数,分别记为 p,q. 当然这里 p ≠ q.
  2. N = p ⋅ q, 则由欧拉函数的积性有 φ(N) = (p − 1)(q − 1).
  3. 选取一个小于 N 的 e, 使得 gcd(e, N) = 1. 求出 e 在模 φ(N) 意义下的逆元,记为d.
  4. 我们得到了 (N, e) 作为公钥, (N, d) 作为私钥。

注意到 e 和 d 是对称的,把 e 拿去当私钥,把 d 拿去当公钥也可行。

公钥可以分发给所有人,私钥必须保存好。别人可以用公钥加密信息发给我,我用私钥可以解密这条信息。下面来讨论加密、解密算法。

假设 Alice 想给 Bob 发一条信息,于是搞到了 Bob 的公钥(N,e). 设 m 为明文,则我们这样计算出密文:

image-20220521201832730

Bob 收到密文 c 之后,用自己手上的私钥解密:

image-20220521201900129

为什么这样就可以恢复出明文 m?

注意到

image-20220521201918652

而 e,d 在模φ(N)意义下互为逆元,故

image-20220521202025602

从而

RSA原理和实现

RSA算法的发明者是:Ron Rivest、Adi Shamir、Leonard Adleman,算法提出于1977年。

  RSA是一种非对称加密算法,是因为加密和解密使用的是不同的密钥。RSA算法利用公钥来加密,用私钥来解密;公钥可以随意公布,任何人都可以利用公钥来加密一条信息,只有私钥持有者才能对信息解密。

  RSA另有一个特点:用私钥加密的信息,用公钥可以解密。这让基于RSA的数字签名成为可能。假如我写了一篇文章,我可以用私钥加密这一篇文本的md5值,把密文(也就是签名)附在文章后面。读者从可信的渠道得到我的公钥,利用公钥解密签名,发现与这篇文章的md5值一致,于是可以确信这篇文章确实是我写的。

  RSA是极其巧妙的算法,而它的数学基础却出奇地简单,代码实现也不复杂。RSA同时是一个难以破解的密码体系,破解RSA的速度取决于大整数分解的速度

RSA的数学基础

RSA的数学基础是一个数论函数(定义域为正整数集,值域为复数集),定义是:φ(n) 为 {1,2,3,⋯,n} 中与 n 互质的数的个数。

  举几个例子:φ(6) = 2,因为6以内与6互质的数只有1和5. 而 φ(10) = 4 ,因为10以内与10互质的数有1、3、7、9,共4个。另外,不难发现,对于质数p,有 φ(p) = p−1.

  欧拉函数的通项公式是什么?先给出结论,再谈谈是如何找到这个结论的。

image-20220521193809617

想求出φ(n),那就得统计出 n 以内有多少个与 n 互质的数。那么,与 n 互质的数 r 有什么特点呢?

关键在于: n 拥有的质因子, r 全都没有【质因子素因数质因数)在数论里是指能整除给定正整数的质数【指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数】。除了1以外,两个没有其他共同质因子的正整数称为互质。因为 1 没有质因子,1与任何正整数(包括1本身)都是互质。正整数的因数分解可将正整数表示为一连串的质因子相乘,质因子如重复可以用指数表示。根据算术基本定理,任何正整数皆有独一无二的质因子分解式只有一个质因子的正整数为质数。】。

每个合数都可以写成几个质数(也可称为素数)相乘的形式,这几个质数就都叫做这个合数的质因数。如果一个质数是某个数的因数,那么就说这个质数是这个数的质因数;而这个因数一定是一个质数。

换句话讲:假设 n 的质因数分解为

image-20220521201015521

则 r 必然满足 r % p1 = 0, r % p2 =0, ⋯既然如此,研究 r 模掉 n 的质因子们所得到的结果,就可以看出 r 的一些性质。构造一个函数:

image-20220521201158979

有性质如下:

  r 与 n 互质,当且仅当 G(r) 生成的元组中,对于第 i 位,总有它不能被 pi 整除。我们把这种元组称为合法元组。

  这是一条很平凡的性质,但它可以成为计数的一把钥匙。直接统计 n 以内与 n 互质的数是困难的,但统计有多少个合法元组则简单得多。合法元组的第 i 位不能是 pi 的倍数或0,那么这一位有 piki⋅(1−1pi) 种取值,故依据乘法原理,合法元组的总数是:

image-20220521201233156

  这个式子要成为欧拉函数的通项公式,还有最后一步路要走:G目前只是一个普通映射,既有可能多个 r 对应同一个合法元组,也可能一个合法元组对应不了任何 r. 从而合法元组的数量未必恰好就是与 n 互质的数的数量。接下来的任务是:证明 元组G:{1,2,3,⋯,n}→(元组) 是一个双射。

  还记得中国剩余定理(CRT) 吗?它通过一个精妙的构造,指出了 元组(元组)→{1,2,3,⋯,n}是一个双射(当然了,这个结论只是CRT的一个副产品)。中国剩余定理在这里不再赘述,感兴趣的读者可以去查阅相关资料。总之,既然元组与 n 以内的整数是一一对应的,那么合法元组的数量就是 n 以内与 n 互质的数的个数。从而我们得到了欧拉函数的通项公式:

image-20220521201320214

  现在我们手上有欧拉函数的通项公式了。可以随便挑几个数验证一下:

image-20220521201344636

  利用欧拉函数的通项公式,可以得到两条性质:

φ 是积性函数。若 a,b 互质,则有

image-20220521201414883

欧拉定理。若 a,m 互质,则有

image-20220521201448217

不难发现,费马小定理是欧拉定理在 m 为质数下的情形。

  以上这堆性质的证明、欧拉函数通项的求法、线性筛欧拉函数、扩展欧几里得算法,是接下来代码实现的基础。

  欧拉定理是欧拉函数最大的作用。利用欧拉定理可以得到结论:若a,m互质,则

image-20220521201719854

这不仅大大降低了计算复杂度(可以利用快速幂算法),也是 RSA 算法的基石。

RSA算法

RSA算法的过程如下:

  1. 随便选择两个极大的质数,分别记为 p,q. 当然这里 p ≠ q.
  2. N = p ⋅ q, 则由欧拉函数的积性有 φ(N) = (p − 1)(q − 1).
  3. 选取一个小于 N 的 e, 使得 gcd(e, N) = 1. 求出 e 在模 φ(N) 意义下的逆元,记为d.
  4. 我们得到了 (N, e) 作为公钥, (N, d) 作为私钥。

注意到 e 和 d 是对称的,把 e 拿去当私钥,把 d 拿去当公钥也可行。

公钥可以分发给所有人,私钥必须保存好。别人可以用公钥加密信息发给我,我用私钥可以解密这条信息。下面来讨论加密、解密算法。

假设 Alice 想给 Bob 发一条信息,于是搞到了 Bob 的公钥(N,e). 设 m 为明文,则我们这样计算出密文:

image-20220521201832730

Bob 收到密文 c 之后,用自己手上的私钥解密:

image-20220521201900129

为什么这样就可以恢复出明文 m?

注意到

image-20220521201918652

而 e,d 在模φ(N)意义下互为逆元,故

image-20220521202025602

从而

image-20220521202039051

代码实现RSA加密、解密异常简单,只需要会扩展欧几里得算法、快速幂就行了。如下:

def exgcd(a, b):                   # 扩展欧几里得算法
    if b==0: return (1, 0)
    x, y = exgcd(b, a%b)
    return (y, x-a//b*y)

def getInv(a, mod):                # exgcd求逆元
    x, y = exgcd(a, mod)
    return x
    # Python下负数模正数得到正数。如果是其他语言,得返回 (x % mod + mod) % mod

def quickPow(x, p, mod):           # 模 mod 意义下求 x 的 p 次方
    if p==0: return 1
    if p==1: return x

    t = quickPow(x, p//2, mod)
    if p%2 == 0: return t * t % mod
    return t * t * x % mod

def encrypt(m, N, e):           # 加密
    return quickPow(m, e, N)

def decrypt(c, N, d):           # 解密
    return quickPow(c, d, N)

# 拿 bsdgames 里面的 `primes` 程序随便造的 p, q
p = 100088459
q = 1000049473

N = p * q
phi = (p-1) * (q-1)
e = 2333333
d = getInv(e, phi)

c = encrypt(123454321, N, e)
print(c) # 加密 `123454321`,得到密文:`8922052926595796`

m = decrypt(c, N, d)
print(m) # 解密,得到明文 `123454321`

RSA的安全性

  RSA是一个简单的算法,(主流的)攻击思路也不复杂:解密是需要私钥 (N,d) 的,那么目标就是取得 d;由于d是 e 在模 φ(N) 意义下的逆元,故只需要知道 φ(N) 就可以由公钥推出私钥;而要想知道 φ(N) 是多少,只需要质因数分解N——因此,一般认为,RSA的安全性取决于分解N的难度。

  目前,分解质因数是极端困难的。人们没有找到多项式时间内分解质因数的做法。而生成一个质数却很简单——我们拥有判断一个数是否为质数的快速算法(例如Miller-Rabin算法),又有质数定理: Π(n)∼n/ln⁡n 保证了 n 附近期望每 ln⁡n 个数里面就有一个质数。想找到适合RSA的大质数 p,q,只需要随机生成很大的数,然后检验是不是质数,重复几次就可以达到目标。

  尽管现在没有多项式时间内分解质因数的算法,但有些算法还是比较快的——例如 Pollard's rho. 此外,量子计算机上可以实现Shor算法,但目前没有多大的应用价值。推荐使用不低于2048位的密钥,这里的密钥长度是指模数 N 的二进制长度。

生成RSA密钥对

ssh-keygen 程序可以造一个RSA密钥对,接下来这个密钥对可以用于git身份验证、ssh服务器身份验证等很多功能。

img生成一个RSA密钥对

  接下来,我们来看这里面有什么东西。 hello 是私钥文件, hello.pub 是公钥文件。里面的东西都是base64编码过的,可以用RsaCtfTool来查看。

img公钥文件

img私钥文件

几道CTF题

Jarvis OJ: veryeasyRSA

已知RSA公钥生成参数:

p = 3487583947589437589237958723892346254777

q = 8767867843568934765983476584376578389

e = 65537, 求d

In [2]: p = 3487583947589437589237958723892346254777

In [3]: q = 8767867843568934765983476584376578389

In [4]: e = 65537

In [5]: phi = (p-1) * (q-1)

In [6]: gmpy2.invert(e, phi)
Out[6]: mpz(19178568796155560423675975774142829153827883709027717723363077606260717434369)

JarvisOJ: Easy RSA

还记得veryeasy RSA吗?是不是不难?那继续来看看这题吧,这题也不难。 已知一段RSA加密的信息为:0xdc2eeeb2782c 已知加密所用的公钥:(N=322831561921859 e = 23) 请解密出明文,提交时请将数字转化为ascii码提交

In [2]: c = 0xdc2eeeb2782c

In [3]: n = 322831561921859

In [4]: e = 23

In [7]: !factor {n}
322831561921859: 13574881 23781539

In [8]: p = 13574881; q = 23781539

In [9]: phi = (p-1)*(q-1)

In [10]: d = gmpy2.invert(e, phi)

In [14]: hex(gmpy2.powmod(c, d, n))
Out[14]: '0x33613559'

In [16]: codecs.decode('33613559', 'hex')
Out[16]: b'3a5Y'

BUUCTF: RSA

在一次RSA密钥对生成中,假设p=473398607161,q=4511491,e=17 求解出d作为flag提交

In [21]: p = 473398607161

In [22]: q = 473398607161

In [23]: e = 17

In [24]: gmpy2.invert(e, (p-1)*(q-1))
Out[24]: mpz(13182720074178117839153)

BUUCTF: rsarsa

Math is cool! Use the RSA algorithm to decode the secret message, c, p, q, and e are parameters for the RSA algorithm. (给定了 p, q, e, c) Use RSA to find the secret message

In [25]: p =  96484230290105156765905517400104265349457376392357398006439893520398525072984913995610350091634    ...: 27050370107570733633350911691280297777160200625281665378483

In [26]: q =  11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732    ...: 443992230647903887510065547947313543299303261986053486569407

In [27]: e =  65537

In [28]: c =  83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605    ...: 6473166465764865262174570063768422808697285817267464015837058999417682141387422596893348407356335530    ...: 5388764184765117377625182029308721288567018036740680740676592363897316137581739273774783276275169010    ...: 4423869019034

In [29]: d = gmpy2.invert(e, (p-1)*(q-1))

In [30]: gmpy2.powmod(c, d, p*q)
Out[30]: mpz(5577446633554466577768879988)

Java实现RSA

<!--引入依赖-->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>annotations-api</artifactId>
    <version>6.0.53</version>
    <scope>provided</scope>
</dependency>
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class TestRSA {
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    /**
     * 获取密钥对
     *
     * @return 密钥对
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(1024);
        return generator.generateKeyPair();
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     * @return
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     * @return
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * RSA加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
        // 加密后的字符串
        return new String(Base64.encodeBase64String(encryptedData));
    }

    /**
     * RSA解密
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     * @return
     */
    public static String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // 解密后的内容
        return new String(decryptedData, "UTF-8");
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     * @return 签名
     */
    public static String sign(String data, PrivateKey privateKey) throws Exception {
        byte[] keyBytes = privateKey.getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(key);
        signature.update(data.getBytes());
        return new String(Base64.encodeBase64(signature.sign()));
    }

    /**
     * 验签
     *
     * @param srcData   原始字符串
     * @param publicKey 公钥
     * @param sign      签名
     * @return 是否验签通过
     */
    public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

    public static void main(String[] args) {
        try {
            // 生成密钥对
            KeyPair keyPair = getKeyPair();
            String privateKey = new String(Base64.encodeBase64(keyPair.getPrivate().getEncoded()));
            String publicKey = new String(Base64.encodeBase64(keyPair.getPublic().getEncoded()));
            System.out.println("私钥:" + privateKey);
            System.out.println("公钥:" + publicKey);
            // RSA加密
            String data = "待加密的文字内容";
            String encryptData = encrypt(data, getPublicKey(publicKey));
            System.out.println("加密后内容:" + encryptData);
            // RSA解密
            String decryptData = decrypt(encryptData, getPrivateKey(privateKey));
            System.out.println("解密后内容:" + decryptData);

            // RSA签名
            String sign = sign(data, getPrivateKey(privateKey));
            // RSA验签
            boolean result = verify(data, getPublicKey(publicKey), sign);
            System.out.print("验签结果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.print("加解密异常");
        }
    }
}

Go 实现 RSA

公钥与私钥

  公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。

  公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过openssl工具:  

    # 生成一个私钥,推荐使用1024位的秘钥,秘钥以pem格式保存到-out参数指定的文件中,采用PKCS1格式
    openssl genrsa -out rsa.pem 1024 
    # 生成与私钥对应的公钥,生成的是Subject Public Key,一般配合PKCS8格式私钥使用
    openssl rsa -in rsa.pem -pubout -out rsa.pub  

  RSA生成公钥与私钥一般有两种格式:PKCS1和PKCS8,上面的命令生成的秘钥是PKCS1格式的,而公钥是Subject Public Key,一般配合PKCS8格式私钥使用,所以就可能会涉及到PKCS1和PKCS8之间的转换:

    # PKCS1格式私钥转换为PKCS8格式私钥,私钥直接输出到-out参数指定的文件中
    openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
    # PKCS8格式私钥转换为PKCS1格式私钥,私钥直接输出到-out参数指定的文件中
    openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem

    # PKCS1格式公钥转换为PKCS8格式公钥,转换后的内容直接输出
    openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
    # PKCS8格式公钥转换为PKCS1格式公钥,转换后的内容直接输出
    openssl rsa -RSAPublicKey_in -pubout -in rsa.pub

Golang实现

  为了方便读取pem、crt、pfx文件中的公私和私钥,这里我使用了第三方的包:golang.org/x/crypto/pkcs12,可以使用go get安装:go get -u golang.org/x/crypto/pkcs12

  安装之后,封装一个工具包rsautil.go:

package rsautil

import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/asn1"
    "encoding/pem"
    "fmt"
    "golang.org/x/crypto/pkcs12"
    "io"
    "io/ioutil"
    "os"
    "strconv"
)

//从Pem文件中读取秘钥
func ReadFromPem(pemFile string) ([]byte, error) {
    buffer, err := ioutil.ReadFile(pemFile)
    if err != nil {
        return nil, err
    }

    block, _ := pem.Decode(buffer)
    return block.Bytes, nil
}

//从pfx文件中读取公私密钥(需要安装golang.org/x/crypto/pkcs12)
func ReadFromPfx(pfxFile, password string, usePKCS8 bool) ([]byte, []byte) {
    buffer, err := ioutil.ReadFile(pfxFile)
    if err != nil {
        panic(err)
    }

    privateKeyInterface, certificate, err := pkcs12.Decode(buffer, password)
    if err != nil {
        panic(err)
    }

    privateKey := privateKeyInterface.(*rsa.PrivateKey)
    publicKey := certificate.PublicKey.(*rsa.PublicKey)

    var (
        privateKeyBuffer []byte
        publicKeyBuffer  []byte
    )
    if usePKCS8 {
        privateKeyBuffer, err = x509.MarshalPKCS8PrivateKey(privateKey)
        if err != nil {
            panic(err)
        }
        publicKeyBuffer, err = x509.MarshalPKIXPublicKey(publicKey)
        if err != nil {
            panic(err)
        }
    } else {
        privateKeyBuffer = x509.MarshalPKCS1PrivateKey(privateKey)
        publicKeyBuffer = x509.MarshalPKCS1PublicKey(publicKey)
    }
    return publicKeyBuffer, privateKeyBuffer
}

//从crt中读取公钥
func ReadPublicKeyFromCrt(crtFile string, usePKCS8 bool) ([]byte, error) {
    buffer, err := ioutil.ReadFile(crtFile)
    if err != nil {
        return nil, err
    }
    certDERBlock, _ := pem.Decode(buffer)
    certificate, err := x509.ParseCertificate(certDERBlock.Bytes)
    if err != nil {
        return nil, err
    }
    publicKey := certificate.PublicKey.(*rsa.PublicKey)

    var publicKeyBuffer []byte
    if usePKCS8 {
        publicKeyBuffer, err = x509.MarshalPKIXPublicKey(publicKey)
    } else {
        publicKeyBuffer = x509.MarshalPKCS1PublicKey(publicKey)
    }
    if err != nil {
        return nil, err
    }
    return publicKeyBuffer, nil
}

//将秘钥写入Pem文件
func WriteToPem(isPrivateKey bool, buffer []byte, pemFile string) error {
    var _type string
    if isPrivateKey {
        _type = "RSA PRIVATE KEY"
    } else {
        _type = "RSA PUBLIC KEY"
    }

    block := &pem.Block{
        Type:  _type, //这个字符串随便写
        Bytes: buffer,
    }

    file, err := os.Create(pemFile)
    if err != nil {
        return err
    }
    return pem.Encode(file, block)
}

//Pkcs1转换为Pkcs8
func Pkcs1ToPkcs8(isPrivateKey bool, buffer []byte) []byte {
    var (
        oid  = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
        info interface{}
    )
    if isPrivateKey {
        val := struct {
            Version    int
            Algo       []asn1.ObjectIdentifier
            PrivateKey []byte
        }{}
        val.Version = 0
        val.Algo = []asn1.ObjectIdentifier{oid}
        val.PrivateKey = buffer
        info = val
    } else {
        val := struct {
            Algo      pkix.AlgorithmIdentifier
            BitString asn1.BitString
        }{}
        val.Algo.Algorithm = oid
        val.Algo.Parameters = asn1.NullRawValue
        val.BitString.Bytes = buffer
        val.BitString.BitLength = 8 * len(buffer)
        info = val
    }

    b, err := asn1.Marshal(info)
    if err != nil {
        panic(err)
    }
    return b
}

//Pkcs8转换为Pkcs1
func Pkcs8ToPkcs1(isPrivateKey bool, buffer []byte) []byte {
    if isPrivateKey {
        val := struct {
            Version    int
            Algo       pkix.AlgorithmIdentifier
            PrivateKey []byte
        }{}
        _, err := asn1.Unmarshal(buffer, &val)
        if err != nil {
            panic(err)
        }
        return val.PrivateKey
    } else {
        val := struct {
            Algo      pkix.AlgorithmIdentifier
            BitString asn1.BitString
        }{}

        _, err := asn1.Unmarshal(buffer, &val)
        if err != nil {
            panic(err)
        }
        return val.BitString.Bytes
    }
}

//生成公私钥
//usePKCS8:是否使用pkcs8
func GenerateRsaKey(usePKCS8 bool) ([]byte, []byte) {
    //生成私钥
    privateKey, err := rsa.GenerateKey(rand.Reader, 1024) //1024位
    if err != nil {
        panic(err)
    }
    //公钥
    publicKey := privateKey.PublicKey

    var (
        privateKeyBuffer []byte
        publicKeyBuffer  []byte
    )

    if usePKCS8 {
        privateKeyBuffer, err = x509.MarshalPKCS8PrivateKey(privateKey)
        if err != nil {
            panic(err)
        }
        publicKeyBuffer, err = x509.MarshalPKIXPublicKey(&publicKey)
        if err != nil {
            panic(err)
        }
    } else {
        privateKeyBuffer = x509.MarshalPKCS1PrivateKey(privateKey)
        publicKeyBuffer = x509.MarshalPKCS1PublicKey(&publicKey)
    }

    return publicKeyBuffer, privateKeyBuffer
}

func parsePkcsKey(buffer []byte, isPrivateKey, usePKCS8 bool) (interface{}, error) {
    var (
        err          error
        keyInterface interface{}
    )

    if isPrivateKey {
        if usePKCS8 {
            keyInterface, err = x509.ParsePKCS8PrivateKey(buffer)
        } else {
            keyInterface, err = x509.ParsePKCS1PrivateKey(buffer)
        }
    } else {
        if usePKCS8 {
            keyInterface, err = x509.ParsePKIXPublicKey(buffer)
        } else {
            keyInterface, err = x509.ParsePKCS1PublicKey(buffer)
        }
    }
    if err != nil {
        return nil, err
    }
    return keyInterface, nil
}

//RSA加密
func RsaEncrypt(value string, publicKey []byte, usePKCS8 bool) (string, error) {
    keyInterface, err := parsePkcsKey(publicKey, false, usePKCS8)
    if err != nil {
        return "", err
    }
    rsaPublicKey := keyInterface.(*rsa.PublicKey)
    buffer, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, []byte(value))
    if err != nil {
        return "", err
    }

    //以hex格式数值输出
    encryptText := fmt.Sprintf("%x", buffer)
    return encryptText, nil
}

//RSA解密
func RsaDecrypt(value string, privateKey []byte, usePKCS8 bool) (string, error) {
    //将hex格式数据转换为byte切片
    valueBytes := []byte(value)
    var buffer = make([]byte, len(valueBytes)/2)
    for i := 0; i < len(buffer); i++ {
        b, err := strconv.ParseInt(value[i*2:i*2+2], 16, 10)
        if err != nil {
            return "", err
        }
        buffer[i] = byte(b)
    }

    keyInterface, err := parsePkcsKey(privateKey, true, usePKCS8)
    if err != nil {
        return "", err
    }
    key := keyInterface.(*rsa.PrivateKey)
    buffer, err = rsa.DecryptPKCS1v15(rand.Reader, key, buffer)
    return string(buffer), nil
}

//RSA签名
func Sign(value string, privateKey []byte, hash crypto.Hash, usePKCS8 bool) (string, error) {
    keyInterface, err := parsePkcsKey(privateKey, true, usePKCS8)
    if err != nil {
        return "", err
    }
    key := keyInterface.(*rsa.PrivateKey)

    var _hash = hash.New()
    if _, err := io.WriteString(_hash, value); err != nil {
        return "", err
    }

    hashed := _hash.Sum(nil)
    result, err := rsa.SignPKCS1v15(rand.Reader, key, hash, hashed)
    if err != nil {
        return "", err
    }

    //以hex格式数值输出
    encryptText := fmt.Sprintf("%x", result)
    return encryptText, nil
}

//RSA验证签名
func Verify(value string, publicKey []byte, signature string, hash crypto.Hash, usePKCS8 bool) error {
    //将hex格式数据转换为byte切片
    valueBytes := []byte(signature)
    var buffer = make([]byte, len(valueBytes)/2)
    for i := 0; i < len(buffer); i++ {
        b, err := strconv.ParseInt(signature[i*2:i*2+2], 16, 10)
        if err != nil {
            return err
        }
        buffer[i] = byte(b)
    }

    keyInterface, err := parsePkcsKey(publicKey, false, usePKCS8)
    if err != nil {
        return err
    }

    key := keyInterface.(*rsa.PublicKey)

    var _hash = hash.New()
    if _, err := io.WriteString(_hash, value); err != nil {
        return err
    }

    hashed := _hash.Sum(nil)
    return rsa.VerifyPKCS1v15(key, hash, hashed, buffer)
}

  可以使用生成RSA的公私秘钥:

    //生成Rsa
    publicKey, privateKey := rsautil.GenerateRsaKey(usePKCS8)

  生成秘钥后,需要保存,一般保存到pem文件中:  

    //保存到Pem文件,filePath是文件目录
    rsautil.WriteToPem(false, publicKey, filepath.Join(filePath, "rsa.pub"))
    rsautil.WriteToPem(true, privateKey, filepath.Join(filePath, "rsa.pem"))

  从pem文件中读取:  

    //从Pem文件读取秘钥,filePath是文件目录
    publicKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pub"))
    privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pem"))

  还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:

    //从crt文件中读取公钥,filePath是文件目录
    publicKey, _ := rsautil.ReadPublicKeyFromCrt(filepath.Join(filePath, "demo.crt"), usePKCS8)
    privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "demo.key"))

  pfx文件中包含了公钥和私钥,可以很方便就读取到:

    //从pfx文件中读取秘钥,filePath是文件目录
    publicKey, privateKey := rsautil.ReadFromPfx(filepath.Join(filePath, "demo.pfx"), "123456", usePKCS8)

  有时候我们还可能需要进行秘钥的转换:  

    //Pkcs8格式公钥转换为Pkcs1格式公钥
    publicKey = rsautil.Pkcs8ToPkcs1(false, publicKey)
    // Pkcs8格式私钥转换为Pkcs1格式私钥
    privateKey = rsautil.Pkcs8ToPkcs1(true, privateKey)
    // Pkcs1格式公钥转换为Pkcs8格式公钥
    publicKey = rsautil.Pkcs1ToPkcs8(false, publicKey)
    // Pkcs1格式私钥转换为Pkcs8格式私钥
    privateKey = rsautil.Pkcs1ToPkcs8(true, privateKey)

  有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:  

    encryptText, _ := rsautil.RsaEncrypt(text, publicKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】加密后:%s\n", text, encryptText)

    decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】解密后:%s\n", encryptText, decryptText)

    signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8)
    fmt.Printf("【%s】经过【RSA】签名后:%s\n", text, signature)

    result := rsautil.Verify(text, publicKey, signature, crypto.MD5, usePKCS8) == nil
    fmt.Printf("【%s】的签名【%s】经过【RSA】验证后结果是:"+strconv.FormatBool(result), text, signature)

  完整的demo代码:

package main

import (
    "crypto"
    "demo/rsautil"
    "fmt"
    "os"
    "path/filepath"
    "strconv"
)

func main() {
    text := "上山打老虎"
    usePKCS8 := true // usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥
    path, _ := os.Executable()
    filePath := filepath.Dir(path)
    fmt.Printf("文件路径:%s\n", filePath) // 存放pem,crt,pfx等文件的目录

    //生成Rsa
    publicKey, privateKey := rsautil.GenerateRsaKey(usePKCS8)
    //从Pem文件读取秘钥,filePath是文件目录
    //publicKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pub"))
    //privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pem"))
    //从pfx文件中读取秘钥,filePath是文件目录
    //publicKey, privateKey := rsautil.ReadFromPfx(filepath.Join(filePath, "demo.pfx"), "123456", usePKCS8)
    //从crt文件中读取公钥,filePath是文件目录
    //publicKey, _ := rsautil.ReadPublicKeyFromCrt(filepath.Join(filePath, "demo.crt"), usePKCS8)
    //privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "demo.key"))

    //保存到Pem文件,filePath是文件目录
    rsautil.WriteToPem(false, publicKey, filepath.Join(filePath, "rsa.pub"))
    rsautil.WriteToPem(true, privateKey, filepath.Join(filePath, "rsa.pem"))

    //Pkcs8格式公钥转换为Pkcs1格式公钥
    publicKey = rsautil.Pkcs8ToPkcs1(false, publicKey)
    // Pkcs8格式私钥转换为Pkcs1格式私钥
    privateKey = rsautil.Pkcs8ToPkcs1(true, privateKey)
    // Pkcs1格式公钥转换为Pkcs8格式公钥
    publicKey = rsautil.Pkcs1ToPkcs8(false, publicKey)
    // Pkcs1格式私钥转换为Pkcs8格式私钥
    privateKey = rsautil.Pkcs1ToPkcs8(true, privateKey)

    encryptText, _ := rsautil.RsaEncrypt(text, publicKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】加密后:%s\n", text, encryptText)

    decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】解密后:%s\n", encryptText, decryptText)

    signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8)
    fmt.Printf("【%s】经过【RSA】签名后:%s\n", text, signature)

    result := rsautil.Verify(text, publicKey, signature, crypto.MD5, usePKCS8) == nil
    fmt.Printf("【%s】的签名【%s】经过【RSA】验证后结果是:"+strconv.FormatBool(result), text, signature)
}

Python 实现 RSA

首先使用pip两个第三方包:  

# 用于从crt、pfx等文件读取公私秘钥
pip install pyOpenSSL
# 用RSA加密解密签名验证等(如果安装不了,先卸载旧版本再安装:pip uninstall pycrypto)
pip install pycryptodome
# 需要安装OpenSSL包:pip install pyOpenSSL
# 需要安装pycrypto包:pip install pycryptodome
from Crypto import Random, Hash
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_v1_5_Cipher
from Crypto.Signature import PKCS1_v1_5 as PKCS1_v1_5_Signature
from Crypto.IO import PEM, PKCS8
from OpenSSL import crypto
from Crypto.Util.asn1 import (DerSequence, DerBitString, DerObjectId, DerNull)

# pkcs1格式转换为pkcs8
def pkcs1_to_pkcs8(buffer):
    rsakey = RSA.importKey(buffer)
    if rsakey.has_private():
        return PKCS8.wrap(buffer, RSA.oid, None)
    return rsakey.exportKey("DER")

# pkcs8格式转换为pkcs1
def pkcs8_to_pkcs1(buffer):
    rsakey = RSA.importKey(buffer)
    if rsakey.has_private():  # 私钥
        return PKCS8.unwrap(buffer, None)[1]

    spki = DerSequence().decode(buffer, nr_elements=2)
    algo = DerSequence().decode(spki[0], nr_elements=(1, 2))
    algo_oid = DerObjectId().decode(algo[0])
    spk = DerBitString().decode(spki[1]).value

    if len(algo) == 1:
        algo_params = None
    else:
        try:
            DerNull().decode(algo[1])
            algo_params = None
        except:
            algo_params = algo[1]

    if algo_oid.value != RSA.oid or algo_params is not None:
        raise ValueError("No RSA subjectPublicKeyInfo")
    return spk

# 生成RSA加密的公私密钥
def generate_rsa_key(use_pkcs8=False):
    rsa_key = RSA.generate(1024, Random.new().read)  # 1024位
    private_key = rsa_key.exportKey("DER", pkcs=1)
    public_key = rsa_key.publickey().exportKey("DER")  # subject_public_key
    if use_pkcs8:
        private_key = pkcs1_to_pkcs8(private_key)
    else:
        public_key = pkcs8_to_pkcs1(public_key)

    return public_key, private_key

# 从pem文件中读取秘钥
def read_from_pem(pen_file_ame):
    with open(pen_file_ame, 'r') as f:
        t = PEM.decode(f.read())
        if t[2]:
            raise ValueError("fail to read pem")
        return t[0]

# 将秘钥写入pem文件
def write_to_pem(buffer, is_private_key, pen_file_ame):
    if is_private_key:
        str = PEM.encode(buffer, "RSA PRIVATE KEY")
    else:
        str = PEM.encode(buffer, "RSA PUBLIC KEY")

    with open(pen_file_ame, 'w') as f:
        f.write(str)

# 从crt文件中读取公钥
def read_public_key_from_crt(crt_file_name):
    with open(crt_file_name, 'rb') as f:
        cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
        return crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey())

# 从pfx文件中读取公私钥
def read_from_pfx(pfx_file_name, password):
    with open(pfx_file_name, 'rb') as f:
        pfx = crypto.load_pkcs12(f.read(), bytes(password, encoding="utf-8"))
        cert = pfx.get_certificate()
        _public_key = crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey())
        _privat_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pfx.get_privatekey())
        return _public_key, _privat_key

# RSA使用公钥加密
def rsa_encrypt(value, public_key):
    rsakey = RSA.importKey(public_key)
    cipher = PKCS1_v1_5_Cipher.new(rsakey)
    buffer = cipher.encrypt(value.encode("utf-8"))
    return buffer.hex()  # 使用hex格式输出

# RSA使用私钥解密
def rsa_decrypt(value, private_key):
    rsakey = RSA.importKey(private_key)
    cipher = PKCS1_v1_5_Cipher.new(rsakey)
    buffer = bytes.fromhex(value)  # 读取hex格式数据
    buffer = cipher.decrypt(buffer, Random.new().read)
    return buffer.decode("utf-8")

# RSA使用私钥签名
def sign(value, private_ey, halg=Hash.MD5):
    rsakey = RSA.importKey(private_ey)
    signer = PKCS1_v1_5_Signature.new(rsakey)

    hash = halg.new()
    hash.update(value.encode("utf-8"))
    buffer = signer.sign(hash)
    return buffer.hex()  # 使用hex格式输出

# RSA使用公钥验证签名
def verify(value, public_key, signature, halg=Hash.MD5):
    rsakey = RSA.importKey(public_key)
    verifier = PKCS1_v1_5_Signature.new(rsakey)

    hash = halg.new()
    hash.update(value.encode("utf-8"))

    buffer = bytes.fromhex(signature)  # 读取hex格式数据
    result: bool = verifier.verify(hash, buffer)
    return result

  生成RSA的公私秘钥:  

# 生成rsa公私秘钥
(publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8)

  生成秘钥后,需要保存,一般保存到pem文件中:  

# 将公私秘钥写入pem文件,filePath是文件目录
RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub"))
RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem"))

  可以保存到pem文件中,当然也可以从pem文件中读取了:  

# 从pem文件中读取秘钥,filePath是文件目录
publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub"))
privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem"))

  还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:

# 从crt文件读取,filePath是文件目录
publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt"))
privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key"))

  pfx文件中包含了公钥和私钥,可以很方便就读取到:

# 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
(publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456")

  有时候我们还可能需要进行秘钥的转换:

# Pkcs8格式公钥转换为Pkcs1格式公钥
publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey)
# Pkcs8格式私钥转换为Pkcs1格式私钥
privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey)
# Pkcs1格式公钥转换为Pkcs8格式公钥
publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey)
# Pkcs1格式私钥转换为Pkcs8格式私钥
privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey)

  有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:

encryptText = RsaUtil.rsa_encrypt(text, publicKey)
print("【", text, "】经过【RSA】加密后:", encryptText)

decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey)
print("【", encryptText, "】经过【RSA】解密后:", decryptText)

signature = RsaUtil.sign(text, privateKey, Hash.MD5)
print("【", text, "】经过【RSA】签名后:", signature)

result = RsaUtil.verify(text, publicKey, signature, Hash.MD5)
print("【", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)

  完整的demo代码:

import RsaUtil
from Crypto import Hash
import os
​
text = "上山打老虎"
use_pkcs8 = True
filePath = os.getcwd()
print("文件路径:", filePath)
​
# 生成rsa公私秘钥
(publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8)
# 从pem文件中读取秘钥,filePath是文件目录
# publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub"))
# privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem"))
# 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
# (publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456")
# 从crt文件读取,filePath是文件目录
# publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt"))
# privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key"))
​
# 将公私秘钥写入pem文件,filePath是文件目录
RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub"))
RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem"))
​
# Pkcs8格式公钥转换为Pkcs1格式公钥
publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey)
# Pkcs8格式私钥转换为Pkcs1格式私钥
privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey)
# Pkcs1格式公钥转换为Pkcs8格式公钥
publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey)
# Pkcs1格式私钥转换为Pkcs8格式私钥
privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey)encryptText = RsaUtil.rsa_encrypt(text, publicKey)
print("【", text, "】经过【RSA】加密后:", encryptText)decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey)
print("【", encryptText, "】经过【RSA】解密后:", decryptText)signature = RsaUtil.sign(text, privateKey, Hash.MD5)
print("【", text, "】经过【RSA】签名后:", signature)result = RsaUtil.verify(text, publicKey, signature, Hash.MD5)
print("【", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)

代码实现RSA加密、解密异常简单,只需要会扩展欧几里得算法、快速幂就行了。如下:

def exgcd(a, b):                   # 扩展欧几里得算法
    if b==0: return (1, 0)
    x, y = exgcd(b, a%b)
    return (y, x-a//b*y)

def getInv(a, mod):                # exgcd求逆元
    x, y = exgcd(a, mod)
    return x
    # Python下负数模正数得到正数。如果是其他语言,得返回 (x % mod + mod) % mod

def quickPow(x, p, mod):           # 模 mod 意义下求 x 的 p 次方
    if p==0: return 1
    if p==1: return x

    t = quickPow(x, p//2, mod)
    if p%2 == 0: return t * t % mod
    return t * t * x % mod

def encrypt(m, N, e):           # 加密
    return quickPow(m, e, N)

def decrypt(c, N, d):           # 解密
    return quickPow(c, d, N)

# 拿 bsdgames 里面的 `primes` 程序随便造的 p, q
p = 100088459
q = 1000049473

N = p * q
phi = (p-1) * (q-1)
e = 2333333
d = getInv(e, phi)

c = encrypt(123454321, N, e)
print(c) # 加密 `123454321`,得到密文:`8922052926595796`

m = decrypt(c, N, d)
print(m) # 解密,得到明文 `123454321`

RSA的安全性

  RSA是一个简单的算法,(主流的)攻击思路也不复杂:解密是需要私钥 (N,d) 的,那么目标就是取得 d;由于d是 e 在模 φ(N) 意义下的逆元,故只需要知道 φ(N) 就可以由公钥推出私钥;而要想知道 φ(N) 是多少,只需要质因数分解N——因此,一般认为,RSA的安全性取决于分解N的难度。

  目前,分解质因数是极端困难的。人们没有找到多项式时间内分解质因数的做法。而生成一个质数却很简单——我们拥有判断一个数是否为质数的快速算法(例如Miller-Rabin算法),又有质数定理: Π(n)∼n/ln⁡n 保证了 n 附近期望每 ln⁡n 个数里面就有一个质数。想找到适合RSA的大质数 p,q,只需要随机生成很大的数,然后检验是不是质数,重复几次就可以达到目标。

  尽管现在没有多项式时间内分解质因数的算法,但有些算法还是比较快的——例如 Pollard's rho. 此外,量子计算机上可以实现Shor算法,但目前没有多大的应用价值。推荐使用不低于2048位的密钥,这里的密钥长度是指模数 N 的二进制长度。

生成RSA密钥对

ssh-keygen 程序可以造一个RSA密钥对,接下来这个密钥对可以用于git身份验证、ssh服务器身份验证等很多功能。

img生成一个RSA密钥对

  接下来,我们来看这里面有什么东西。 hello 是私钥文件, hello.pub 是公钥文件。里面的东西都是base64编码过的,可以用RsaCtfTool来查看。

img公钥文件

img私钥文件

几道CTF题

Jarvis OJ: veryeasyRSA

已知RSA公钥生成参数: p = 3487583947589437589237958723892346254777 q = 8767867843568934765983476584376578389 e = 65537, 求d

In [2]: p = 3487583947589437589237958723892346254777

In [3]: q = 8767867843568934765983476584376578389

In [4]: e = 65537

In [5]: phi = (p-1) * (q-1)

In [6]: gmpy2.invert(e, phi)
Out[6]: mpz(19178568796155560423675975774142829153827883709027717723363077606260717434369)

JarvisOJ: Easy RSA

还记得veryeasy RSA吗?是不是不难?那继续来看看这题吧,这题也不难。 已知一段RSA加密的信息为:0xdc2eeeb2782c 已知加密所用的公钥:(N=322831561921859 e = 23) 请解密出明文,提交时请将数字转化为ascii码提交

In [2]: c = 0xdc2eeeb2782c

In [3]: n = 322831561921859

In [4]: e = 23

In [7]: !factor {n}
322831561921859: 13574881 23781539

In [8]: p = 13574881; q = 23781539

In [9]: phi = (p-1)*(q-1)

In [10]: d = gmpy2.invert(e, phi)

In [14]: hex(gmpy2.powmod(c, d, n))
Out[14]: '0x33613559'

In [16]: codecs.decode('33613559', 'hex')
Out[16]: b'3a5Y'

BUUCTF: RSA

在一次RSA密钥对生成中,假设p=473398607161,q=4511491,e=17 求解出d作为flag提交

In [21]: p = 473398607161

In [22]: q = 473398607161

In [23]: e = 17

In [24]: gmpy2.invert(e, (p-1)*(q-1))
Out[24]: mpz(13182720074178117839153)

BUUCTF: rsarsa

Math is cool! Use the RSA algorithm to decode the secret message, c, p, q, and e are parameters for the RSA algorithm. (给定了 p, q, e, c) Use RSA to find the secret message

In [25]: p =  96484230290105156765905517400104265349457376392357398006439893520398525072984913995610350091634    ...: 27050370107570733633350911691280297777160200625281665378483

In [26]: q =  11874843837980297032092405848653656852760910154543380907650040190704283358909208578251063047732    ...: 443992230647903887510065547947313543299303261986053486569407

In [27]: e =  65537

In [28]: c =  83208298995174604174773590298203639360540024871256126892889661345742403314929861939100492666605    ...: 6473166465764865262174570063768422808697285817267464015837058999417682141387422596893348407356335530    ...: 5388764184765117377625182029308721288567018036740680740676592363897316137581739273774783276275169010    ...: 4423869019034

In [29]: d = gmpy2.invert(e, (p-1)*(q-1))

In [30]: gmpy2.powmod(c, d, p*q)
Out[30]: mpz(5577446633554466577768879988)

Java实现RSA

<!--引入依赖-->
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>annotations-api</artifactId>
    <version>6.0.53</version>
    <scope>provided</scope>
</dependency>
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class TestRSA {
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    /**
     * 获取密钥对
     *
     * @return 密钥对
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
        generator.initialize(1024);
        return generator.generateKeyPair();
    }

    /**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     * @return
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     * @return
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.decodeBase64(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     * RSA加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.getBytes().length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
        // 加密后的字符串
        return new String(Base64.encodeBase64String(encryptedData));
    }

    /**
     * RSA解密
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     * @return
     */
    public static String decrypt(String data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dataBytes = Base64.decodeBase64(data);
        int inputLen = dataBytes.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offset > 0) {
            if (inputLen - offset > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
            }
            out.write(cache, 0, cache.length);
            i++;
            offset = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        // 解密后的内容
        return new String(decryptedData, "UTF-8");
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 私钥
     * @return 签名
     */
    public static String sign(String data, PrivateKey privateKey) throws Exception {
        byte[] keyBytes = privateKey.getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(key);
        signature.update(data.getBytes());
        return new String(Base64.encodeBase64(signature.sign()));
    }

    /**
     * 验签
     *
     * @param srcData   原始字符串
     * @param publicKey 公钥
     * @param sign      签名
     * @return 是否验签通过
     */
    public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

    public static void main(String[] args) {
        try {
            // 生成密钥对
            KeyPair keyPair = getKeyPair();
            String privateKey = new String(Base64.encodeBase64(keyPair.getPrivate().getEncoded()));
            String publicKey = new String(Base64.encodeBase64(keyPair.getPublic().getEncoded()));
            System.out.println("私钥:" + privateKey);
            System.out.println("公钥:" + publicKey);
            // RSA加密
            String data = "待加密的文字内容";
            String encryptData = encrypt(data, getPublicKey(publicKey));
            System.out.println("加密后内容:" + encryptData);
            // RSA解密
            String decryptData = decrypt(encryptData, getPrivateKey(privateKey));
            System.out.println("解密后内容:" + decryptData);

            // RSA签名
            String sign = sign(data, getPrivateKey(privateKey));
            // RSA验签
            boolean result = verify(data, getPublicKey(publicKey), sign);
            System.out.print("验签结果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.print("加解密异常");
        }
    }
}

Go 实现 RSA

公钥与私钥

  公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。

  公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过openssl工具:  

    # 生成一个私钥,推荐使用1024位的秘钥,秘钥以pem格式保存到-out参数指定的文件中,采用PKCS1格式
    openssl genrsa -out rsa.pem 1024 
    # 生成与私钥对应的公钥,生成的是Subject Public Key,一般配合PKCS8格式私钥使用
    openssl rsa -in rsa.pem -pubout -out rsa.pub  

  RSA生成公钥与私钥一般有两种格式:PKCS1和PKCS8,上面的命令生成的秘钥是PKCS1格式的,而公钥是Subject Public Key,一般配合PKCS8格式私钥使用,所以就可能会涉及到PKCS1和PKCS8之间的转换:

    # PKCS1格式私钥转换为PKCS8格式私钥,私钥直接输出到-out参数指定的文件中
    openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
    # PKCS8格式私钥转换为PKCS1格式私钥,私钥直接输出到-out参数指定的文件中
    openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem

    # PKCS1格式公钥转换为PKCS8格式公钥,转换后的内容直接输出
    openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
    # PKCS8格式公钥转换为PKCS1格式公钥,转换后的内容直接输出
    openssl rsa -RSAPublicKey_in -pubout -in rsa.pub

Golang实现

  为了方便读取pem、crt、pfx文件中的公私和私钥,这里我使用了第三方的包:golang.org/x/crypto/pkcs12,可以使用go get安装:go get -u golang.org/x/crypto/pkcs12

  安装之后,封装一个工具包rsautil.go:

package rsautil

import (
    "crypto"
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/asn1"
    "encoding/pem"
    "fmt"
    "golang.org/x/crypto/pkcs12"
    "io"
    "io/ioutil"
    "os"
    "strconv"
)

//从Pem文件中读取秘钥
func ReadFromPem(pemFile string) ([]byte, error) {
    buffer, err := ioutil.ReadFile(pemFile)
    if err != nil {
        return nil, err
    }

    block, _ := pem.Decode(buffer)
    return block.Bytes, nil
}

//从pfx文件中读取公私密钥(需要安装golang.org/x/crypto/pkcs12)
func ReadFromPfx(pfxFile, password string, usePKCS8 bool) ([]byte, []byte) {
    buffer, err := ioutil.ReadFile(pfxFile)
    if err != nil {
        panic(err)
    }

    privateKeyInterface, certificate, err := pkcs12.Decode(buffer, password)
    if err != nil {
        panic(err)
    }

    privateKey := privateKeyInterface.(*rsa.PrivateKey)
    publicKey := certificate.PublicKey.(*rsa.PublicKey)

    var (
        privateKeyBuffer []byte
        publicKeyBuffer  []byte
    )
    if usePKCS8 {
        privateKeyBuffer, err = x509.MarshalPKCS8PrivateKey(privateKey)
        if err != nil {
            panic(err)
        }
        publicKeyBuffer, err = x509.MarshalPKIXPublicKey(publicKey)
        if err != nil {
            panic(err)
        }
    } else {
        privateKeyBuffer = x509.MarshalPKCS1PrivateKey(privateKey)
        publicKeyBuffer = x509.MarshalPKCS1PublicKey(publicKey)
    }
    return publicKeyBuffer, privateKeyBuffer
}

//从crt中读取公钥
func ReadPublicKeyFromCrt(crtFile string, usePKCS8 bool) ([]byte, error) {
    buffer, err := ioutil.ReadFile(crtFile)
    if err != nil {
        return nil, err
    }
    certDERBlock, _ := pem.Decode(buffer)
    certificate, err := x509.ParseCertificate(certDERBlock.Bytes)
    if err != nil {
        return nil, err
    }
    publicKey := certificate.PublicKey.(*rsa.PublicKey)

    var publicKeyBuffer []byte
    if usePKCS8 {
        publicKeyBuffer, err = x509.MarshalPKIXPublicKey(publicKey)
    } else {
        publicKeyBuffer = x509.MarshalPKCS1PublicKey(publicKey)
    }
    if err != nil {
        return nil, err
    }
    return publicKeyBuffer, nil
}

//将秘钥写入Pem文件
func WriteToPem(isPrivateKey bool, buffer []byte, pemFile string) error {
    var _type string
    if isPrivateKey {
        _type = "RSA PRIVATE KEY"
    } else {
        _type = "RSA PUBLIC KEY"
    }

    block := &pem.Block{
        Type:  _type, //这个字符串随便写
        Bytes: buffer,
    }

    file, err := os.Create(pemFile)
    if err != nil {
        return err
    }
    return pem.Encode(file, block)
}

//Pkcs1转换为Pkcs8
func Pkcs1ToPkcs8(isPrivateKey bool, buffer []byte) []byte {
    var (
        oid  = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
        info interface{}
    )
    if isPrivateKey {
        val := struct {
            Version    int
            Algo       []asn1.ObjectIdentifier
            PrivateKey []byte
        }{}
        val.Version = 0
        val.Algo = []asn1.ObjectIdentifier{oid}
        val.PrivateKey = buffer
        info = val
    } else {
        val := struct {
            Algo      pkix.AlgorithmIdentifier
            BitString asn1.BitString
        }{}
        val.Algo.Algorithm = oid
        val.Algo.Parameters = asn1.NullRawValue
        val.BitString.Bytes = buffer
        val.BitString.BitLength = 8 * len(buffer)
        info = val
    }

    b, err := asn1.Marshal(info)
    if err != nil {
        panic(err)
    }
    return b
}

//Pkcs8转换为Pkcs1
func Pkcs8ToPkcs1(isPrivateKey bool, buffer []byte) []byte {
    if isPrivateKey {
        val := struct {
            Version    int
            Algo       pkix.AlgorithmIdentifier
            PrivateKey []byte
        }{}
        _, err := asn1.Unmarshal(buffer, &val)
        if err != nil {
            panic(err)
        }
        return val.PrivateKey
    } else {
        val := struct {
            Algo      pkix.AlgorithmIdentifier
            BitString asn1.BitString
        }{}

        _, err := asn1.Unmarshal(buffer, &val)
        if err != nil {
            panic(err)
        }
        return val.BitString.Bytes
    }
}

//生成公私钥
//usePKCS8:是否使用pkcs8
func GenerateRsaKey(usePKCS8 bool) ([]byte, []byte) {
    //生成私钥
    privateKey, err := rsa.GenerateKey(rand.Reader, 1024) //1024位
    if err != nil {
        panic(err)
    }
    //公钥
    publicKey := privateKey.PublicKey

    var (
        privateKeyBuffer []byte
        publicKeyBuffer  []byte
    )

    if usePKCS8 {
        privateKeyBuffer, err = x509.MarshalPKCS8PrivateKey(privateKey)
        if err != nil {
            panic(err)
        }
        publicKeyBuffer, err = x509.MarshalPKIXPublicKey(&publicKey)
        if err != nil {
            panic(err)
        }
    } else {
        privateKeyBuffer = x509.MarshalPKCS1PrivateKey(privateKey)
        publicKeyBuffer = x509.MarshalPKCS1PublicKey(&publicKey)
    }

    return publicKeyBuffer, privateKeyBuffer
}

func parsePkcsKey(buffer []byte, isPrivateKey, usePKCS8 bool) (interface{}, error) {
    var (
        err          error
        keyInterface interface{}
    )

    if isPrivateKey {
        if usePKCS8 {
            keyInterface, err = x509.ParsePKCS8PrivateKey(buffer)
        } else {
            keyInterface, err = x509.ParsePKCS1PrivateKey(buffer)
        }
    } else {
        if usePKCS8 {
            keyInterface, err = x509.ParsePKIXPublicKey(buffer)
        } else {
            keyInterface, err = x509.ParsePKCS1PublicKey(buffer)
        }
    }
    if err != nil {
        return nil, err
    }
    return keyInterface, nil
}

//RSA加密
func RsaEncrypt(value string, publicKey []byte, usePKCS8 bool) (string, error) {
    keyInterface, err := parsePkcsKey(publicKey, false, usePKCS8)
    if err != nil {
        return "", err
    }
    rsaPublicKey := keyInterface.(*rsa.PublicKey)
    buffer, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, []byte(value))
    if err != nil {
        return "", err
    }

    //以hex格式数值输出
    encryptText := fmt.Sprintf("%x", buffer)
    return encryptText, nil
}

//RSA解密
func RsaDecrypt(value string, privateKey []byte, usePKCS8 bool) (string, error) {
    //将hex格式数据转换为byte切片
    valueBytes := []byte(value)
    var buffer = make([]byte, len(valueBytes)/2)
    for i := 0; i < len(buffer); i++ {
        b, err := strconv.ParseInt(value[i*2:i*2+2], 16, 10)
        if err != nil {
            return "", err
        }
        buffer[i] = byte(b)
    }

    keyInterface, err := parsePkcsKey(privateKey, true, usePKCS8)
    if err != nil {
        return "", err
    }
    key := keyInterface.(*rsa.PrivateKey)
    buffer, err = rsa.DecryptPKCS1v15(rand.Reader, key, buffer)
    return string(buffer), nil
}

//RSA签名
func Sign(value string, privateKey []byte, hash crypto.Hash, usePKCS8 bool) (string, error) {
    keyInterface, err := parsePkcsKey(privateKey, true, usePKCS8)
    if err != nil {
        return "", err
    }
    key := keyInterface.(*rsa.PrivateKey)

    var _hash = hash.New()
    if _, err := io.WriteString(_hash, value); err != nil {
        return "", err
    }

    hashed := _hash.Sum(nil)
    result, err := rsa.SignPKCS1v15(rand.Reader, key, hash, hashed)
    if err != nil {
        return "", err
    }

    //以hex格式数值输出
    encryptText := fmt.Sprintf("%x", result)
    return encryptText, nil
}

//RSA验证签名
func Verify(value string, publicKey []byte, signature string, hash crypto.Hash, usePKCS8 bool) error {
    //将hex格式数据转换为byte切片
    valueBytes := []byte(signature)
    var buffer = make([]byte, len(valueBytes)/2)
    for i := 0; i < len(buffer); i++ {
        b, err := strconv.ParseInt(signature[i*2:i*2+2], 16, 10)
        if err != nil {
            return err
        }
        buffer[i] = byte(b)
    }

    keyInterface, err := parsePkcsKey(publicKey, false, usePKCS8)
    if err != nil {
        return err
    }

    key := keyInterface.(*rsa.PublicKey)

    var _hash = hash.New()
    if _, err := io.WriteString(_hash, value); err != nil {
        return err
    }

    hashed := _hash.Sum(nil)
    return rsa.VerifyPKCS1v15(key, hash, hashed, buffer)
}

  可以使用生成RSA的公私秘钥:

    //生成Rsa
    publicKey, privateKey := rsautil.GenerateRsaKey(usePKCS8)

  生成秘钥后,需要保存,一般保存到pem文件中:  

    //保存到Pem文件,filePath是文件目录
    rsautil.WriteToPem(false, publicKey, filepath.Join(filePath, "rsa.pub"))
    rsautil.WriteToPem(true, privateKey, filepath.Join(filePath, "rsa.pem"))

  从pem文件中读取:  

    //从Pem文件读取秘钥,filePath是文件目录
    publicKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pub"))
    privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pem"))

  还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:

    //从crt文件中读取公钥,filePath是文件目录
    publicKey, _ := rsautil.ReadPublicKeyFromCrt(filepath.Join(filePath, "demo.crt"), usePKCS8)
    privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "demo.key"))

  pfx文件中包含了公钥和私钥,可以很方便就读取到:

    //从pfx文件中读取秘钥,filePath是文件目录
    publicKey, privateKey := rsautil.ReadFromPfx(filepath.Join(filePath, "demo.pfx"), "123456", usePKCS8)

  有时候我们还可能需要进行秘钥的转换:  

    //Pkcs8格式公钥转换为Pkcs1格式公钥
    publicKey = rsautil.Pkcs8ToPkcs1(false, publicKey)
    // Pkcs8格式私钥转换为Pkcs1格式私钥
    privateKey = rsautil.Pkcs8ToPkcs1(true, privateKey)
    // Pkcs1格式公钥转换为Pkcs8格式公钥
    publicKey = rsautil.Pkcs1ToPkcs8(false, publicKey)
    // Pkcs1格式私钥转换为Pkcs8格式私钥
    privateKey = rsautil.Pkcs1ToPkcs8(true, privateKey)

  有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:  

    encryptText, _ := rsautil.RsaEncrypt(text, publicKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】加密后:%s\n", text, encryptText)

    decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】解密后:%s\n", encryptText, decryptText)

    signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8)
    fmt.Printf("【%s】经过【RSA】签名后:%s\n", text, signature)

    result := rsautil.Verify(text, publicKey, signature, crypto.MD5, usePKCS8) == nil
    fmt.Printf("【%s】的签名【%s】经过【RSA】验证后结果是:"+strconv.FormatBool(result), text, signature)

  完整的demo代码:

package main

import (
    "crypto"
    "demo/rsautil"
    "fmt"
    "os"
    "path/filepath"
    "strconv"
)

func main() {
    text := "上山打老虎"
    usePKCS8 := true // usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥
    path, _ := os.Executable()
    filePath := filepath.Dir(path)
    fmt.Printf("文件路径:%s\n", filePath) // 存放pem,crt,pfx等文件的目录

    //生成Rsa
    publicKey, privateKey := rsautil.GenerateRsaKey(usePKCS8)
    //从Pem文件读取秘钥,filePath是文件目录
    //publicKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pub"))
    //privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "rsa.pem"))
    //从pfx文件中读取秘钥,filePath是文件目录
    //publicKey, privateKey := rsautil.ReadFromPfx(filepath.Join(filePath, "demo.pfx"), "123456", usePKCS8)
    //从crt文件中读取公钥,filePath是文件目录
    //publicKey, _ := rsautil.ReadPublicKeyFromCrt(filepath.Join(filePath, "demo.crt"), usePKCS8)
    //privateKey, _ := rsautil.ReadFromPem(filepath.Join(filePath, "demo.key"))

    //保存到Pem文件,filePath是文件目录
    rsautil.WriteToPem(false, publicKey, filepath.Join(filePath, "rsa.pub"))
    rsautil.WriteToPem(true, privateKey, filepath.Join(filePath, "rsa.pem"))

    //Pkcs8格式公钥转换为Pkcs1格式公钥
    publicKey = rsautil.Pkcs8ToPkcs1(false, publicKey)
    // Pkcs8格式私钥转换为Pkcs1格式私钥
    privateKey = rsautil.Pkcs8ToPkcs1(true, privateKey)
    // Pkcs1格式公钥转换为Pkcs8格式公钥
    publicKey = rsautil.Pkcs1ToPkcs8(false, publicKey)
    // Pkcs1格式私钥转换为Pkcs8格式私钥
    privateKey = rsautil.Pkcs1ToPkcs8(true, privateKey)

    encryptText, _ := rsautil.RsaEncrypt(text, publicKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】加密后:%s\n", text, encryptText)

    decryptText, _ := rsautil.RsaDecrypt(encryptText, privateKey, usePKCS8)
    fmt.Printf("【%s】经过【RSA】解密后:%s\n", encryptText, decryptText)

    signature, _ := rsautil.Sign(text, privateKey, crypto.MD5, usePKCS8)
    fmt.Printf("【%s】经过【RSA】签名后:%s\n", text, signature)

    result := rsautil.Verify(text, publicKey, signature, crypto.MD5, usePKCS8) == nil
    fmt.Printf("【%s】的签名【%s】经过【RSA】验证后结果是:"+strconv.FormatBool(result), text, signature)
}

Python 实现 RSA

首先使用pip两个第三方包:  

# 用于从crt、pfx等文件读取公私秘钥
pip install pyOpenSSL
# 用RSA加密解密签名验证等(如果安装不了,先卸载旧版本再安装:pip uninstall pycrypto)
pip install pycryptodome
# 需要安装OpenSSL包:pip install pyOpenSSL
# 需要安装pycrypto包:pip install pycryptodome
from Crypto import Random, Hash
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as PKCS1_v1_5_Cipher
from Crypto.Signature import PKCS1_v1_5 as PKCS1_v1_5_Signature
from Crypto.IO import PEM, PKCS8
from OpenSSL import crypto
from Crypto.Util.asn1 import (DerSequence, DerBitString, DerObjectId, DerNull)

# pkcs1格式转换为pkcs8
def pkcs1_to_pkcs8(buffer):
    rsakey = RSA.importKey(buffer)
    if rsakey.has_private():
        return PKCS8.wrap(buffer, RSA.oid, None)
    return rsakey.exportKey("DER")

# pkcs8格式转换为pkcs1
def pkcs8_to_pkcs1(buffer):
    rsakey = RSA.importKey(buffer)
    if rsakey.has_private():  # 私钥
        return PKCS8.unwrap(buffer, None)[1]

    spki = DerSequence().decode(buffer, nr_elements=2)
    algo = DerSequence().decode(spki[0], nr_elements=(1, 2))
    algo_oid = DerObjectId().decode(algo[0])
    spk = DerBitString().decode(spki[1]).value

    if len(algo) == 1:
        algo_params = None
    else:
        try:
            DerNull().decode(algo[1])
            algo_params = None
        except:
            algo_params = algo[1]

    if algo_oid.value != RSA.oid or algo_params is not None:
        raise ValueError("No RSA subjectPublicKeyInfo")
    return spk

# 生成RSA加密的公私密钥
def generate_rsa_key(use_pkcs8=False):
    rsa_key = RSA.generate(1024, Random.new().read)  # 1024位
    private_key = rsa_key.exportKey("DER", pkcs=1)
    public_key = rsa_key.publickey().exportKey("DER")  # subject_public_key
    if use_pkcs8:
        private_key = pkcs1_to_pkcs8(private_key)
    else:
        public_key = pkcs8_to_pkcs1(public_key)

    return public_key, private_key

# 从pem文件中读取秘钥
def read_from_pem(pen_file_ame):
    with open(pen_file_ame, 'r') as f:
        t = PEM.decode(f.read())
        if t[2]:
            raise ValueError("fail to read pem")
        return t[0]

# 将秘钥写入pem文件
def write_to_pem(buffer, is_private_key, pen_file_ame):
    if is_private_key:
        str = PEM.encode(buffer, "RSA PRIVATE KEY")
    else:
        str = PEM.encode(buffer, "RSA PUBLIC KEY")

    with open(pen_file_ame, 'w') as f:
        f.write(str)

# 从crt文件中读取公钥
def read_public_key_from_crt(crt_file_name):
    with open(crt_file_name, 'rb') as f:
        cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
        return crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey())

# 从pfx文件中读取公私钥
def read_from_pfx(pfx_file_name, password):
    with open(pfx_file_name, 'rb') as f:
        pfx = crypto.load_pkcs12(f.read(), bytes(password, encoding="utf-8"))
        cert = pfx.get_certificate()
        _public_key = crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey())
        _privat_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pfx.get_privatekey())
        return _public_key, _privat_key

# RSA使用公钥加密
def rsa_encrypt(value, public_key):
    rsakey = RSA.importKey(public_key)
    cipher = PKCS1_v1_5_Cipher.new(rsakey)
    buffer = cipher.encrypt(value.encode("utf-8"))
    return buffer.hex()  # 使用hex格式输出

# RSA使用私钥解密
def rsa_decrypt(value, private_key):
    rsakey = RSA.importKey(private_key)
    cipher = PKCS1_v1_5_Cipher.new(rsakey)
    buffer = bytes.fromhex(value)  # 读取hex格式数据
    buffer = cipher.decrypt(buffer, Random.new().read)
    return buffer.decode("utf-8")

# RSA使用私钥签名
def sign(value, private_ey, halg=Hash.MD5):
    rsakey = RSA.importKey(private_ey)
    signer = PKCS1_v1_5_Signature.new(rsakey)

    hash = halg.new()
    hash.update(value.encode("utf-8"))
    buffer = signer.sign(hash)
    return buffer.hex()  # 使用hex格式输出

# RSA使用公钥验证签名
def verify(value, public_key, signature, halg=Hash.MD5):
    rsakey = RSA.importKey(public_key)
    verifier = PKCS1_v1_5_Signature.new(rsakey)

    hash = halg.new()
    hash.update(value.encode("utf-8"))

    buffer = bytes.fromhex(signature)  # 读取hex格式数据
    result: bool = verifier.verify(hash, buffer)
    return result

  生成RSA的公私秘钥:  

# 生成rsa公私秘钥
(publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8)

  生成秘钥后,需要保存,一般保存到pem文件中:  

# 将公私秘钥写入pem文件,filePath是文件目录
RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub"))
RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem"))

  可以保存到pem文件中,当然也可以从pem文件中读取了:  

# 从pem文件中读取秘钥,filePath是文件目录
publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub"))
privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem"))

  还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:

# 从crt文件读取,filePath是文件目录
publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt"))
privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key"))

  pfx文件中包含了公钥和私钥,可以很方便就读取到:

# 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
(publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456")

  有时候我们还可能需要进行秘钥的转换:

# Pkcs8格式公钥转换为Pkcs1格式公钥
publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey)
# Pkcs8格式私钥转换为Pkcs1格式私钥
privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey)
# Pkcs1格式公钥转换为Pkcs8格式公钥
publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey)
# Pkcs1格式私钥转换为Pkcs8格式私钥
privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey)

  有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:

encryptText = RsaUtil.rsa_encrypt(text, publicKey)
print("【", text, "】经过【RSA】加密后:", encryptText)

decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey)
print("【", encryptText, "】经过【RSA】解密后:", decryptText)

signature = RsaUtil.sign(text, privateKey, Hash.MD5)
print("【", text, "】经过【RSA】签名后:", signature)

result = RsaUtil.verify(text, publicKey, signature, Hash.MD5)
print("【", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)

  完整的demo代码:

import RsaUtil
from Crypto import Hash
import os
​
text = "上山打老虎"
use_pkcs8 = True
filePath = os.getcwd()
print("文件路径:", filePath)
​
# 生成rsa公私秘钥
(publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8)
# 从pem文件中读取秘钥,filePath是文件目录
# publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub"))
# privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem"))
# 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录
# (publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456")
# 从crt文件读取,filePath是文件目录
# publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt"))
# privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key"))
​
# 将公私秘钥写入pem文件,filePath是文件目录
RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub"))
RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem"))
​
# Pkcs8格式公钥转换为Pkcs1格式公钥
publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey)
# Pkcs8格式私钥转换为Pkcs1格式私钥
privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey)
# Pkcs1格式公钥转换为Pkcs8格式公钥
publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey)
# Pkcs1格式私钥转换为Pkcs8格式私钥
privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey)encryptText = RsaUtil.rsa_encrypt(text, publicKey)
print("【", text, "】经过【RSA】加密后:", encryptText)decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey)
print("【", encryptText, "】经过【RSA】解密后:", decryptText)signature = RsaUtil.sign(text, privateKey, Hash.MD5)
print("【", text, "】经过【RSA】签名后:", signature)result = RsaUtil.verify(text, publicKey, signature, Hash.MD5)
print("【", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)