【密码学】一文读懂RSA

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 本文来聊聊RSA, 这是一个非对称密码,和之前所提到的AES与DES不同的是,这个加密方式有两个密钥,一个是公钥,一个是私钥,公钥用来加密,私钥用来解密。相比于对称密码,非对称密码大多基于某个数学难题,比如接下来要谈论的RSA即基于大整数分解的困难性来的,因此为了说明白这个加密算法,首先要先补充"一"点点数学知识。

一文读懂RSA


一文读懂RSA

本文来聊聊RSA, 这是一个非对称密码,和之前所提到的AES与DES不同的是,这个加密方式有两个密钥,一个是公钥,一个是私钥,公钥用来加密,私钥用来解密。相比于对称密码,非对称密码大多基于某个数学难题,比如接下来要谈论的RSA即基于大整数分解的困难性来的,因此为了说明白这个加密算法,首先要先补充"一"点点数学知识。


数学基础

RSA用到的数学知识还是比较少的,基本上只是小学学过的一些关于整数的一些知识,相信看到这里的读者应该都能理解,在这里我不打算去讲过多的证明,只是说一些结论,想看证明的大佬,可以去参考文章后面参考资料里面的书籍。

素数

素数,也就是除了1和它本身,没有其他的约数的数字,我当时学的时候,一直都是叫的素数,也有叫质数的,我还是沿用我之前的习惯,叫素数了,有不同叫法的注意一下,说的是一个东西。

算数基本定理

对于任意的整数(n > 1),都可以唯一的分解为:

image.png

其中均为素数,这个定理比较好理解,任何一个合数都可以被分解成为若干个素数的乘积,这个小学的因式分解应该就讲过。

费马小定理

如果p是素数,a是正整数并且不能被p整除,则:

image.png

费马小定理还有一种表示形式,若p为素数,并且a为任意正整数,则:

image.png

欧拉函数()

这个函数表示小于n并且与n互素的元素的个数,特别的我们定义。

在RSA当中,我们用到了下面一个关系式,对于素数p来说显然有:

image.png

假设有两个素数p和q,则有:


欧拉定理

对于任意互素的两个数a和n, 有:

image.png

同费马小定理一样,还有另一种表示形式:

image.png

同样,这里也不要求a和n互素。

到这里,所用到的数学知识就都介绍完了,是不是发现并不多,而且大多都比较好理解。


RSA算法描述

RSA是基于大整数分解这个困难问题的,为什么说这是一个困难问题呢,比如我现在给出一个数字3220479853, 我说这是两个素数的乘积,如果要找出这两个素数,相信如果不用计算器,纯靠笔算的话估计一时半会也不会算出来吧,当然要是有超级大脑哪种水平,有可能能直接看出结果,这种情况除外,但是如果计算这个106649*30197这个值相信各位读者用笔稍稍计算一会就可以得出结果了,对于计算机来说,如果n足够大(2048bit)的话,基本也不太可能在有效时间能计算出结果,当然这里我说的是有效时间,而不是绝对算不出来,如果算力无限或者时间无限的的话,这是肯定能计算出来的,实际显然不会是时间无限或者算力无限,啰嗦了这么久,下面进入正题了,来聊聊RSA具体是怎么运作的。

  • 第一步: 选取两个大素数p和q
  • 第二步: 计算这两个素数的乘积
  • 第三步: 计算
  • 第四步: 选取e, 并且满足和
  • 第五步: 计算
  • 第六步: 公开(e, n)作为公钥,保密(d, p, q)作为私钥。
  • 第七步: 加密过程, 计算
  • 第八步: 解密过程, 计算

注意,为了安全,n要取2048bit大小的数字,从上面可以看出,如果M的长度大于n, 则会丢失一部分明文信息,因此RAS加密信息的长度和密钥长度相关,并且由于这里做了模幂运算,因此性能相对于之前所讲过的分组密码来说,要差的多,因此不建议采用RSA加密过长的信息,因为性能想对来说比较差,如果消息长度大于n, 也可以采用之前提到的连接模式,只不过这里的分组长度变成了密钥的长度,按照同样规则进行处理即可。

