python - 密码加密与解密

Posted 黎陌MLing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python - 密码加密与解密相关的知识,希望对你有一定的参考价值。

Python之密码加密与解密 - 对称算法


  由于计算机软件的非法复制,通信的泄密、数据安全受到威胁。一般为了安全,会要求将数据库名称、密码等信息进行加密。所以加密在开发过程中是经常使用到的技术,在一些重要场景中都有所应用,如:登录、支付、oauth等,场景不同需要搭配不一样的签名加密算法来达到业务目标。项目中用到了python端,需要用到python对密码的加密解密模块。
  加密算法分散列算法、对称加密、非对称加密。参考网上资料,初步将密码管理的逻辑思路整理了一下。

一、对称加密

  就是采用这种加密方法的双方使用方式用同样的密钥进行加密和解密。密钥是控制加密及解密过程的指令。算法是一组规则,规定如何进行加密和解密。常见的对称算法有AES、DES、3DES等。

1.1 安装第三方库 - PyCrypto

  对于对称加密或非对称都需要安装第三方库,Python中的密码库是PyCrypto,但在2012年已停止更新,现在使用 PyCrytodome 取代 PyCrypto 。
  window下安装pycryptodemo,linux下安装pycrypto

pip install pycryptodome

示例如下:

1.2 加密实现

  AES算法是目前应用最广泛的加密算法。AES有5种加密模式,分别是ECB, CBC, CTR, CFB, OFB。下面以AES的ECB模式为例,同样AES也需要加密秘钥aes_key,需要注意的是如果加密数据不足16或32位时需要补足为它们的倍数,下面以16的倍数为例:

  1. 先创建函数,不全数据不足16倍数的部分
def addStrToSpecifyLen(s,specifyLen=0):
    """
    s不是specifyLen的倍数那就补足为specifyLen的倍数
    :param s: 需要加密的参数
    :param specifyLen: 指定参数的位数
    :return: 补足位数的参数
    """
    if specifyLen <= 0:
        specifyLen = 1;
    while len(s) % specifyLen != 0:
        s += '\\0'
    return s.encode(encoding='utf-8')
  1. 加密算法 - aes
def encrypt_aes(text='', key=''):
    """
    aes的ecb模式加密
    :param data: 加密数据
    :param aes_key: 加密的秘钥
    :return: 加密之后的密文
    """
    # 初始化加密器
    aes = AES.new(addStrToSpecifyLen(key,16), AES.MODE_ECB)
    # 先进行aes加密
    encrypt = aes.encrypt(addStrToSpecifyLen(text,16))
    # 用base64转成字符串形式
    encrypted_text = str(base64.encodebytes(encrypt), encoding='utf-8')  # 执行加密并转码返回bytes
    return encrypted_text
  1. 解密算法
def decrypt_aes(data, aes_key):
    """
    aes的ecb模式解密
    :param data: 待解密数据
    :param aes_key: 加密的秘钥
    :return: 解密之后的数据
    """
    # 初始化加密器
    aes = AES.new(addStrToSpecifyLen(aes_key,16), AES.MODE_ECB)
    #优先逆向解密base64成bytes
    base64_decrypted = base64.decodebytes(addStrToSpecifyLen(data,16))
    #执行解密密并转码返回str
    decrypted_text = str(aes.decrypt(base64_decrypted),encoding='utf-8').replace('\\0','')
    return decrypted_text
  1. 测试输出如下:
if __name__ == '__main__':
    key = '12223'
    data = 'test12dcds'
    encrypt = encrypt_aes(data,key)
    print(encrypt)
    print(decrypt_aes(encrypt,key))

二、非对称加密

  与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。常见的非对称算法有RSA、DSA、ECC等。

三、摘要算法

  Python的hashlib提供了常见的摘要算法,如MD5、SHA1、SHA224、SHA256、SHA384、SHA512等算法。摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
   一般用于网络通信中消息加密,前提是双方先要约定好key,就像接头暗号一样,然后消息发送把用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值 跟 发送者的相对比是否相等,这样就能验证消息的真实性,及发送者的合法性了。
   摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。散列算法加密数据一般采用base64编码格式。常用的散列算示例如下:

