Notas de lectura "Programación de criptografía en Python James/Christopher" (2)
Capítulo 4 Cifrado asimétrico
A través de una contraseña, se genera una clave pública y una clave privada,
y luego la clave pública cifra el texto para formar un texto cifrado, y la clave privada descifra el texto cifrado para obtener el texto.
RSA
Como uno de los algoritmos de cifrado asimétrico clásicos, está casi desactualizado, pero puede comprender algunos conceptos básicos aprendiendo 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())
La clave pública se deriva de la clave privada.
4.2 Errores RSA
La fórmula del modelo matemático encriptado c
es el texto cifrado y m
el mensaje, y los parámetros restantes son la clave pública y la clave privada.
c ≡ yo ( mod n ) \ c ≡ m^e(mod\space n) C≡metroe (modn)__
metro ≡ cd ( mod n ) \ m ≡ c^d(mod\space n) metro≡CLa conversión de d (modn)
a código se puede denominar de la siguiente manera
El siguiente código es solo para pruebas y no se puede usar en ningún entorno de producció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
Hay 3 parámetros en el método,
bytes
El primer numero es para especificar cuantos bytes convertir,
y el segundo es byteorder, que se especifica big
o little
si
El tercer signed
parámetro indica que este bytes
corresponde a un número con signo o un número sin signo int
, este es boolean
un valor que se puede omitir
junto con el código anterior
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()
Pasos:
1. Seleccione 5
, ingrese el nombre del archivo, my_private_key_file.pem
, my_public_key_file.pem
y guarde la clave pública y la clave privada en estos dos archivos.
2. Después de que A obtenga el archivo de clave pública, seleccione 3
para cargar el archivo de clave pública
. 3. Seleccione 1
, A necesita para responder Ba hot dogs
", usará la clave pública anterior para cifrar este mensaje y devolverá el texto cifrado. Estos mensajes están cifrados por A , pero no se pueden descifrar porque A no tiene una clave privada. A envía el texto cifrado generado a B
Ciphertext (hexlified): b'a8d6b19f7661fa7bcc11476df13c7c0a151db81967308afc083d9223d2492b3e139fab519b419b7dcf14c8a856ec8ff3701d9ebdf520ec4584172acd34d9d2c9e216d6abbf9697e641185104c52dedf72115ecfcf6ee34a504ed983c7bce463d5e8963d1d4111bced4c937816e52e7ce986e57bedfe5896a5d5a133c29f3f2006ac480dca589f5938092b0ac8bbad91fb20572429d079be4a8d2583c187ba3f22a6cd7b5779d6589fef39b3595363427c4c19e1a4ac78b3e62d56d17810fff1f74385f03db4051841cd6d48ff70dfc406954e50fd4aa33f392aaf510c4bb3341e3f406f68ff8b97db34821add1464f85f770b12303fa3d207650561ed3a81ab3'
4. Seleccione 4
, B cargue el archivo de clave privada
5. Seleccione 2
, solo ingrese el contenido entre las comillas de texto cifrado anteriores
4.6 Relleno de pase
4.6.2 Ataque de texto cifrado elegido (CCA)
Esta sección significa que el método anterior es fácil de descifrar, de acuerdo con un montón de fórmulas matemáticas, bla, bla, bla, bla,
4.6.3 Ataque en modo común
n
El parámetro es el módulo, si el mismo RSA
mensaje se cifra con dos n
claves públicas diferentes con el mismo módulo, se puede descifrar sin usar la clave privada
4.7 Acolchado
cryptography
El uso de ningún relleno está prohibido en RSA
, pero la biblioteca OpenSSL le permite
usar OAEP
un relleno óptimo para el cifrado asimétrico.
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()
Insecure
se ejecuta repetidamente, el texto cifrado para ambos esquemas de relleno cambiará cada vez que
el relleno garantice que la entrada siempre tenga un tamaño fijo: la entrada al módulo de bits
RSA
cifrado (tamaño de módulo 2048
) siempre será 256
bytes. Dos discusiones
sobre (1) Los parámetros generalmente son , el uso no aumentará la seguridad, por lo tanto, ignore temporalmente (2) es necesario usar el algoritmo hash, más seguroOAEP
label
None
label
OAEP
SHA256
Se acerca la computación cuántica, ahora la mayoría de los algoritmos asimétricos serán fáciles de descifrar, RSA será descifrado
Capítulo 5 Integridad de mensajes, firmas y certificados
5.2 MAC, HMAC, CBC-MAC
MAC (Message Authentication Code) es un mecanismo de autenticación utilizado por entidades de comunicación y una herramienta para garantizar la integridad de los datos del mensaje. Aquí hay 2 métodos
5.2.1 HMAC
HMAC es un código de autenticación de mensajes basado en hash
Algoritmo hash, para la misma entrada, la salida es la misma, la misma en cada computadora
Para el algoritmo sin clave, la misma entrada siempre da la misma salida, usando la clave se gana 't porque la salida depende tanto de la entrada como de la clave