开发者

openssl_seal() in Python

开发者 https://www.devze.com 2022-12-29 06:56 出处:网络
To connect a server, I\'ve found that, using PHP, I need to use openssl_seal(). That\'s OK, but I want开发者_开发百科 to use Python. I\'m not able to convert openssl_seal() in an equivalent function.

To connect a server, I've found that, using PHP, I need to use openssl_seal(). That's OK, but I want开发者_开发百科 to use Python. I'm not able to convert openssl_seal() in an equivalent function.

Can you help me?

This is what openssl_seal() does:

Description int openssl_seal ( string $data , string &$sealed_data , array &$env_keys , array $pub_key_ids )

openssl_seal() seals (encrypts) data by using RC4 with a randomly generated
secret key. The key is encrypted with each of the public keys associated
with the identifiers in pub_key_ids and each encrypted key is returned in
env_keys. This means that one can send sealed data to multiple recipients
(provided one has obtained their public keys). Each recipient must receive
both the sealed data and the envelope key that was encrypted with the
recipient's public key.


this blogpost has a very detailed description of what's going on inside openssl_seal(). It also has an implementation in java.

From this, I would think it should be relatively straightforward ("the proof left as an exercise to the reader" kind of straightforward) to do an equivalent implementation in python using pyopenssl, which includes RC4, or the newer, but for these purposes more focused tlslite.


What openssl_seal does is:

  1. Extract the public_key from the certificate
  2. Generate a 128 bits (16 bytes) long random_key (this will be used to encrypt the message using a symmetrical algorithm, since it's faster)
  3. Encrypt the random_key using PKCS #1
  4. Encrypt the message using ARC4 a secure cipher method and the random_key (Note that ARC4 is no longer considered secure and that PHP strongly recommends to explicitly specify a secure cipher method using the cipher_algo param)
  5. Output the encrypted_random_key and the encrypted_message

The receiving party can then decrypt the encrypted_random_key using their private_key and then decrypt the encrypted_message using the random_key.

Since there's no way of doing this in Python via the standard library, I'm just gonna' throw out the 3 approaches that I've tried out:

# pyca/cryptography (cryptography.io) version
# pip install cryptography

import os

import cryptography
from cryptography import x509


message = 'Super secret secret message'
message = message.encode('utf-8')
certificate_data = open('/path/to/certificate.cer', 'r').read()
certificate_data = certificate_data.encode('utf-8')
certificate = cryptography.x509.load_pem_x509_certificate(data=certificate_data, backend=cryptography.hazmat.backends.default_backend())
public_key = certificate.public_key()
random_key = os.urandom(16)
encrypted_random_key = public_key.encrypt(plaintext=random_key, padding=cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15())
print(encrypted_random_key)
algorithm = cryptography.hazmat.primitives.ciphers.algorithms.AES(random_key)
cipher = cryptography.hazmat.primitives.ciphers.Cipher(algorithm=algorithm, mode=None, backend=cryptography.hazmat.backends.default_backend())
encryptor = cipher.encryptor()
encrypted_message = encryptor.update(message)
print(encrypted_message)

.

# M2Crypto version
# pip install pip install git+https://gitlab.com/m2crypto/m2crypto@python3

import M2Crypto


message = 'Super secret secret message'
message = message.encode('utf-8')
certificate = M2Crypto.X509.load_cert('/path/to/certificate.cer')
public_key = certificate.get_pubkey()
rsa_pub = public_key.get_rsa()
random_key = M2Crypto.Rand.rand_bytes(16)
encrypted_random_key = rsa_pub.public_encrypt(random_key, M2Crypto.RSA.pkcs1_padding)
print(encrypted_random_key)
cipher = M2Crypto.EVP.Cipher(alg='aes_128_cbc', key=random_key, iv=b'', op=M2Crypto.encrypt)
encrypted_message = cipher.update(message)
encrypted_message += cipher.final()
print(encrypted_message)

.

# PyCrypto version
# Update: PyCrypto 2.x is unmaintained, obsolete, and contains security vulnerabilities!!!
# pip install pycrypto

# Please bear in mind that PyCrypto cannot handle x509 certificates.
# You will have to extract the public_key to a pem file:
# openssl x509 -inform pem -in certificate.cer -pubkey -noout > public_key.pem

from Crypto import Random
from Crypto.Cipher import ARC4
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA


message = 'Super secret secret message'
message = message.encode('utf-8')
public_key_data = open('/path/to/public_key.pem', 'r').read()
public_key = RSA.importKey(public_key_data)
random_key = Random.new().read(16)
cipher = PKCS1_v1_5.new(public_key)
encrypted_random_key = cipher.encrypt(random_key)
print(encrypted_random_key)
cipher = ARC4.new(random_key)
encrypted_message = cipher.encrypt(message)
print(encrypted_message)

You can check out my post at => http://helpfulsheep.com/2017-09-01-openssl-seal-in-python/


Since I can't post comments yet, I need to add to Gabi Nagy's answer, that while their answer describes a correct algorithm, it is not the same as using openssl_seal() function.

OpenSSL doesn't let unencrypted key to get outside of OpenSSL structures. It generates key somewhere inside and keeps it there, giving you only encrypted key. The crucial difference is that when OpenSSL cleans it's structures, it should dispose of unencrypted key in a safe way.

0

精彩评论

暂无评论...
验证码 换一张
取 消