相关文章推荐
酒量大的充电器  ·  Controller action ...·  3 周前    · 
酒量大的充电器  ·  如何将 LINQ ...·  2 月前    · 
酒量大的充电器  ·  标注属性面板·  5 月前    · 
酒量大的充电器  ·  XML 与 HTML 对比 - ...·  8 月前    · 
酒量大的充电器  ·  Problem in login with ...·  9 月前    · 
威武的香瓜  ·  [转]Entity Framework ...·  4 小时前    · 

阮一峰的网络日志:RSA算法原理(一)

阮一峰的网络日志:RSA算法原理(二)

RSA加密算法 是一种 非对称加密算法 ,在 公开密钥加密 和电子商业中被广泛使用。RSA是1977年由 罗纳德·李维斯特 (Ron Rivest)、 阿迪·萨莫尔 (Adi Shamir)和 伦纳德·阿德曼 (Leonard Adleman)一起提出的。当时他们三人都在 麻省理工学院 工作。RSA就是他们三人姓氏开头字母拼在一起组成的。

1973年,在英国政府通讯总部工作的数学家克利福德·柯克斯(Clifford Cocks)在一个内部文件中提出了一个与之等效的算法,但该算法被列入机密,直到1997年才得到公开。

RSA算法基于一个十分简单的数论事实:将两个大质数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。

所以对极大整数做 因数分解 的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA钥匙才可能被强力方式破解。到当前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被破解的。

2. 如何生成

示例为常见的Linux或者mac平台

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 生成公钥 《默认生成PKCS1格式》
openssl genrsa -out rsa_pri_key.pem 2048
# 将PKCS1格式私钥转换成PKCS8格式
openssl pkcs8 -topk8 -inform PEM -in rsa_pri_key.pem -outform PEM -nocrypt -out rsa_pri_key_pkcs8.pem
# 将PKCS8格式私钥转换成PKCS1格式
openssl rsa -inform PEM -in rsa_pri_key_pkcs8.pem -outform PEM -out rsa_pri_key.pem

# 2. 根据私钥生成公钥 《默认生成PKCS8格式》
openssl rsa -in rsa_pri_key.pem -pubout -out rsa_pub_key.pem
# 将PKCS8格式公钥转换成PKCS1格式
openssl rsa -pubin -in rsa_pub_key.pem -RSAPublicKey_out -out rsa_pub_key_pkcs1.pem
# 将PKCS1格式公钥转换成PKCS8格式
openssl rsa -RSAPublicKey_in -in rsa_pub_key_pkcs1.pem -pubout -out rsa_pub_key.pem

3. 密钥格式

常见的为 PKCS#8 PKCS#1 ,除此之外还有其他的比如PKCS#5、PKCS#12等。

