web-dev-qa-db-fra.com

Comment puis-je chiffrer avec une clé privée RSA en python?

Est-il possible de chiffrer un message avec une clé privée en python en utilisant pycryptodome ou toute autre bibliothèque? Je sais que vous n'êtes pas censé chiffrer avec la clé privée et déchiffrer avec la clé publique, mais mon but est de chiffrer avec le privé afin que le destinataire puisse être sûr que le message a été envoyé par le véritable auteur. Plus qu'un chiffrement sécurisé, je recherche une sorte d'obscurcissement. Je veux faire une application où le message est public mais il ne peut être vu que si vous avez la clé publique. J'ai essayé de le faire:

from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64


def generate_keys():
    modulus_lenght = 256 * 4
    private_key = RSA.generate(modulus_lenght, Random.new().read)
    public_key = private_key.publickey()
    return private_key, public_key

def encrypt_private_key(a_message, private_key):
    encryptor = PKCS1_OAEP.new(private_key)
    encrypted_msg = encryptor.encrypt(a_message)
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
   return encoded_encrypted_msg

def decrypt_public_key(encoded_encrypted_msg, public_key):
    encryptor = PKCS1_OAEP.new(public_key)
    decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
    return decoded_decrypted_msg

private_key, public_key = generate_keys()

message = "Hello world"

encoded = encrypt_private_key(message, private_key)
decoded = decrypt_public_key(encoded, public_key)

print decoded

Mais cela soulève l'erreur suivante: TypeError: Ce n'est pas une clé privée.

5
Cesar Cabrera

Réponse courte

  • le code que vous utilisez ne vous permet pas de le faire pour des raisons de sécurité
  • code alternatif ci-dessous

Réponse longue

J'étais curieux de connaître votre problème, puis j'ai commencé à essayer de coder

Après un certain temps, j'ai réalisé que si vous exécutez cet extrait, vous verrez qu'il fonctionne correctement:

#!/usr/bin/env python

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64

def generate_keys():
    modulus_length = 1024

    key = RSA.generate(modulus_length)
    #print (key.exportKey())

    pub_key = key.publickey()
    #print (pub_key.exportKey())

    return key, pub_key

def encrypt_private_key(a_message, private_key):
    encryptor = PKCS1_OAEP.new(private_key)
    encrypted_msg = encryptor.encrypt(a_message)
    print(encrypted_msg)
    encoded_encrypted_msg = base64.b64encode(encrypted_msg)
    print(encoded_encrypted_msg)
    return encoded_encrypted_msg

def decrypt_public_key(encoded_encrypted_msg, public_key):
    encryptor = PKCS1_OAEP.new(public_key)
    decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
    print(decoded_encrypted_msg)
    decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
    print(decoded_decrypted_msg)
    #return decoded_decrypted_msg

def main():
  private, public = generate_keys()
  print (private)
  message = b'Hello world'
  encoded = encrypt_private_key(message, public)
  decrypt_public_key(encoded, private)

if __name__== "__main__":
  main()

mais si vous changez maintenant deux des dernières lignes [c.-à-d. le rôle des touches] en:

    encoded = encrypt_private_key(message, private)
    decrypt_public_key(encoded, public)

et relancez le programme, vous obtiendrez le TypeError: No private key

Permettez-moi de citer cette excellente réponse :

"En fait, PyCrypto essaie seulement de vous empêcher de vous confondre ici, OpenSSL ou Ruby OpenSSL vous permet par exemple de faire les deux: public_encrypt/public_decrypt et private_encrypt/private_decrypt

[...]

Des éléments supplémentaires doivent être pris en compte pour rendre le résultat utilisable dans la pratique. Et c'est pourquoi il y a un package de signature dédié dans PyCrypto - cela fait effectivement ce que vous avez décrit, mais s'occupe également des choses que j'ai mentionnées "

Adapter ce lien Je suis arrivé au code suivant qui devrait résoudre votre question:

# RSA helper class for pycrypto
# Copyright (c) Dennis Lee
# Date 21 Mar 2017

# Description:
# Python helper class to perform RSA encryption, decryption, 
# signing, verifying signatures & keys generation

# Dependencies Packages:
# pycrypto 

# Documentation:
# https://www.dlitz.net/software/pycrypto/api/2.6/

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA512, SHA384, SHA256, SHA, MD5
from Crypto import Random
from base64 import b64encode, b64decode
import rsa

hash = "SHA-256"

def newkeys(keysize):
    random_generator = Random.new().read
    key = RSA.generate(keysize, random_generator)
    private, public = key, key.publickey()
    return public, private

