《Python 密码学编程》读书笔记(2)

第 4 章 非对称加密

通过一个密码,生成公钥私钥
然后公钥加密文本形成密文,私钥解密密文,得到文本

RSA作为经典的非对称加密算法之一,几乎已经过时了,但是可以通过学习RSA了解一些核心概念

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend

# 生成一个私钥
private_key = rsa.generate_private_key(public_exponent=65537,key_size=2048,backend=default_backend())# 参数就这样设定,文档建议
# 从私钥中提取公钥
public_key = private_key.public_key()
# 把私钥转成字节,这一次不加密它
private_key_bytes = private_key.private_bytes(encoding=serialization.Encoding.PEM,
                                              format=serialization.PrivateFormat.TraditionalOpenSSL,
                                              encryption_algorithm=serialization.NoEncryption()
                                            )
# 把公钥转成字节
public_key_bytes = public_key.public_bytes(encoding=serialization.Encoding.PEM,
                                           format=serialization.PublicFormat.SubjectPublicKeyInfo)

# 把私钥字节转回key
private_key = serialization.load_pem_private_key(private_key_bytes,backend=default_backend(),password=None)
public_key = serialization.load_pem_public_key(public_key_bytes,backend=default_backend())

在这里插入图片描述
公钥是从私钥派生的

4.2 RSA 出错

加密的数学模型公式,c是密文,m是消息,剩下的参数是公钥和私钥

  c ≡ m e ( m o d   n ) \ c ≡ m^e(mod\space n)  cme(mod n)

  m ≡ c d ( m o d   n ) \ m ≡ c^d(mod\space n)  mcd(mod n)
转换为代码可以参考如下

以下代码只用来测试,不能用于任何生产环境

import gmpy2, os, binascii
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

def simple_rsa_encrypt(m, publickey):
    numbers = publickey.public_numbers()
    return gmpy2.powmod(m, numbers.e, numbers.n)

def simple_rsa_decrypt(c, privatekey):
    numbers = privatekey.private_numbers()
    return gmpy2.powmod(c, numbers.d, numbers.public_numbers.n)
    
