"Python Cryptography Programming James/Christopher" Notas de leitura (2)
Capítulo 4 Criptografia assimétrica
Por meio de uma senha, uma chave pública e uma chave privada são geradas
e, em seguida, a chave pública criptografa o texto para formar um texto cifrado e a chave privada descriptografa o texto cifrado para obter o texto
RSA
Como um dos algoritmos clássicos de criptografia assimétrica, está quase desatualizado, mas você pode entender alguns conceitos básicos aprendendo 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())
A chave pública é derivada da chave privada
4.2 Erros RSA
A fórmula do modelo matemático criptografado c
é o texto cifrado e m
a mensagem, e os parâmetros restantes são a chave pública e a chave privada
c ≡ me ( mod n ) \ c ≡ m^e(mod\space n) c≡me (modn)
m ≡ cd ( mod n ) \ m ≡ c^d(mod\space n) m≡cA conversão de d (modn)
para código pode ser referida como segue
O código a seguir é apenas para teste e não pode ser usado em nenhum ambiente de produção
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
Existem 3 parâmetros no método,
bytes
O primeiro número é para especificar quantos bytes converter
e o segundo é byteorder, que é especificado big
ou little
sim
O terceiro signed
parâmetro indica que bytes
corresponde a um número com sinal ou sem sinal int
, este é boolean
um valor que pode ser omitido
junto com o código acima
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()
Etapas:
1. Selecione 5
, insira o nome do arquivo, my_private_key_file.pem
, my_public_key_file.pem
, e salve a chave pública e a chave privada nesses dois arquivos.
2. Depois que A obtiver o arquivo de chave pública, selecione 3
para carregar o arquivo de chave pública
. 3. Selecione 1
, A precisa para responder Ba hot dogs
", usará a chave pública anterior para criptografar esta mensagem e retornará o texto cifrado criptografado. Essas mensagens são criptografadas por A , mas não podem ser descriptografadas porque A não possui uma chave privada. A envia o texto cifrado gerado para B
Ciphertext (hexlified): b'a8d6b19f7661fa7bcc11476df13c7c0a151db81967308afc083d9223d2492b3e139fab519b419b7dcf14c8a856ec8ff3701d9ebdf520ec4584172acd34d9d2c9e216d6abbf9697e641185104c52dedf72115ecfcf6ee34a504ed983c7bce463d5e8963d1d4111bced4c937816e52e7ce986e57bedfe5896a5d5a133c29f3f2006ac480dca589f5938092b0ac8bbad91fb20572429d079be4a8d2583c187ba3f22a6cd7b5779d6589fef39b3595363427c4c19e1a4ac78b3e62d56d17810fff1f74385f03db4051841cd6d48ff70dfc406954e50fd4aa33f392aaf510c4bb3341e3f406f68ff8b97db34821add1464f85f770b12303fa3d207650561ed3a81ab3'
4. Selecione 4
, B carregue o arquivo de chave privada
5. Selecione 2
, insira apenas o conteúdo entre aspas do texto cifrado acima
4.6 preenchimento de passe
4.6.2 Ataque de texto cifrado escolhido (CCA)
Esta seção significa que o método acima é fácil de decifrar, de acordo com um monte de fórmulas matemáticas, blá blá, blá blá,
4.6.3 Ataque de modo comum
n
O parâmetro é o módulo, se a mesma RSA
mensagem for criptografada com duas n
chaves públicas diferentes com o mesmo módulo, ela pode ser descriptografada sem usar a chave privada
4.7 Preenchimento
cryptography
O uso de nenhum preenchimento é proibido em RSA
, mas a biblioteca OpenSSL permite o
uso OAEP
de preenchimento ideal para criptografia assimétrica
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()
Inseguro
é executado repetidamente, o texto cifrado para ambos os esquemas de preenchimento mudará cada vez que
o preenchimento garante que a entrada seja sempre um tamanho fixo: a entrada para o módulo bit
RSA
criptografado (modulo-size 2048
) sempre será 256
bytes. Duas discussões
sobre (1) Os parâmetros são geralmente , o uso não aumentará a segurança, então ignore temporariamente (2) a necessidade de usar o algoritmo de hash, mais seguroOAEP
label
None
label
OAEP
SHA256
A computação quântica está chegando, agora a maioria dos algoritmos assimétricos será fácil de decifrar, o RSA será decifrado
Capítulo 5 Integridade da Mensagem, Assinaturas e Certificados
5.2 MAC, HMAC, CBC-MAC
O MAC (Message Authentication Code) é um mecanismo de autenticação utilizado tanto por entidades de comunicação como uma ferramenta para garantir a integridade dos dados da mensagem. Aqui estão 2 métodos
5.2.1 HMAC
HMAC é um código de autenticação de mensagem baseado em hash
Algoritmo de hash, para a mesma entrada, a saída é a mesma, a mesma em todos os computadores
Para o algoritmo sem chave, a mesma entrada sempre dá a mesma saída, usando a chave é Ganhou 't porque a saída depende da entrada e da chave