对于RSA正确性的证明,我就不在这里啰嗦了,有兴趣的读者可以自行查阅相关书籍,在我后文的参考资料里面也有相关的知识,包括前面所提到的所有数学知识的证明也都有。

注意上面所说的rsa是理论上的rsa, 没有进行填充,rfc的标准实现里面是要求进行PKCS1Padding#2的方式进行padding的,关于这个padding是如何处理的,可以参考我之前写过的关于padding的文章,也可以参考文末给出的rfc,下面我给出的代码实现也是没有padding的,要注意这一点。


代码实现

这里用到了rust的大数运算的库,需要自行添加一下依赖,这个实现,我并没有添加找素数的过程,默认是有公钥和私钥的,只是实现了加密和解密算法。

// num_bigint = 0.4.2
use num_bigint::{BigUint, BigInt};
use std::ops::{Sub, Mul};
use num_traits::{FromPrimitive, zero, one};
pub struct PublicKey {
    pub n: BigUint,
    pub e: BigUint,
}
pub struct PrivateKey {
    pub n: BigUint,
    pub d: BigUint,
}
pub struct KeyPair {
    pub private_key: PrivateKey,
    pub public_key: PublicKey,
}
impl PublicKey {
    pub fn new(n: BigUint, e: BigUint) -> PublicKey {
        return PublicKey { n, e };
    }
}
impl PrivateKey {
    pub fn new(n: BigUint, d: BigUint) -> PrivateKey {
        return PrivateKey { n, d };
    }
}
pub fn phi(p: BigUint, q: BigUint) -> BigUint {
    return p.sub(BigUint::from_u8(1).unwrap()).mul(q.sub(BigUint::from_u8(1).unwrap()));
}
pub fn mod_inv(u: &BigUint, v: &BigUint) -> BigUint {
    let mut q: BigUint;
    let mut t1: BigUint;
    let mut t3: BigUint;
    let (mut u1, mut u3, mut v1, mut v3) =
        (BigUint::from(1u8), u.clone(), BigUint::from(0u8), v.clone());
    let mut iter: i8 = 1;
    while &v3 != &zero() {
        q = &u3 / &v3;
        t3 = &u3 % &v3;
        t1 = &u1 + &q * &v1;
        u1 = v1;
        v1 = t1;
        u3 = v3;
        v3 = t3;
        iter = -iter;
    }
    if &u3 != &one() {
        return zero();
    }
    if iter < 0 {
        return v - u1;
    }
    u1
}
impl KeyPair {
    pub fn from_keys(private_key: PrivateKey, public_key: PublicKey) -> KeyPair {
        return KeyPair {
            private_key,
            public_key,
        };
    }
    fn encrypt(&self, message: &[u8]) -> Vec<u8> {
        let res = BigUint::from_bytes_be(message);
        return res.modpow(&self.public_key.e, &self.public_key.n).to_bytes_be();
    }
    fn decrypt(&self, message: &[u8]) -> Vec<u8> {
        let res = BigUint::from_bytes_be(message);
        return res.modpow(&self.private_key.d, &self.private_key.n).to_bytes_be();
    }
}
#[cfg(test)]
mod test {
    use crate::rsa::{KeyPair, PublicKey, PrivateKey, phi, mod_inv};
    use num_bigint::{BigUint, BigInt};
    use std::str::FromStr;
    #[test]
    fn test_phi() {
        let p = BigUint::from_str("253510634729688379261").unwrap();
        let q = BigUint::from_str("719924526944767849").unwrap();
        assert_eq!(phi(p, q).to_string(), "182508523783238741385145482544078032480")
    }
    #[test]
    fn test_mod_inv() {
        let result = mod_inv(&mut BigUint::from_str("65537").unwrap(), &mut BigUint::from_str("182508523783238741385145482544078032480").unwrap());
        assert_eq!(result.to_string(), "148798319159955634674933143465449706753");
    }
    #[test]
    fn test() {
        let key = KeyPair::from_keys(
            PrivateKey::new(BigUint::from_str("182508523783238741639376041800711179589").unwrap(), BigUint::from_str("148798319159955634674933143465449706753").unwrap()),
            PublicKey::new(BigUint::from_str("182508523783238741639376041800711179589").unwrap(), BigUint::from_str("65537").unwrap()));
        let result = key.encrypt("AAAA".as_bytes());
        println!("{:?}", result);
        println!("{:?}", key.decrypt(&result));
    }
}