3.1 md5加密

 是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。

import hashlib
hash = hashlib.md5()
hash.update("mayi".encode("utf-8"))
# 7d1080e20427559fcc0a647826741f66
print(hash.hexdigest())

3.2 sha1加密

 SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。

import hashlib
hash = hashlib.sha1()
hash.update("mayi".encode("utf-8"))
# c159ce3114fb4553683cf96d91db6d51080c02e8
print(hash.hexdigest())

3.3 sha256加密

比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长。

import hashlib
hash = hashlib.sha256()
hash.update("mayi".encode("utf-8"))
# 5dfae51e782cce2f213ef6bc89f75c9ab6c3bd8a5d1299a73191677cd5aa1f93
print(hash.hexdigest())

3.4 sha384加密

import hashlib
hash = hashlib.sha384()
hash.update("mayi".encode("utf-8"))
# a1eb5c52e830d5ea4fdb0a3dc2241374f56426aebacd8890a69c7db57724788ec5047a005ecff4a23310b7f87035926f
print(hash.hexdigest())

3.5 sha512加密

import hashlib
hash = hashlib.sha512()
hash.update("mayi".encode("utf-8"))
# 93102ec5658f739c060e3d82096e538ec116d0c9d6925119b465f0823be99697056518465cc6fe75265deb26632c8ce62b3d63a8782c492
daac2b9c03a89defe
print(hash.hexdigest())

3.6 “加盐”加密

  以上加密算法虽然很厉害,但仍然存在缺陷,通过撞库可以反解。所以必要对加密算法中添加自定义key(通过加入用户名或者随机字符等)再来做加密。

import hashlib
hash = hashlib.md5('python'.encode('utf-8'))
hash.update("mayi".encode("utf-8"))
# b0758ad1aad20530044668775f389922
print(hash.hexdigest())

python中的RSA加密与解密

什么是RSA:

RSA公开密钥密码体制。所谓的公开密钥密码体制就是使用不同的加密密钥与解密密钥,是一种“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。

在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK。

正是基于这种理论,1978年出现了著名的RSA算法,它通常是先生成一对RSA 密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可在网络服务器中注册。为提高保密强度,RSA密钥至少为500位长,一般推荐使用1024位。这就使加密的计算量很大。为减少计算量,在传送信息时,常采用传统加密方法与公开密钥加密方法相结合的方式,即信息采用改进的DES或IDEA密钥加密,然后使用RSA密钥加密对话密钥和信息摘要。对方收到信息后,用不同的密钥解密并可核对信息摘要。

RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作。RSA是被研究得最广泛的公钥算法,从提出到现今的三十多年里,经历了各种攻击的考验,逐渐为人们接受,截止2017年被普遍认为是最优秀的公钥方案之一。
SET(Secure Electronic Transaction)协议中要求CA采用2048bits长的密钥,其他实体使用1024比特的密钥。RSA密钥长度随着保密级别提高,增加很快。下表列出了对同一安全级别所对应的密钥长度。

python实现对RSA的加密和解密

Python密码库--Pycrypto
Python良好的生态,对于加密解密技术都有成熟的第三方库。大名鼎鼎的M2Crypto和Pycrypto,前者非常容易使用,可是安装却非常头疼,不同的系统依赖软件的版本还有影响。后者则比较方面,直接使用pip安装即可。

密码技术
为了进行加密以及通信,人们发明了很多公开的算法。对称与非对称算法等。常见的加密方式有RSA, AES等算法。对于选择加密算法,一个常识就是使用公开的算法。一方面是这些算法经过实践检验,另一方面对于破译难度和破译条件破译时间都有预估。对于任何加密算法,都是能破解的,不同在于时间上的投入。

Python密码库--Pycrypto
Python良好的生态,对于加密解密技术都有成熟的第三方库。大名鼎鼎的M2Crypto和Pycrypto,前者非常容易使用,可是安装却非常头疼,不同的系统依赖软件的版本还有影响。后者则比较方面,直接使用pip安装即可。

安装

pip install pycrypto

