RSA

2019-10-15  本文已影响0人  非你不可_a036

一、RSA的使用

RSA属于非对称加密,采用一对密钥进行加密(公钥和私钥),一个用来加密、另一个用来解密,非对称加密运算速率慢(需要采用大量的数学运算,一般用于少量信息加密)

1.RSA三要素

RSA加密需要三个值:n,e1, e2。

n是两个质数的乘积,n的值越大,加密越安全,一般处于公开状态。

e1是公钥中的e(指数),与n形成公钥,用于加密,一般处于公开状态。

e2是私钥中的e(指数),与n形成私钥,用于解密,处于保密状态(不公开)。

2.Python使用rsa包加密

首先导入rsa包,把n和e转化为10进制。通过rsa.PublicKey(e=e, n=n)生成公钥。

然后使用rsa.encrypt("8helkyme0ludtdw0".encode(), pub_key=key)进行加密,其中8helkyme0ludtdw0为加密的明文,由于rsa是对字节流进行加密,所以enode()。

加密后的内容感觉乱七八糟的,一般在这时候使用hex(),把每个字节转化为两位16进制数(字节转变字符串),因此加密结果长度增加一倍。如图1所示。

图1

二、RSA原理

假设a为明文,b为密文。

加密:b=pow(a, e1, n)

解密:a=pow(b, e2, n)

1.n的来源

n是通过两个质数相乘得到的,n的二进制长度就是密钥长度。RSA的密钥一般是1024位,少数是2048位。如图1中的modulus就是1024位。

例:x和y是两个不同的质数:11和13,则n=11*13=143(加密内容不能超过143)。143的二进制是:10001111,为8位。则密钥为8位。

2.e1的来源

获取e1需要使用欧拉函数,再选取与φ(n)互质的一个数。

φ(n)=(x-1)*(y-1)=10*12=120

则:1<e1<120,且e1与120互质,如:e1=13,一般来说e1为65537。

例:2与3只有公因数1,则2和3互质。

3.e2的来源

e2泄露则代表加密被破解,在rsa加密中的6个数字中,x,y,n,φ(n),e1,e2,n=x*y,φ(n)=(x-1)*(y-1),也就是说x,y一定不能泄露,否则6个数字已知晓了4个数字,再加上e1是公开的,所以e2就能被推算出来了。

e1*e2 mod φ(n) = 1 即:e1*e2 = k * φ(n) + 1 —>  13*e2 = k*120 +1,这个使用“扩展欧几里得算法”求解。python实现方式如图2所示。

图2

由于“扩展欧几里得算法”中的方程式是13*e2 + k*120= 1,因此k是负数,最后得出:e2=37, k=-4。

4.尝试加密解密

从上文叙述已知:e1=13, e2=37, n=143,即公钥是(13,143),私钥是(37, 143)

这里用数字9进行测试,即明文a=9,加密:b=pow(a, e1, n)=9^13%143=113

明文9加密后得到密文b:113,解密:a=pow(b, e2, n) = 113^37%143=9

三、RSA加密源码

从图1中已知,使用rsa包就两步,首先传入e,n生成公钥,key=rsa.PublicKey(e=e, n=n)

然后传入明文和公钥key,输出加密结果,h=rsa.encrypt("8helkyme0ludtdw0".encode(), pub_key=key)

在encrypt()中就几步,首先看源码:

def encrypt(message, pub_key):

keylength = common.byte_size(pub_key.n)

padded = _pad_for_encryption(message, keylength)

payload = transform.bytes2int(padded)

encrypted = core.encrypt_int(payload, pub_key.e, pub_key.n)

block = transform.int2bytes(encrypted, keylength)

return block

从源码中可以看到需要两个参数,message是明文,pub_key是公钥。

第一步:common.byte_size(pub_key.n)目的是计算n的二进制长度(位),然后除以8(字节),得到支持的最大加密长度。

第二步:_pad_for_encryption(message, keylength)目的是对明文进行填充,右侧补全,填充值是使用os.urandom()随机生成的,因此每次填充结果不一样。

第三步:transform.bytes2int(padded)目的是把每个字节转化为两位16进制来表示,再转化为10进制。

第四步:core.encrypt_int(payload, pub_key.e, pub_key.n),对明文进行加密。

第五步:transform.int2bytes(encrypted, keylength)目的是舍弃左侧的0,在右侧进行0补全,最后每64位进行一次封装。

四、自己实现RSA加密

import os

import math

import copy

from structimport pack

k ="8helkyme0ludtdw1".encode()

modulus ="00A2FD668D79120D41EFFA26B0F5A5A43D6139D46893F7559CC2BA0C704BDA19A9420F97D113E32EF4E462C49F95B9DC487E2963F0FBE9C9C79DBD128D26C244D062BA69F9E7961D646193B8EF36814C7BB17053AB2418E1C8671781827D4BF3C6AA3D8B581FDF4F60210900EBD4AF34C5B51B77C73FB707448C527A56541B142B"

e =int("010001", 16)

n =int(modulus, 16)

# 计算n的二进制长度,获取证书位数(1024和2048)

key_length = math.ceil(n.bit_length() /8)

# 预留11个字节,获取加密时支持的最大字节数

max_msglength = key_length -11

msg_length =len(k)

if msg_length > max_msglength:

raise OverflowError("超出可支持的最大加密长度,支持:[{}]字节,目前:[{}]字节".format(max_msglength, msg_length))

padding_length = key_length - msg_length -3

new_padding = os.urandom(int(padding_length) +5).replace(b'\x00', b'')

padding = new_padding[:padding_length]

padded =b''.join([b'\x00\x02', padding, b'\x00', k])

# 原方法使用hexlify(),目的:每个字节的数据转换成相应的 2 位十六进制表示。因此产生的字符串是原数据的两倍长度

# padded.hex()在python3.5以前的实现:''.join(['%02x' % b for b in a_bytes])

# padded.hex()在python3.5以后的实现:a_bytes.hex()

payload =int(padded.hex(), 16)

# RSA加密

encrypted =pow(payload, e, n)

raw_bytes =b''

num = copy.deepcopy(encrypted)

# 加密内容进行包装

while num >0:

raw_bytes = pack(">Q", num &0xffffffffffffffff) + raw_bytes

num >>=64

# 获取左侧为0的长度

leading =0

for xin raw_bytes:

if x ==0:

leading +=1

    else:

break

# 舍弃左侧为0的数据,进行右侧补全

if leading:

raw_bytes = raw_bytes[leading:]

raw_bytes = raw_bytes.rjust(int(key_length), b'\x00')

result = raw_bytes.hex()

这是对1024位密钥加密的实现,存在部分局限性。

号外:

RSA解密:

解密原理与加密原理相同,不过多赘述,在使用python中的RSA包的时候,有一个坑,生成私钥需要传5个参数e,d,n,p,q。我们知道解密不需要e,p,q。从生成私钥源码中发现存在对p,q的检查,但是只是检查p,q是否互质。也就是说p,q传值2,3也可以。

上一篇下一篇

猜你喜欢

热点阅读