相关文章
|
Rust 算法 安全
【密码学】一文读懂HMAC
本文将来聊一聊基于哈希函数的消息认证码,在此之前,先来科普一下什么是 「消息认证码」 (MAC), 先来看一个简单的栗子
1541 0
【密码学】一文读懂HMAC
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂XTEA加密
本篇文章,我们来看一下上一次讲过的TEA加密算法的一个升级版XTEA, 相比于TEA, XTEA的安全性显然是更高的,其中的过程要比TEA稍微复杂一点点。
1217 0
【密码学】一文读懂XTEA加密
|
算法 数据安全/隐私保护
一文详解 RSA 非对称加密算法
非对称加密算法指的是 加、解密使用不同的密钥,一把为公开的公钥,另一把为私钥。 公钥加密的内容只能由私钥进行解密,反之由私钥加密的内容只能由公钥进行解密。也就是说,这一对公钥、私钥都可以用来加密和解密,并且一方加密的内容只能由对方进行解密。
7811 1
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂RSA的随机数生成器
本文接着来聊一个比较轻松的内容,再来说一个随机数生成器,对于这个随机数生成器呢,这里和之前讲到过的BBS有一些类似,直接来看具体的内容蛤。
1076 1
【密码学】一文读懂RSA的随机数生成器
|
算法 安全 数据安全/隐私保护
【密码学】 一篇文章讲透数字签名
数字签名(又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。它是一种类似写在纸上的普通的物理签名,但是在使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。数字签名可以识别消息是否被篡改, 并验证消息的可靠性, 也可以防止否认。
684 0
【密码学】 一篇文章讲透数字签名
|
算法 数据安全/隐私保护
【密码学】一文读懂SHA-1
SHA-1(Secure Hash Algorithm 1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦资料处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
1147 1
【密码学】一文读懂SHA-1
|
数据安全/隐私保护
通俗易懂的RSA加密解密指导(二)
前言 RSA加密算法是一种非对称加密算法,简单来说,就是加密时使用一个钥匙,解密时使用另一个钥匙。 因为加密的钥匙是公开的,所又称公钥,解密的钥匙是不公开的,所以称为私钥。 图片 密钥 关于RSA加密有很多文章,但几乎都只介绍了RSACryptoServiceProvider类的使用方法,如果只是走走看看,是没问题的,但真的想使用时,就会发现,你没有密钥字符串。。。 下面我们从获取密钥字符串开始逐步学习加密。 密钥字符串 每个安装过VisualStudio的电脑都可以找到一个文件—makecert.exe。 我电脑的makecert.exe地址:
通俗易懂的RSA加密解密指导(二)
|
算法 Serverless 数据安全/隐私保护
【密码学】一文读懂SHA-2
SHA-2安全散列算法2(Secure Hash Algorithm 2)一种密码散列函数算法标准,由美国国家安全局研发,由美国国家标准与技术研究院(NIST)在2001年发布。属于SHA算法之一,是SHA-1的后继者。其下又可再分为六个不同的算法标准,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。
【密码学】一文读懂SHA-2
|
Rust 算法 数据安全/隐私保护
【密码学】一文读懂AES
AES加密算法 本文将不会讲述过多有关AES的数学知识, 如果有兴趣的可以自行了解。
1693 0
【密码学】一文读懂AES
|
算法
【密码学】一文读懂数字签名
本文来简单的聊一聊数字签名,先来看下面一个例子。
【密码学】一文读懂数字签名