关于 PKCS#8 PKCS#1

  • PKC#8:定义了一种编码和传输密钥的方法,它并不特定于OpenSSL;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 公钥
    -----BEGIN PUBLIC KEY-----
    -----END PUBLIC KEY-----

    # 私钥
    -----BEGIN PRIVATE KEY-----
    -----END PRIVATE KEY-----

    # 私钥加密的格式
    -----BEGIN ENCRYPTED PRIVATE KEY-----
    -----END ENCRYPTED PRIVATE KEY-----
  • PKCS#1:定义了一种使用RSA密钥的方法(无论它是如何加载到应用程序中,是否使用PKCS#8)来执行和验证数据的数字签名。

    1
    2
    3
    4
    5
    6
    7
    # 公钥
    -----BEGIN RSA PUBLIC KEY-----
    -----END RSA PUBLIC KEY-----

    # 私钥
    -----BEGIN RSA PRIVATE KEY-----
    -----END RSA PRIVATE KEY-----

    二、Python加密模块示例

    RSA 有两种填充方式,一种是 PKCS1_v1_5,另一种是 PKCS1_OAEP

    Python RSA文件加密

    1. rsa模块

    由python实现,封装得较深,使用简单,比如签名和验签,两行代码就搞定了,很符合python简约的语言理念。

  • 加密与解密

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import rsa
    import base64
    from urllib import request


    # 生成密钥
    def create_new_keys(len):
    (public_key, private_key) = rsa.newkeys(len)
    with open('public1.pem', 'wb') as f:
    f.write(public_key.save_pkcs1())
    with open('private1.pem', 'wb') as f:
    f.write(private_key.save_pkcs1())


    # 加密
    def rsa_encrypt(msg):
    with open(PUBLIC_FILE_PATH, 'rb') as public_file:
    # 加载pkcs8格式公钥
    public_key = rsa.PublicKey.load_pkcs1_openssl_pem(public_file.read())
    code = rsa.encrypt(msg.encode('utf-8'), public_key)
    code = base64.b64encode(code).decode('utf-8')
    code = request.quote(code)
    return code


    # 解密
    def rsa_decrypt(code):
    code = request.unquote(code)
    with open(PRIVATE_FILE_PATH, 'rb') as private_file:
    # 加载pkcs1格式公钥
    private_key = rsa.PrivateKey.load_pkcs1(private_file.read())
    code = base64.b64decode(code.encode('utf-8'))
    msg = rsa.decrypt(code, private_key).decode('utf-8')
    return msg
  • 签名与验签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 签名
    def sign(data):
    # 只能加载PKCS1格式的私钥,如果私钥是PKCS8格式的,需要将PKCS8转PKCS1再使用
    pri_key = rsa.PrivateKey.load_pkcs1(open('./pri.pem').read())
    signature = base64.b64encode(rsa.sign(data.encode('utf-8'), pri_key, 'MD5'))
    return signature


    # 验签
    def verify(signature, data):
    # 加载pkcs1格式公钥
    pub_key = rsa.PublicKey.load_pkcs1(open('./pub.pem').read())
    # 加载pkcs8格式公钥
    # pub_key = rsa.PublicKey.load_pkcs1_openssl_pem(open('./pub.pem').read())
    return rsa.verify(data, base64.b64decode(signature), pub_key)

    2. Crypto模块

    pip install pycrypto

    Python Cryptography Toolkit

  • 签名与验签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import MD5
    import base64


    def RSA_sign(data):
    # key处理方式一:
    privateKey = '''
    MIGvAgEAAiIMzxsBkAn8f9vA5z8phs0z5JT9d9xWS+9hnrMurYY7yySRAgMBAAEC
    IgTmnOudI+UDOp51G/qUhCDtkYhYKvTgENlK1DsroFYXtN0CET4oCSZvz+1GEKsQ
    bE4Ny7y7AhE0wXdLJpQgJnD4cmGmQo8VIwIRNghBpAsw+nedB8gYDmZJxP8CEStE
    eQiDrXzoykKZ3Qi1EhCtAhE20ZvBe0I1fj48Pryi+0gtVA=='''
    private_keyBytes = base64.b64decode(privateKey)
    priKey = RSA.importKey(private_keyBytes)

    # key处理方式二:
    # privateKey = open('./pri.pem').read()
    # priKey = RSA.importKey(privateKey)

    signer = PKCS1_v1_5.new(priKey)
    # 哈希算法可以采用MD5,也可以用别的比如SHA
    # data需要字节化后才能传进MD5.new()中
    hash_obj = MD5.new(data.encode('utf-8'))
    signature = base64.b64encode(signer.sign(hash_obj))
    return signature


    def verify(signature, data):
    # key处理同样是上面两种方式
    publicKey = '''
    MD0wDQYJKoZIhvcNAQEBBQADLAAwKQIiDM8bAZAJ/H/bwOc/KYbNM+SU/XfcVkvv
    YZ6zLq2GO8skkQIDAQAB'''
    public_keyBytes = base64.b64decode(publicKey)
    pubKey = RSA.importKey(public_keyBytes)
    # pubKey = RSA.importKey(publicKey)
    hash_obj = MD5.new(data.encode('utf-8'))
    verifier = PKCS1_v1_5.new(pubKey)
    return verifier.verify(hash_obj, base64.b64decode(signature)) # bool

    上面的签名代码做了如下几件事:

  • 它将Base64解码为PKCS#8
  • 它将PKCS#8解码为内存中的实际密钥(请注意,可能需要在此处提供密码)
  • 它使用所述密钥将数据(使用SHA-1或者MD5等算法进行hash)执行PKCS#1 v1.5签名
  • 它使用Base64将签名进行编码
  • 3. pycryptodome模块

    pip install pycryptodome

    segmentfault:Crypto算法库详解

    该模块和上面的pycrypto很像,但pycrypto最后一次更新是在13年,安装后引用方式是一样的,api也非常相似,安装其中一个就好了。

  • 加密与解密

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    from Crypto.PublicKey import RSA
    from Crypto.Cipher import PKCS1_v1_5
    import base64


    # 生成密钥
    def create_new_keys(len):
    rsa = RSA.generate(2048) # 返回的是密钥对象

    public_pem = rsa.publickey().exportKey('PEM') # 生成公钥字节流
    private_pem = rsa.exportKey('PEM') # 生成私钥字节流

    with open('public.pem','wb') as f:
    f.write(public_pem)
    with open('private.pem','wb') as f:
    f.write(private_pem)


    # 加密
    def rsa_encrypt(plain):
    with open('public.pem','rb') as f:
    key = RSA.importKey(f.read())
    rsa = PKCS1_v1_5.new(key)
    cipher = rsa.encrypt(plain)
    return base64.b64encode(cipher)


    # 解密
    def rsa_decrypt(cipher):
    with open('private.pem','rb') as f:
    key = RSA.importKey(f.read())
    rsa = PKCS1_v1_5.new(key)
    plain = rsa.decrypt(base64.b64decode(cipher),'ERROR') # 'ERROR'必需
    return plain
  • 签名与验签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from Crypto.Signature import pkcs1_15
    from Crypto.Hash import SHA256
    from Crypto.PublicKey import RSA


    # 签名
    def sign(data):
    key = RSA.import_key(open('private_key.pem').read())
    h = SHA256.new(data)
    rsa = pkcs1_15.new(key)
    signature = rsa.sign(h)


    # 验签
    def verify(signature, data):
    key = RSA.import_key(open('public_key.pem').read())
    hash_obj = SHA256.new(data)
    rsa = pkcs1_15.new(key)
    return rsa.verify(hash_obj, signature):

    4. cryptography模块

  •  
    推荐文章