通过一个密码,生成公钥私钥
然后公钥加密文本形成密文,私钥解密密文,得到文本
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())
公钥是从私钥派生的
加密的数学模型公式,c
是密文,m
是消息,剩下的参数是公钥和私钥
c ≡ m e ( m o d n ) \ c ≡ m^e(mod\space n) c≡me(mod n)
m
≡
c
d
(
m
o
d
n
)
\ m ≡ c^d(mod\space n)
m≡cd(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.pem
,my_public_key_file.pem
,将公钥和私钥保存到这两个文件中
2,A 拿到公钥文件之后,选3
加载公钥文件
3,选1
,A 给 B 要回信,“hot dogs
”,将使用前面的公钥对这个消息加密,返回加密后的密文,这些消息是 A 加密的,但是因为 A 没有私钥,所以也不能解密。A 把生成的密文发送给 B
Ciphertext (hexlified): b'a8d6b19f7661fa7bcc11476df13c7c0a151db81967308afc083d9223d2492b3e139fab519b419b7dcf14c8a856ec8ff3701d9ebdf520ec4584172acd34d9d2c9e216d6abbf9697e641185104c52dedf72115ecfcf6ee34a504ed983c7bce463d5e8963d1d4111bced4c937816e52e7ce986e57bedfe5896a5d5a133c29f3f2006ac480dca589f5938092b0ac8bbad91fb20572429d079be4a8d2583c187ba3f22a6cd7b5779d6589fef39b3595363427c4c19e1a4ac78b3e62d56d17810fff1f74385f03db4051841cd6d48ff70dfc406954e50fd4aa33f392aaf510c4bb3341e3f406f68ff8b97db34821add1464f85f770b12303fa3d207650561ed3a81ab3'
4,选4
,B 加载私钥文件
5,选2
,只输入上面密文 引号 里面的内容
这一节就是说上面的方法很容易破解,根据一堆数学公式,吧啦吧啦,
n
参数是模数,如果同一个RSA
消息有两个具有相同n
模的不通公钥加密,则可在不使用私钥的情况下对其解密
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将被破解
MAC(消息验证码)通信实体双方使用的一种验证机制,保证消息数据完整性的一种工具。下面介绍2种方法
HMAC 是基于哈希的消息验证码
哈希算法,对于相同的输入,输出也相同,每台计算机上都相同
对于未使用密钥的算法,相同的输入总是得到相同的输出,使用密钥就不会这样,因为输出同时依赖于输入和密钥