• 《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())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

    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')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    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
    • 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
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    步骤:
    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'
    
    • 1

    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()
    
    • 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
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

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

  • 相关阅读:
    拓端tecdat|R语言时间序列分解和异常检测方法应用案例
    基于R语言的raster包读取遥感影像
    强化学习与ChatGPT:快速让AI学会玩贪食蛇游戏!
    在uni-app中使用ECharts - 配置四种不同的图表
    sh脚本工具集锦(文件批量操作、音视频相关)持续更新
    网络安全——CVE-2015-1635-HTTP远程代码执行
    不会写文档的程序员不是好的程序员
    分布式电源接入对配电网影响分析(Matlab代码实现)
    Java 手机上传图片打水印时,图片被翻转90度
    电影下载工具推荐
  • 原文地址:https://blog.csdn.net/weixin_44831720/article/details/123201112