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
.
欧拉函数的通项公式是什么?先给出结论,再谈谈是如何找到这个结论的。
想求出φ(n),那就得统计出 n 以内有多少个与 n 互质的数。那么,与 n 互质的数 r 有什么特点呢?
关键在于: n 拥有的质因子, r 全都没有【质因子(素因数或质因数)在数论里是指能整除给定正整数的质数【指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数】。除了1以外,两个没有其他共同质因子的正整数称为互质。因为 1 没有质因子,1与任何正整数(包括1本身)都是互质。正整数的因数分解可将正整数表示为一连串的质因子相乘,质因子如重复可以用指数表示。根据算术基本定理,任何正整数皆有独一无二的质因子分解式 。只有一个质因子的正整数为质数。】。
每个合数都可以写成几个质数(也可称为素数)相乘的形式,这几个质数就都叫做这个合数的质因数。如果一个质数是某个数的因数,那么就说这个质数是这个数的质因数;而这个因数一定是一个质数。
换句话讲:假设 n 的质因数分解为
则 r 必然满足 r % p1 = 0, r % p2 =0, ⋯
既然如此,研究 r 模掉 n 的质因子们所得到的结果,就可以看出 r 的一些性质。构造一个函数:
有性质如下:
r 与 n 互质,当且仅当 G(r) 生成的元组中,对于第 i 位,总有它不能被 pi 整除。我们把这种元组称为合法元组。
这是一条很平凡的性质,但它可以成为计数的一把钥匙。直接统计 n 以内与 n 互质的数是困难的,但统计有多少个合法元组则简单得多。合法元组的第 i 位不能是 pi 的倍数或0,那么这一位有 piki⋅(1−1pi) 种取值,故依据乘法原理,合法元组的总数是:
这个式子要成为欧拉函数的通项公式,还有最后一步路要走:G目前只是一个普通映射,既有可能多个 r 对应同一个合法元组,也可能一个合法元组对应不了任何 r. 从而合法元组的数量未必恰好就是与 n 互质的数的数量。接下来的任务是:证明 元组G:{1,2,3,⋯,n}→(元组) 是一个双射。
中国剩余定理(CRT) 通过一个精妙的构造,指出了 元组(元组)→{1,2,3,⋯,n}是一个双射(当然了,这个结论只是CRT的一个副产品)。中国剩余定理在这里不再赘述,感兴趣的读者可以去查阅相关资料。总之,既然元组与 n 以内的整数是一一对应的,那么合法元组的数量就是 n 以内与 n 互质的数的个数。从而我们得到了欧拉函数的通项公式:
现在我们手上有欧拉函数的通项公式了。可以随便挑几个数验证一下:
利用欧拉函数的通项公式,可以得到两条性质:
φ 是积性函数。若 a,b 互质,则有
欧拉定理。若 a,m 互质,则有
不难发现,费马小定理是欧拉定理在 m 为质数下的情形。
以上这堆性质的证明、欧拉函数通项的求法、线性筛欧拉函数、扩展欧几里得算法,是接下来代码实现的基础。
欧拉定理是欧拉函数最大的作用。利用欧拉定理可以得到结论:若a,m互质,则
这不仅大大降低了计算复杂度(可以利用快速幂算法),也是 RSA 算法的基石。
RSA算法
RSA算法的过程如下:
- 随便选择两个极大的质数,分别记为 p,q. 当然这里
p ≠ q
. - 记
N = p ⋅ q
, 则由欧拉函数的积性有φ(N) = (p − 1)(q − 1)
. - 选取一个小于 N 的 e, 使得
gcd(e, N) = 1
. 求出 e 在模 φ(N) 意义下的逆元,记为d. - 我们得到了
(N, e)
作为公钥,(N, d)
作为私钥。
注意到 e 和 d 是对称的,把 e 拿去当私钥,把 d 拿去当公钥也可行。
公钥可以分发给所有人,私钥必须保存好。别人可以用公钥加密信息发给我,我用私钥可以解密这条信息。下面来讨论加密、解密算法。
假设 Alice 想给 Bob 发一条信息,于是搞到了 Bob 的公钥(N,e). 设 m 为明文,则我们这样计算出密文:
Bob 收到密文 c 之后,用自己手上的私钥解密:
为什么这样就可以恢复出明文 m?
注意到
而 e,d 在模φ(N)意义下互为逆元,故
从而
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
.
欧拉函数的通项公式是什么?先给出结论,再谈谈是如何找到这个结论的。
想求出φ(n),那就得统计出 n 以内有多少个与 n 互质的数。那么,与 n 互质的数 r 有什么特点呢?
关键在于: n 拥有的质因子, r 全都没有【质因子(素因数或质因数)在数论里是指能整除给定正整数的质数【指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数】。除了1以外,两个没有其他共同质因子的正整数称为互质。因为 1 没有质因子,1与任何正整数(包括1本身)都是互质。正整数的因数分解可将正整数表示为一连串的质因子相乘,质因子如重复可以用指数表示。根据算术基本定理,任何正整数皆有独一无二的质因子分解式 。只有一个质因子的正整数为质数。】。
每个合数都可以写成几个质数(也可称为素数)相乘的形式,这几个质数就都叫做这个合数的质因数。如果一个质数是某个数的因数,那么就说这个质数是这个数的质因数;而这个因数一定是一个质数。
换句话讲:假设 n 的质因数分解为
则 r 必然满足 r % p1 = 0, r % p2 =0, ⋯
既然如此,研究 r 模掉 n 的质因子们所得到的结果,就可以看出 r 的一些性质。构造一个函数:
有性质如下:
r 与 n 互质,当且仅当 G(r) 生成的元组中,对于第 i 位,总有它不能被 pi 整除。我们把这种元组称为合法元组。
这是一条很平凡的性质,但它可以成为计数的一把钥匙。直接统计 n 以内与 n 互质的数是困难的,但统计有多少个合法元组则简单得多。合法元组的第 i 位不能是 pi 的倍数或0,那么这一位有 piki⋅(1−1pi) 种取值,故依据乘法原理,合法元组的总数是:
这个式子要成为欧拉函数的通项公式,还有最后一步路要走:G目前只是一个普通映射,既有可能多个 r 对应同一个合法元组,也可能一个合法元组对应不了任何 r. 从而合法元组的数量未必恰好就是与 n 互质的数的数量。接下来的任务是:证明 元组G:{1,2,3,⋯,n}→(元组) 是一个双射。
还记得中国剩余定理(CRT) 吗?它通过一个精妙的构造,指出了 元组(元组)→{1,2,3,⋯,n}是一个双射(当然了,这个结论只是CRT的一个副产品)。中国剩余定理在这里不再赘述,感兴趣的读者可以去查阅相关资料。总之,既然元组与 n 以内的整数是一一对应的,那么合法元组的数量就是 n 以内与 n 互质的数的个数。从而我们得到了欧拉函数的通项公式:
现在我们手上有欧拉函数的通项公式了。可以随便挑几个数验证一下:
利用欧拉函数的通项公式,可以得到两条性质:
φ 是积性函数。若 a,b 互质,则有
欧拉定理。若 a,m 互质,则有
不难发现,费马小定理是欧拉定理在 m 为质数下的情形。
以上这堆性质的证明、欧拉函数通项的求法、线性筛欧拉函数、扩展欧几里得算法,是接下来代码实现的基础。
欧拉定理是欧拉函数最大的作用。利用欧拉定理可以得到结论:若a,m互质,则
这不仅大大降低了计算复杂度(可以利用快速幂算法),也是 RSA 算法的基石。
RSA算法
RSA算法的过程如下:
- 随便选择两个极大的质数,分别记为 p,q. 当然这里
p ≠ q
. - 记
N = p ⋅ q
, 则由欧拉函数的积性有φ(N) = (p − 1)(q − 1)
. - 选取一个小于 N 的 e, 使得
gcd(e, N) = 1
. 求出 e 在模 φ(N) 意义下的逆元,记为d. - 我们得到了
(N, e)
作为公钥,(N, d)
作为私钥。
注意到 e 和 d 是对称的,把 e 拿去当私钥,把 d 拿去当公钥也可行。
公钥可以分发给所有人,私钥必须保存好。别人可以用公钥加密信息发给我,我用私钥可以解密这条信息。下面来讨论加密、解密算法。
假设 Alice 想给 Bob 发一条信息,于是搞到了 Bob 的公钥(N,e). 设 m 为明文,则我们这样计算出密文:
Bob 收到密文 c 之后,用自己手上的私钥解密:
为什么这样就可以恢复出明文 m?
注意到
而 e,d 在模φ(N)意义下互为逆元,故
从而
代码实现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/lnn 保证了 n 附近期望每 lnn 个数里面就有一个质数。想找到适合RSA的大质数 p,q,只需要随机生成很大的数,然后检验是不是质数,重复几次就可以达到目标。
尽管现在没有多项式时间内分解质因数的算法,但有些算法还是比较快的——例如 Pollard's rho. 此外,量子计算机上可以实现Shor算法,但目前没有多大的应用价值。推荐使用不低于2048位的密钥,这里的密钥长度是指模数 N 的二进制长度。
生成RSA密钥对
ssh-keygen
程序可以造一个RSA密钥对,接下来这个密钥对可以用于git身份验证、ssh服务器身份验证等很多功能。
生成一个RSA密钥对
接下来,我们来看这里面有什么东西。 hello
是私钥文件, hello.pub
是公钥文件。里面的东西都是base64编码过的,可以用RsaCtfTool来查看。
公钥文件
私钥文件
几道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/lnn 保证了 n 附近期望每 lnn 个数里面就有一个质数。想找到适合RSA的大质数 p,q,只需要随机生成很大的数,然后检验是不是质数,重复几次就可以达到目标。
尽管现在没有多项式时间内分解质因数的算法,但有些算法还是比较快的——例如 Pollard's rho. 此外,量子计算机上可以实现Shor算法,但目前没有多大的应用价值。推荐使用不低于2048位的密钥,这里的密钥长度是指模数 N 的二进制长度。
生成RSA密钥对
ssh-keygen
程序可以造一个RSA密钥对,接下来这个密钥对可以用于git身份验证、ssh服务器身份验证等很多功能。
生成一个RSA密钥对
接下来,我们来看这里面有什么东西。 hello
是私钥文件, hello.pub
是公钥文件。里面的东西都是base64编码过的,可以用RsaCtfTool来查看。
公钥文件
私钥文件
几道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)