I am currently trying to write a script that allows me to compute the Tor HS address from the hiddens service's private key file.
In order to do this the file needs to be brought into the DER format.
Using OpenSSL this can be done with:
openssl rsa -in private_key -pubout -outform DER
Piping this into python with:
base64.b32encode(hashlib.sha1(sys.stdin.read()[22:]).digest()[:10]).lower()'
will return the address correctly.
However I would like to perform the same using only python. My problem is that using the pycrypto module the DER output is different and the address therefore incorrect.
key = RSA.importKey(keyfile.read()).publickey()
print(key.exportKey(format='DER'))
Will result in a different output than the openssl call.
Is this just a matter of implementation that allows different results? Or am I making a mistake somewhere?
Any help would be appreciated
convert certificate to der using python
first we load the file
cert_file = keyfile.read()
Then we convert it to pem format
from OpenSSL import crypto
cert_pem = crypto.load_certificate(crypto.FILETYPE_PEM, cert_file)
now we are generating the der-output
i.e.: output equals to openssl x509 -outform der -in certificate.pem -out certificate.der.
cert_der = crypto.dump_certificate(crypto.FILETYPE_ASN1, cert_pem)
I was looking for something similar and, as of March 2019, OpenSSL recommends using pyca/cryptography instead of the crypto module. (source)
Here after is then what you intend to do: convert PEM to DER
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
with open("id_rsa", "rb") as keyfile:
# Load the PEM format key
pemkey = serialization.load_pem_private_key(
keyfile.read(),
None,
default_backend()
)
# Serialize it to DER format
derkey = pemkey.private_bytes(
serialization.Encoding.DER,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()
)
# And write the DER format to a file
with open("key.der", "wb") as outfile:
outfile.write(derkey)
I want Convert Certificate file not the key file from DER to PEM, but Google took me here. thanks #alleen1's answer, I can convert certificate or key from DER to PEM and vice versa.
Step one, load the file.
Step two,save it to the format you want.
I ommit the process to get the "pem_data" and "der_data",you can get it from file or anywhere else. they should be bytes not string, use method .encode() when needed.
from cryptography import x509
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
# Step one, load the file.
# Load key file
# PEM
key = serialization.load_pem_private_key(pem_data, None, default_backend())
# DER
key = serialization.load_pem_private_key(der_data, None, default_backend())
# Load cert file
# PEM
cert = x509.load_pem_x509_certificate(pem_data, default_backend())
# DER
cert = x509.load_der_x509_certificate(der_data, default_backend())
# Step two,save it to the format you want.
# PEM key
key_val = key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()
)
# DER key
key_val = key.private_bytes(
serialization.Encoding.DER,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption()
)
# PEM cert
cert_val = cert.public_bytes(serialization.Encoding.PEM)
# DER cert
cert_val = cert.public_bytes(serialization.Encoding.DER)
The inital question is: "Exact the public key from private key", this because the openSSL command states "pubout" in initial question.
Using OpenSSL this can be done with: (note that "pubout" defines OUTPUT as public key only)
openssl ALGORITHM_USED -in private_key -pubout -outform DER
But with Python cryptography module you can exact the public key from private key (note this seems applicable for RSA and EC based cryptography).
With Python:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
# Create private key (example uses elliptic curve encryption)
priv_key = ec.generate_private_key(ec.SECP256K1, default_backend())
pub_key = priv_key.public_key()
pub_key_pem = pub_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open('public_key.pem', 'wb') as outfile:
outfile.write(public_key_pem)
More info on cryptography documentation: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/#cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey
Related
I am trying to find the Python-equivalent of running openssl ec -pubin -in example.pem -inform PEM -outform DER conv_form compressed
Example using the following public key:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk
6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==
-----END PUBLIC KEY-----
and running the following command gives the following output:
echo '-----BEGIN PUBLIC KEY-----\r\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk\r\n6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==\r\n-----END PUBLIC KEY-----\r\n' | openssl ec -pubin -inform PEM -outform DER -conv_form compressed | base64
read EC key
writing EC key
MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0=
now providing a bit more verbosity with the same command plus the -text flag and minus the base64 encoding:
echo '-----BEGIN PUBLIC KEY-----\r\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk\r\n6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==\r\n-----END PUBLIC KEY-----\r\n' | openssl ec -pubin -inform PEM -outform DER -conv_form compressed -text
read EC key
Private-Key: (256 bit)
pub:
02:e2:af:a8:b7:ba:37:3e:e2:6a:b0:1a:27:66:78:
b6:b2:95:4f:be:a2:e4:e8:50:a2:10:5f:46:5f:34:
f2:61:9d
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
090*�H�*�H�="⯨��7>�j�'fx���O����P�_F_4�a�%
So far I am able to do something like:
import base64
import cryptography
csr_crypto = cryptography.x509.load_pem_x509_csr(csr_encoded) # csr_encoded being the CSR in PEM format that the public key is derived from
pub_key = csr_crypto.public_key()
compressed_bytes = pub_key.public_bytes(cryptography.hazmat.primitives.serialization.Encoding.X962, cryptography.hazmat.primitives.serialization.PublicFormat.CompressedPoint).hex()
this leads to compressed_bytes being equal to
02e2afa8b7ba373ee26ab01a276678b6b2954fbea2e4e850a2105f465f34f2619d
which if you look closely is equal to what is in the pub above.
How are these hex bytes ultimately converted to the string MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0= that openssl outputs as a compressed public key?
The first key posted is a public key in X.509/SPKI format (PEM encoded), which contains the actual key in uncompressed format 0x04 + <x> + <y>.
The key derived from this using the OpenSSL statement is also a public key in X.509/SPKI format, but contains the actual key in compressed format 0x02 + <x> or 0x03 + <x> for even or odd y, respectively. This format also contains the complete information, since for a given curve an uncompressed key can be derived from a compressed key.
X.509/SPKI keys can be parsed with an ASN.1 parser (in addition to OpenSSL), e.g. online: https://lapo.it/asn1js.
In Python, an X.509/SPKI key with uncompressed key can be converted to an X.509/SPKI key with compressed key using the PyCryptodome library, see export_key():
from base64 import b64encode
from Crypto.PublicKey import ECC
x509PemWithUncompressed = '''-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk
6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==
-----END PUBLIC KEY-----'''
publicKey = ECC.import_key(x509PemWithUncompressed);
x509withCompressed = publicKey.export_key(format='DER', compress=True)
print(b64encode(x509withCompressed).decode('utf-8')) # MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0=
The Cryptography library on the other hand only allows the export of an X.509/SPKI key with uncompressed key. For this, the format has to be specified with SubjectPublicKeyInfo. An export as X.509/SPKI key with compressed key is not possible. The options UncompressedPoint and CompressedPoint only allow export in the format 0x04 + x + y or 0x02/0x03 + x, but not in the X.509/SPKI format.
But there is a workaround for the desired conversion. In the X.509/SPKI format, the actual key (compressed or uncompressed) is located at the end. The preceding part contains information about the curve, data lengths, etc. In the case of an X.509/SPKI key with compressed key and curve P-256, the preceding part is: 0x3039301306072a8648ce3d020106082a8648ce3d030107032200 (note that the prefix for an X.509 /SPKI key with uncompressed key differs because of the different data lengths) and can be used as a prefix for converting a compressed key in 0x02/0x03 + x format to X.509/SPKI format:
from base64 import b64encode
from cryptography.hazmat.primitives import serialization
x509PemWithUncompressed = b'''-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4q+ot7o3PuJqsBonZni2spVPvqLk
6FCiEF9GXzTyYZ1snzreGB+pyoiUUkz2/H60XWmQsgC7zZ60TBT0rVimtg==
-----END PUBLIC KEY-----'''
publicKey = serialization.load_pem_public_key(x509PemWithUncompressed)
compressedKey = publicKey.public_bytes(serialization.Encoding.X962, serialization.PublicFormat.CompressedPoint)
x509withCompressed = bytes.fromhex('3039301306072a8648ce3d020106082a8648ce3d030107032200') + compressedKey
print(b64encode(x509withCompressed).decode('utf-8')) # MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgAC4q+ot7o3PuJqsBonZni2spVPvqLk6FCiEF9GXzTyYZ0=
Working with an HSM I can download the public key of a RSA 4096, and can use the HSM to encrypt a blob using the corresponding private key, which I can't download.
I'm trying to find a reference to how I can use the HSM apis, to build the X509 certificate myself. I could find the example below in python, wrapping up openssl libs.
It does mostly all, but
from OpenSSL import crypto, SSL
from socket import gethostname
from pprint import pprint
from time import gmtime, mktime
CERT_FILE = "selfsigned.crt"
KEY_FILE = "private.key"
def create_self_signed_cert():
# create a key pair
k = crypto.PKey()
k.generate_key(crypto.TYPE_RSA, 1024)
# create a self-signed cert
cert = crypto.X509()
cert.get_subject().C = "UK"
cert.get_subject().ST = "London"
cert.get_subject().L = "London"
cert.get_subject().O = "Dummy Company Ltd"
cert.get_subject().OU = "Dummy Company Ltd"
cert.get_subject().CN = gethostname()
cert.set_serial_number(1000)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(10*365*24*60*60)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(k)
cert.sign(k, 'sha1')
open(CERT_FILE, "wt").write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
open(KEY_FILE, "wt").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, k))
create_self_signed_cert()
Mostly what I would expect to be possible is to replace the line
cert.sign(k, 'sha1')
by the corresponding calls to the APIs of my HSM with "an export" of the structure of "cert". Is it possible at all ?
So after one day of research I found a satisfactory answer at Adding a signature to a certificate . While there seems to be some Python examples out how to build the certificate through Python with pyasn1, the most robust seems to be the ones in java using boucycastle libraries. I also found a strong answer how to incorporate it with openssl here How to generate certificate if private key is in HSM? (mostly points 3 and 4), but the approach is far beyond my capacities.
When you want someone else, like your HSM, to sign a certificate for you the standard way to do that is to generate a Certificate Signing Request (CSR) and send the CSR to the signer. The CSR can contain all of the fields that you can set when making a certificate, and those values will be reflected in the resulting certificate.
While you can do this with the OpenSSL library that you are using, see X509Req, their documentation (and I quote) "strongly suggests" that you use the cryptography library instead. (They're maintained by the same group of people.)
In the cryptography library you use the CertificateSigningRequestBuilder to make a CSR. To quote from their docs:
>>> from cryptography import x509
>>> from cryptography.hazmat.primitives import hashes
>>> from cryptography.hazmat.primitives.asymmetric import rsa
>>> from cryptography.x509.oid import AttributeOID, NameOID
>>> private_key = rsa.generate_private_key(
... public_exponent=65537,
... key_size=2048,
... )
>>> builder = x509.CertificateSigningRequestBuilder()
>>> builder = builder.subject_name(x509.Name([
... x509.NameAttribute(NameOID.COMMON_NAME, u'cryptography.io'),
... ]))
>>> builder = builder.add_extension(
... x509.BasicConstraints(ca=False, path_length=None), critical=True,
... )
>>> builder = builder.add_attribute(
... AttributeOID.CHALLENGE_PASSWORD, b"changeit"
... )
>>> request = builder.sign(
... private_key, hashes.SHA256()
... )
>>> isinstance(request, x509.CertificateSigningRequest)
True
To render out the CSR in the format usually used for sending them around:
>>> from cryptography.hazmat.primitives import serialization
>>> request.public_bytes(serialization.Encoding.PEM)
I wrote a test demo of signature & verification complete process base on rsa, which helps me to figure out the logic of the process.
# https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
# Preparation phase
# Generate key pairs
# private_key contains the both private key and public key
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
# Serilize the keys
from cryptography.hazmat.primitives import serialization
pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.BestAvailableEncryption(b'mypassword')
)
with open('private-key.pem', 'wb') as f:
f.write(pem)
f.close()
public_key = private_key.public_key()
pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open('public-key.pem', 'wb') as f:
f.write(pem)
f.close()
# Signer
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.asymmetric import utils
with open('private-key.pem', 'rb') as f:
private_key = serialization.load_pem_private_key(
f.read(),
password=b'mypassword',
backend=default_backend()
)
chosen_hash = hashes.SHA256()
hasher = hashes.Hash(chosen_hash, default_backend())
hasher.update(b"data & ")
hasher.update(b"more data")
digest = hasher.finalize()
signature = private_key.sign(
digest,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
utils.Prehashed(chosen_hash)
)
with open('signature', 'wb') as f:
f.write(signature)
f.close()
# Verifier
chosen_hash = hashes.SHA256()
hasher = hashes.Hash(chosen_hash, default_backend())
hasher.update(b"data & ")
hasher.update(b"more data")
digest = hasher.finalize()
hasher1 = hashes.Hash(chosen_hash, default_backend())
hasher1.update(b"data & more data")
digest1 = hasher1.finalize()
print(digest == digest1)
with open('signature', 'rb') as f:
signature = f.read()
with open('public-key.pem', 'rb') as f:
public_key = serialization.load_pem_public_key(
f.read(),
backend=default_backend()
)
if isinstance(public_key, rsa.RSAPublicKey):
public_key.verify(
signature,
digest,
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
utils.Prehashed(chosen_hash)
)
Question:
Does the padding type(eg. PSS) having to be known as input when verification?
But in openssl CLI Generate EC KeyPair from OpenSSL command line
openssl dgst -sha256 -verify public.pem -signature msg.signature.txt msg.digest.txt
Why here didn't mention the padding? I think no matter the key pairs algorithm(ECC or RSA) is different or not, the inputs parameter of (standard?) verification method shall be the same.
Another question, I saw in python having isinstance(public_key, rsa.RSAPublicKey) can find out the algorithm of the keys.
Does the algorithm type also necessary for the verification method?
Like inside the lib may having such ecc_verify rsa_verify methods.
BTW for my understanding, the verify method parameter(same as openssl CLI):
public key
hash type
signature
Does the padding type(eg. PSS) having to be known as input when verification?
Yes, the padding is a required configuration parameter that should be known beforehand.
Why here didn't mention the padding?
ECDSA signature verification doesn't require padding; it is significantly different from RSA signature generation / verification.
There is only one used standard for it, as was the case previously for RSA (PKCS#1 v1.5 padding). PSS was only added later in the RSA PKCS#1 standards when version 2.0 was published.
Does the algorithm type also necessary for the verification method?
Although there is probably not a direct attack, you should assume that the verification algorithm is known in advance, just like with the padding method. RSA as described in PKCS#1 has at least two signature generation functions and there are more in other standards - although those are not common at all.
BTW for my understanding, the verify method parameter(same as openssl CLI):
As you've already seen, RSA PSS uses two hash functions, one for hashing the input data and one for the internal MGF1 function used for padding. So there is not one hash type, but two. The hash types are not necessarily the same and implementations differ on how the MGF1 hash is determined (specifying it explicitly, as you do, is for the best).
Currently I have some code that signs a byte string with the SHA256 algorithm using the native OpenSSL binary, the code calls an external process, sends the parameters, and receive the result back into the Python code.
The current code is as follows:
signed_digest_proc = subprocess.Popen(
['openssl', 'dgst', '-sha256', '-sign', tmp_path],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
)
signed_digest_proc.stdin.write(original_string)
signed_digest, _ = signed_digest_proc.communicate()
base64.encodestring(signed_digest).decode().replace('\n', '')
When original_string is too big, I might have problems with the result (from the communication with an external process I think), that's why I'm trying to change it to a Python only solution:
import hmac, hashlib
h = hmac.new(bytes(key_pem(), 'ASCII'), original_string, hashlib.sha256)
result = base64.encodestring(h).decode().replace('\n', '')
This result in a completely different string than the first one.
What would be the way to implement the original code without calling an external process?
The openssl command you used does three things:
Create a hash of the data, using SHA256
If RSA is used, pad out the message to a specific length, using PKCS#1 1.5
Sign the (padded) hash, using the private key you provided. It'll depend on the type of key what algorithm was used.
The hmac module does not serve the same function.
You'll need to install a cryptography package like cryptography to replicate what openssl dgst -sign does. cryptography uses OpenSSL as a backend, so it will produce the same output.
You can then
load the key with the load_pem_private_key() function. This returns the right type of object for the algorithm used.
use the key to sign the message; each key type has a sign() method, and this method will take care of hashing the message for you if you so wish. See for example the Signing section for RSA.
However, you'll need to provide different kinds of config for the different .sign() methods. Only the RSA, DSA and Elliptic Curve keys can be used to create a signed digest.
You'll have to switch between the types to get the signature right:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa, utils
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
# configuration per key type, each lambda takes a hashing algorithm
_signing_configs = (
(dsa.DSAPrivateKey, lambda h: {
'algorithm': h}),
(ec.EllipticCurvePrivateKey, lambda h: {
'signature_algorithm': ec.ECDSA(h)}),
(rsa.RSAPrivateKey, lambda h: {
'padding': padding.PKCS1v15(),
'algorithm': h
}),
)
def _key_singing_config(key, hashing_algorithm):
try:
factory = next(
config
for type_, config in _signing_configs
if isinstance(key, type_)
)
except StopIteration:
raise ValueError('Unsupported key type {!r}'.format(type(key)))
return factory(hashing_algorithm)
def sign(private_key, data, algorithm=hashes.SHA256()):
with open(private_key, 'rb') as private_key:
key = serialization.load_pem_private_key(
private_key.read(), None, default_backend())
return key.sign(data, **_key_singing_config(key, algorithm))
If you need to hash a large amount of data, you can hash the data yourself first, in chunks, before passing in just the digest and the special util.Prehashed() object:
def sign_streaming(private_key, data_iterable, algorithm=hashes.SHA256()):
with open(private_key, 'rb') as private_key:
key = serialization.load_pem_private_key(
private_key.read(), None, default_backend())
hasher = hashes.Hash(algorithm, default_backend())
for chunk in data_iterable:
hasher.update(chunk)
digest = hasher.finalize()
prehashed = utils.Prehashed(algorithm)
return key.sign(digest, **_key_singing_config(key, prehashed))
with open(large_file, 'rb') as large_file:
signature = sign_streaming(private_key_file, iter(lambda: large_file.read(2 ** 16), b''))
This uses the iter() function to read data from a binary file in chunks of 64 kilobytes.
Demo; I'm using an RSA key I generated in /tmp/test_rsa.pem. Using the command-line to produce a signed digest for Hello world!:
$ echo -n 'Hello world!' | openssl dgst -sign /tmp/test_rsa.pem -sha256 | openssl base64
R1bRhzEr+ODNThyYiHbiUackZpx+TCviYR6qPlmiRGd28wpQJZGnOFg9tta0IwkT
HetvITcdggXeiqUqepzzT9rDkIw6CU7mlnDRcRu2g76TA4Uyq+0UzW8Ati8nYCSx
Wyu09YWaKazOQgIQW3no1e1Z4HKdN2LtZfRTvATk7JB9/nReKlXgRjVdwRdE3zl5
x3XSPlaMwnSsCVEhZ8N7Gf1xJf3huV21RKaXZw5zMypHGBIXG5ngyfX0+aznYEve
x1uBrtZQwUGuS7/RuHw67WDIN36aXAK1sRP5Q5CzgeMicD8d9wr8St1w7WtYLXzY
HwzvHWcVy7kPtfIzR4R0vQ==
or using the Python code:
>>> signature = sign(keyfile, b'Hello world!')
>>> import base64
>>> print(base64.encodebytes(signature).decode())
R1bRhzEr+ODNThyYiHbiUackZpx+TCviYR6qPlmiRGd28wpQJZGnOFg9tta0IwkTHetvITcdggXe
iqUqepzzT9rDkIw6CU7mlnDRcRu2g76TA4Uyq+0UzW8Ati8nYCSxWyu09YWaKazOQgIQW3no1e1Z
4HKdN2LtZfRTvATk7JB9/nReKlXgRjVdwRdE3zl5x3XSPlaMwnSsCVEhZ8N7Gf1xJf3huV21RKaX
Zw5zMypHGBIXG5ngyfX0+aznYEvex1uBrtZQwUGuS7/RuHw67WDIN36aXAK1sRP5Q5CzgeMicD8d
9wr8St1w7WtYLXzYHwzvHWcVy7kPtfIzR4R0vQ==
Although the line lengths differ, the base64 data the two output is clearly the same.
Or, using a generated file with random binary data, size 32kb:
$ dd if=/dev/urandom of=/tmp/random_data.bin bs=16k count=2
2+0 records in
2+0 records out
32768 bytes transferred in 0.002227 secs (14713516 bytes/sec)
$ cat /tmp/random_data.bin | openssl dgst -sign /tmp/test_rsa.pem -sha256 | openssl base64
b9sYFdRzpBtJTan7Pnfod0QRon+YfdaQlyhW0aWabia28oTFYKKiC2ksiJq+IhrF
tIMb0Ti60TtBhbdmR3eF5tfRqOfBNHGAzZxSaRMau6BuPf5AWqCIyh8GvqNKpweF
yyzWNaTBYATTt0RF0fkVioE6Q2LdfrOP1q+6zzRvLv4BHC0oW4qg6F6CMPSQqpBy
dU/3P8drJ8XCWiJV/oLhVehPtFeihatMzcZB3IIIDFP6rN0lY1KpFfdBPlXqZlJw
PJQondRBygk3fh+Sd/pGYzjltv7/4mC6CXTKlDQnYUWV+Rqpn6+ojTElGJZXCnn7
Sn0Oh3FidCxIeO/VIhgiuQ==
Processing the same file in Python:
>>> with open('/tmp/random_data.bin', 'rb') as random_data:
... signature = sign_streaming('/tmp/test_rsa.pem', iter(lambda: random_data.read(2 ** 16), b''))
...
>>> print(base64.encodebytes(signature).decode())
b9sYFdRzpBtJTan7Pnfod0QRon+YfdaQlyhW0aWabia28oTFYKKiC2ksiJq+IhrFtIMb0Ti60TtB
hbdmR3eF5tfRqOfBNHGAzZxSaRMau6BuPf5AWqCIyh8GvqNKpweFyyzWNaTBYATTt0RF0fkVioE6
Q2LdfrOP1q+6zzRvLv4BHC0oW4qg6F6CMPSQqpBydU/3P8drJ8XCWiJV/oLhVehPtFeihatMzcZB
3IIIDFP6rN0lY1KpFfdBPlXqZlJwPJQondRBygk3fh+Sd/pGYzjltv7/4mC6CXTKlDQnYUWV+Rqp
n6+ojTElGJZXCnn7Sn0Oh3FidCxIeO/VIhgiuQ==
I have a RSA public key in PEM format + PKCS#1(I guess):
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAJNrHWRFgWLqgzSmLBq2G89exgi/Jk1NWhbFB9gHc9MLORmP3BOCJS9k
onzT/+Dk1hdZf00JGgZeuJGoXK9PX3CIKQKRQRHpi5e1vmOCrmHN5VMOxGO4d+zn
JDEbNHODZR4HzsSdpQ9SGMSx7raJJedEIbr0IP6DgnWgiA7R1mUdAgMBAAE=
-----END RSA PUBLIC KEY-----
I want to get the SHA1 digest of its ASN1 encoded version in Python. The first step should be to read this key, but I failed to do it in PyCrypto:
>> from Crypto.PublicKey import RSA
>> RSA.importKey(my_key)
ValueError: RSA key format is not supported
The documentation of PyCrypto says PEM + PKCS#1 is supported, so I'm confused.
I've also tried M2Crypto, but it turns out that M2Crypto does not support PKCS#1 but only X.509.
PyCrypto supports PKCS#1 in the sense that it can read in X.509 SubjectPublicKeyInfo objects that contain an RSA public key encoded in PKCS#1.
Instead, the data encoded in your key is a pure RSAPublicKey object (that is, an ASN.1 SEQUENCE with two INTEGERs, modulus and public exponent).
You can still read it in though. Try something like:
from Crypto.PublicKey import RSA
from Crypto.Util import asn1
from base64 import b64decode
key64 = 'MIGJAoGBAJNrHWRFgWLqgzSmLBq2G89exgi/Jk1NWhbFB9gHc9MLORmP3BOCJS9k\
onzT/+Dk1hdZf00JGgZeuJGoXK9PX3CIKQKRQRHpi5e1vmOCrmHN5VMOxGO4d+znJDEbNHOD\
ZR4HzsSdpQ9SGMSx7raJJedEIbr0IP6DgnWgiA7R1mUdAgMBAAE='
keyDER = b64decode(key64)
seq = asn1.DerSequence()
seq.decode(keyDER)
keyPub = RSA.construct( (seq[0], seq[1]) )
Starting from version 2.6, PyCrypto can import also RsaPublicKey ASN.1 objects.
The code is then much simpler:
from Crypto.PublicKey import RSA
from base64 import b64decode
key64 = b'MIGJAoGBAJNrHWRFgWLqgzSmLBq2G89exgi/Jk1NWhbFB9gHc9MLORmP3BOCJS9k\
onzT/+Dk1hdZf00JGgZeuJGoXK9PX3CIKQKRQRHpi5e1vmOCrmHN5VMOxGO4d+znJDEbNHOD\
ZR4HzsSdpQ9SGMSx7raJJedEIbr0IP6DgnWgiA7R1mUdAgMBAAE='
keyDER = b64decode(key64)
keyPub = RSA.importKey(keyDER)