def importKey(externKey):
    return RSA.importKey(externKey)

def getpublickey(priv_key):
    return priv_key.publickey()

def encrypt(message, pub_key):
    #RSA encryption protocol according to PKCS#1 OAEP
    cipher = PKCS1_OAEP.new(pub_key)
    return cipher.encrypt(message)

def decrypt(ciphertext, priv_key):
    #RSA encryption protocol according to PKCS#1 OAEP
    cipher = PKCS1_OAEP.new(priv_key)
    return cipher.decrypt(ciphertext)

def sign(message, priv_key, hashAlg="SHA-256"):
    global hash
    hash = hashAlg
    signer = PKCS1_v1_5.new(priv_key)
    if (hash == "SHA-512"):
        digest = SHA512.new()
    Elif (hash == "SHA-384"):
        digest = SHA384.new()
    Elif (hash == "SHA-256"):
        digest = SHA256.new()
    Elif (hash == "SHA-1"):
        digest = SHA.new()
    else:
        digest = MD5.new()
    digest.update(message)
    return signer.sign(digest)

def verify(message, signature, pub_key):
    signer = PKCS1_v1_5.new(pub_key)
    if (hash == "SHA-512"):
        digest = SHA512.new()
    Elif (hash == "SHA-384"):
        digest = SHA384.new()
    Elif (hash == "SHA-256"):
        digest = SHA256.new()
    Elif (hash == "SHA-1"):
        digest = SHA.new()
    else:
        digest = MD5.new()
    digest.update(message)
    return signer.verify(digest, signature)

def main():
    msg1 = b"Hello Tony, I am Jarvis!"
    msg2 = b"Hello Toni, I am Jarvis!"

    keysize = 2048

    (public, private) = rsa.newkeys(keysize)

    # https://docs.python.org/3/library/base64.html
    # encodes the bytes-like object s
    # returns bytes
    encrypted = b64encode(rsa.encrypt(msg1, private))
    # decodes the Base64 encoded bytes-like object or ASCII string s
    # returns the decoded bytes
    decrypted = rsa.decrypt(b64decode(encrypted), private)
    signature = b64encode(rsa.sign(msg1, private, "SHA-512"))

    verify = rsa.verify(msg1, b64decode(signature), public)

    #print(private.exportKey('PEM'))
    #print(public.exportKey('PEM'))
    print("Encrypted: " + encrypted.decode('ascii'))
    print("Decrypted: '%s'" % (decrypted))
    print("Signature: " + signature.decode('ascii'))
    print("Verify: %s" % verify)
    rsa.verify(msg2, b64decode(signature), public)

if __name__== "__main__":
    main()

Notes finales:

  • les derniers print ont ascii car comme indiqué ici "En cas de base64 cependant, tous les caractères sont valides ASCII caractères"
  • dans ce cas, nous utilisons la même clé - la clé privée - à la fois pour le cryptage et le décryptage, alors oui: nous finirions par être symétriques mais ...
  • mais - comme indiqué ici - "La clé publique est PUBLIQUE - c'est quelque chose que vous partageriez facilement et donc serait facilement diffusé. Il n'y a aucune valeur ajoutée dans ce cas par rapport à l'utilisation d'un chiffrement symétrique et d'une clé partagée "plus" Conceptuellement, le "chiffrement" avec la clé privée est plus utile pour signer un message alors que le "déchiffrement" utilisant la clé publique est utilisé pour vérifier le message "
  • le même dernier principe identique est exprimé dans cette réponse - "Typiquement [...] nous disons signer avec la clé privée et vérifier avec la clé publique"
3
Antonino

Ce que vous décrivez s'appelle signature de message et utilise des clés privées/publiques pour vérifier que le message provient bien de l'expéditeur déclaré et qu'il n'a pas été falsifié en cours de route. Vous n'avez pas à "inventer" ces méthodes ...

https://medium.com/@securegns/implementing-asymmetric-encryption-to-secure-your-project-35368049cb5f

0
Mike Robinson

Il semble que pycrypto n'ait pas été en développement actif depuis 2014 et le support a pris fin à python 3.3. cryptography semble être la norme maintenant.

Utilisation de cryptography:

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

password = b'thepassword'

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

private_key = key.private_bytes(
    serialization.Encoding.PEM,
    serialization.PrivateFormat.PKCS8,
    serialization.BestAvailableEncryption(password)
)

public_key = key.public_key().public_bytes(
    serialization.Encoding.OpenSSH,
    serialization.PublicFormat.OpenSSH
)
0
zemekeneng