RSA 密码算法与签名
RSA是一种公钥密码算法,RSA的密文是对代码明文的数字的 E 次方求mod N 的结果。也就是将明文和自己做E次乘法,然后再将其结果除以 N 求余数,余数就是密文。RSA是一个简洁的加密算法。E 和 N 的组合就是公钥(public key)。

对于RSA的解密,即密文的数字的 D 次方求mod N 即可,即密文和自己做 D 次乘法,再对结果除以 N 求余数即可得到明文。D 和 N 的组合就是私钥(private key)。

算法的加密和解密还是很简单的,可是公钥和私钥的生成算法却不是随意的。本文在于使用,对生成秘钥对的算法就暂时忽略。使用 Pycrypto生成秘钥对很简单,我们分别为 Server和Client各生成一对属于自己的秘钥对。

from Crypto import Random
from Crypto.Hash import SHA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from Crypto.PublicKey import RSA
 
# 伪随机数生成器
random_generator = Random.new().read
# rsa算法生成实例
rsa = RSA.generate(1024, random_generator)
 
# Server的秘钥对的生成
private_pem = rsa.exportKey()
with open("server-private.pem", "w") as f:
  f.write(private_pem)
 
public_pem = rsa.publickey().exportKey()
with open("server-public.pem", "w") as f:
  f.write(public_pem)
 
# Client的秘钥对的生成
private_pem = rsa.exportKey()
with open("client-private.pem", "w") as f:
  f.write(private_pem)
 
public_pem = rsa.publickey().exportKey()
with open("client-public.pem", "w") as f:
  f.write(public_pem)

  

所生成的私钥和公钥大概是这样的:

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC26X6A0WCWiVxdxq3jtm42yDdGbf+99v2zyi0UMVGZfowlnkcW
eMvpz8NBm2UVlrjZpnFr8wFkdHyjFFkq/ilclH3AN4+Xw8Ap7CGJ2jVMyS5h9RRB
Uyf3F4D5Og8789Ywh9HXYyvD/6J62EtbbkhGPxg3aa8n2kfKZ9N6Q7DqrwIDAQAB
AoGBAJn5qu1D1FxE24Vxl7ZGPzdMigN227+NaPptak9CSR++gLm2KL+JBpcXt5XF
+20WCRvnWjl2QijPSpB5s6pWdHezEa1cl6WrqB1jDJd1U99WNCL5+nfEVD9IF+uE
ig0pnj+wAT5fu78Z0UjxD9307f9S7BLC8ou3dWVkIqob6W95AkEAuPGTNlTkquu/
wBJTb4/+/2ZCf7Ci9qvsN3+RcrzFkKa3uTtBOa6Xk2R61zBkucUgY6cQHPbxhFLN
TVmXdbwxTQJBAP0wGenVOq4dCPdz3NhyghkKT6SL2w/SgbrROiJ1mG9MoBq58/0g
k85I91R7nuvOYTKTUkhWdPYITpDarmPJzesCQGRBmOMgHCHH0NfHV3Gn5rz+61eb
IoyD4Hapceh4CsWCiyAfzhj9229sTecvdbr68Lb0zphVCdIIrQCca63IShUCQGYI
e3jzmHlQdCudArQruWgz8pKiVf7TW7qY1O/MKkk4PRFoPP6WoVoxp5LhWtM20Y7b
Nf628N2xzU+tAThvvE8CQQCI1C7GO3I5EMfqPbTSq2oZq8thvGlyFyI7SNNuvAHj
hj2+0217B9CcTZqloYln01CNDVuaoUgEvFSw1OdRB1tC
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC26X6A0WCWiVxdxq3jtm42yDdG
bf+99v2zyi0UMVGZfowlnkcWeMvpz8NBm2UVlrjZpnFr8wFkdHyjFFkq/ilclH3A
N4+Xw8Ap7CGJ2jVMyS5h9RRBUyf3F4D5Og8789Ywh9HXYyvD/6J62EtbbkhGPxg3
aa8n2kfKZ9N6Q7DqrwIDAQAB
-----END PUBLIC KEY-----

  

加密与解密
通常通信的时候,发送者使用接受者的公钥加密,接受者使用接受者私钥解密。