def int_to_byte(i):
    i = int(i)
    return i.to_bytes((i.bit_length()+7)//8,byteorder='big')

def bytes_to_int(b):
    return int.from_bytes(b,byteorder='big')

to_bytes 方法里面有3个参数 ,

第一个数是指定要转换的bytes占多少个字节
第二个是byteorder 是指定big或者是little
在这里插入图片描述

第三个是signed参数表示这个bytes对应的是有符号的数,或者无符号的int,这个是boolean值可以不写
和上面的代码一起

def main():
    public_key_file = None
    private_key_file = None
    public_key = None
    private_key = None
    while True:
        print("Simple RSA Crypto")
        print("--------------------")
        print("\tprviate key file: {}".format(private_key_file))
        print("\tpublic key file: {}".format(public_key_file))
        print("\t1. Encrypt Message.")
        print("\t2. Decrypt Message.")
        print("\t3. Load public key file.")
        print("\t4. Load private key file.")
        print("\t5. Create and load new public and private key files.")
        print("\t6. Quit.\n")
        choice = input(">> ")
        if choice == '1':
            if not public_key:
                print("\nNo public key loaded\n")
            else:
                message = input("\nPlaintext: ").encode()# 字符串encode之后可以转为一个整数
                message_as_int = bytes_to_int(message)
                cipher_as_int = simple_rsa_encrypt(message_as_int, public_key)
                cipher = int_to_bytes(cipher_as_int)
                print("\nCiphertext (hexlified): {}\n".format(binascii.hexlify(cipher)))# binascii.hexlify返回一个数据的16进制表示
        elif choice == '2':
            if not private_key:
                print("\nNo private key loaded\n")
            else:
                cipher_hex = input("\nCiphertext (hexlified): ").encode()
                cipher = binascii.unhexlify(cipher_hex)
                cipher_as_int = bytes_to_int(cipher)
                message_as_int = simple_rsa_decrypt(cipher_as_int, private_key)
                message = int_to_bytes(message_as_int)
                print("\nPlaintext: {}\n".format(message))
        elif choice == '3':
            public_key_file_temp = input("\nEnter public key file: ")
            if not os.path.exists(public_key_file_temp):
                print("File {} does not exist.")
            else:
                with open(public_key_file_temp, "rb") as public_key_file_object:
                    public_key = serialization.load_pem_public_key(
                                     public_key_file_object.read(),
                                     backend=default_backend())
                    public_key_file = public_key_file_temp
                    print("\nPublic Key file loaded.\n")

                    # unload private key if any
                    private_key_file = None
                    private_key = None
        elif choice == '4':
            private_key_file_temp = input("\nEnter private key file: ")
            if not os.path.exists(private_key_file_temp):
                print("File {} does not exist.")
            else:
                with open(private_key_file_temp, "rb") as private_key_file_object:
                    private_key = serialization.load_pem_private_key(
                                     private_key_file_object.read(),
                                     backend=default_backend(),
                                     password=None)
                    private_key_file = private_key_file_temp
                    print("\nPrivate Key file loaded.\n")

                    # load public key for private key
                    # (unload previous public key if any)
                    public_key = private_key.public_key()
                    public_key_file = None
        elif choice == '5':
            private_key_file_temp = input("\nEnter a file name for new private key: ")
            public_key_file_temp  = input("\nEnter a file name for a new public key: ")
            if os.path.exists(private_key_file_temp) or os.path.exists(public_key_file_temp):
                print("File already exists.")
            else:
                with open(private_key_file_temp, "wb+") as private_key_file_obj:
                    with open(public_key_file_temp, "wb+") as public_key_file_obj:

                        private_key = rsa.generate_private_key(
                                          public_exponent=65537,
                                          key_size=2048,
                                          backend=default_backend()
                                      )
                        public_key = private_key.public_key()

                        private_key_bytes = private_key.private_bytes(
                            encoding=serialization.Encoding.PEM,
                            format=serialization.PrivateFormat.TraditionalOpenSSL,
                            encryption_algorithm=serialization.NoEncryption()
                        )
                        private_key_file_obj.write(private_key_bytes)
                        public_key_bytes = public_key.public_bytes(
                            encoding=serialization.Encoding.PEM,
                            format=serialization.PublicFormat.SubjectPublicKeyInfo
                        )
                        public_key_file_obj.write(public_key_bytes)

                        public_key_file = None
                        private_key_file = private_key_file_temp
        elif choice == '6':
            print("\n\nTerminaing. This program will self destruct in 5 seconds.\n")
            break
        else:
            print("\n\nUnknown option {}.\n".format(choice))

if __name__ == '__main__':
    main()

步骤:
1,选5,输入文件文件名, my_private_key_file.pemmy_public_key_file.pem,将公钥和私钥保存到这两个文件中
2,A 拿到公钥文件之后,选3 加载公钥文件
3,选1AB 要回信,“hot dogs”,将使用前面的公钥对这个消息加密,返回加密后的密文,这些消息是 A 加密的,但是因为 A 没有私钥,所以也不能解密。A 把生成的密文发送给 B

Ciphertext (hexlified): b'a8d6b19f7661fa7bcc11476df13c7c0a151db81967308afc083d9223d2492b3e139fab519b419b7dcf14c8a856ec8ff3701d9ebdf520ec4584172acd34d9d2c9e216d6abbf9697e641185104c52dedf72115ecfcf6ee34a504ed983c7bce463d5e8963d1d4111bced4c937816e52e7ce986e57bedfe5896a5d5a133c29f3f2006ac480dca589f5938092b0ac8bbad91fb20572429d079be4a8d2583c187ba3f22a6cd7b5779d6589fef39b3595363427c4c19e1a4ac78b3e62d56d17810fff1f74385f03db4051841cd6d48ff70dfc406954e50fd4aa33f392aaf510c4bb3341e3f406f68ff8b97db34821add1464f85f770b12303fa3d207650561ed3a81ab3'

4,选4B 加载私钥文件
5,选2,只输入上面密文 引号 里面的内容

4.6 传递填充

4.6.2 选择性密文攻击(CCA)

这一节就是说上面的方法很容易破解,根据一堆数学公式,吧啦吧啦,

4.6.3 共模攻击

n参数是模数,如果同一个RSA消息有两个具有相同n模的不通公钥加密,则可在不使用私钥的情况下对其解密

4.7 填充

cryptography 中禁止使用没有填充的RSA,但是OpenSSL 库允许这样做
使用OAEP 最佳的非对称加密填充

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

def main():
    message = b'test'
    
    private_key = rsa.generate_private_key(
          public_exponent=65537,
          key_size=2048,
          backend=default_backend()
      )
    public_key = private_key.public_key()
    
    ciphertext1 = public_key.encrypt(
        message,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None # rarely used. Just leave it 'None'
        )
    )
    
    ###
    # WARNING: PKCS #1 v1.5 is obsolete and has vulnerabilities
    # DO NOT USE EXCEPT WITH LEGACY PROTOCOLS
    ciphertext2 = public_key.encrypt(
        message,
        padding.PKCS1v15()
    )
    
    recovered1 = private_key.decrypt(
    ciphertext1,
     padding.OAEP(
         mgf=padding.MGF1(algorithm=hashes.SHA256()),
         algorithm=hashes.SHA256(),
         label=None # rarely used. Just leave it 'None'
     ))
       
    recovered2 = private_key.decrypt(
    ciphertext2,
     padding.PKCS1v15()
 )
    
    print("Plaintext: {}".format(message))
    print("Ciphertext with PKCS #1 v1.5 padding (hexlified): {}".format(ciphertext1.hex()))
    print("Ciphertext with OAEP padding (hexlified): {}".format(ciphertext2.hex()))
    print("Recovered 1: {}".format(recovered1))
    print("Recovered 2: {}".format(recovered2))
    if ciphertext1 != ciphertext2 and recovered1 == message and recovered2 == message:
        print("[PASS]")
    
if __name__=="__main__":
    main()

padding.PKCS1v15() 不安全
重复运行,两种填充方案的密文每次都会改变
填充确保输入总是固定的大小:模数位数
RSA加密的输入(模数大小为2048)将始终是256字节。
关于OAEP 的两条讨论
(1)label参数通常为None,使用label不会增加安全性,所以暂时忽略
(2)OAEP需要使用哈希算法,SHA256更安全

量子计算来临,现在大多数非对称算法将被易于破解,RSA将被破解

第 5 章 消息完整性,签名和证书

5.2 MAC 、HMAC、CBC-MAC

MAC(消息验证码)通信实体双方使用的一种验证机制,保证消息数据完整性的一种工具。下面介绍2种方法

5.2.1 HMAC

HMAC 是基于哈希的消息验证码
哈希算法,对于相同的输入,输出也相同,每台计算机上都相同
对于未使用密钥的算法,相同的输入总是得到相同的输出,使用密钥就不会这样,因为输出同时依赖于输入和密钥

猜你喜欢

转载自blog.csdn.net/weixin_44831720/article/details/123201112