简而言之,Server给Client通信,需要加密内容,那么Client会生成一个秘钥对,Client的公钥client-public.pem和私钥client-private.pem 。Client把公钥公开给发送者,任何人都可以用来加密,然后Server使用client-public.pem进行加密,然后把内容发给Client,Client再使用私钥client-private.pem进行解密。

1.加密(encrypt)

# Server使用Client的公钥对内容进行rsa 加密
 
message = "hello client, this is a message"
with open("client-public.pem") as f:
    key = f.read()
    rsakey = RSA.importKey(key)
    cipher = Cipher_pkcs1_v1_5.new(rsakey)
    cipher_text = base64.b64encode(cipher.encrypt(message))
    print cipher_text
 
#加密结果: 
HYQPGB+axWCbPp7PPGNTJEAhVPW0TX5ftvUN2v40ChBLB1pS+PVM3YGT5vfcsvmPZhW8NKVSBp8FwjLUnMn6yXP1O36NaunUzyHwI+cpjlkTwZs3DfCY/32EzeuKuJABin1FHBYUMTOKtHy+eEDOuaJTnZTC7ZBkdha+J88HXSc=

cipher_text 即 Master加密后将要发送给Client的密文。

2.解密(decrypt)

# Client使用自己的私钥对内容进行rsa 解密 
 
with open("client-private.pem") as f:
    key = f.read()
    rsakey = RSA.importKey(key)
    cipher = Cipher_pkcs1_v1_5.new(rsakey)
    text = cipher.decrypt(base64.b64decode(encrypt_text), random_generator)
    print text

#解密结果:
hello client, this is a message

 这样Client就能看到Server所发的内容了,当然,如果Client想要给Server发消息,就需要Server先把其的公钥给Client,后者再使用公钥加密,然后发送给Server,最后Server使用自己的私钥解密。

签名与验签
当然,对于窃听者,有时候也可以对伪造Server给Client发送内容。为此出现了数字签名。也就是Server给Client发送消息的时候,先对消息进行签名,表明自己的身份,并且这个签名无法伪造。具体过程即Server使用自己的私钥对内容签名,然后Client使用Server的公钥进行验签。

签名

# Server使用自己的私钥对内容进行签名

with open("server-private.pem") as f:
    key = f.read()
    rsakey = RSA.importKey(key)
    signer = Signature_pkcs1_v1_5.new(rsakey)
    digest = SHA.new()
    digest.update(message)
    sign = signer.sign(digest)
    signature = base64.b64encode(sign)
    print signature

#签名结果:
jVUcAYfgF5Pwlpgrct3IlCX7KezWqNI5tD5OIFTrfCOQgfyCrOkN+/gRLsMiSDOHhFPj2LnfY4Cr5u4eG2IiH8+uSF5z4gUX48AqCQlqiOTLk2EGvyp+w+iYo2Bso1MUi424Ebkx7SnuJwLiPqNzIBLfEZLA3ov69aDArh6hQiw=

  

验签

#Client使用Server的公钥对内容进行验签

with open("server-public.pem") as f:
    key = f.read()
    rsakey = RSA.importKey(key)
    verifier = Signature_pkcs1_v1_5.new(rsakey)
    digest = SHA.new()
    # Assumes the data is base64 encoded to begin with
    digest.update(message)
    is_verify = signer.verify(digest, base64.b64decode(signature))
    print is_verify

#验签结果:
True

  

  

总结
Pycrypto提供了比较完善的加密算法。RSA广泛用于加密与解密,还有数字签名通信领域。使用Publick/Private秘钥算法中,加密主要用对方的公钥,解密用自己的私钥。签名用自己的私钥,验签用对方的公钥。

  • 加密解密:公钥加密,私钥解密
  • 签名验签:私钥签名,公钥验签

无论是加密解密还是签名验签都使用同一对秘钥对。

以上是关于python - 密码加密与解密的主要内容,如果未能解决你的问题,请参考以下文章

Python加密与解密

python 加密与解密

Python使用rsa模块实现非对称加密与解密

python中的AES加密与解密

Python中的密码加密和解密

python字符串加密与反解密