I'm trying to write a python function that will take a PEM formatted private key file and convert it into the raw binary representation, similar to what the following openssl function does:
openssl ec -in ec_private.pem -noout -text
read EC key
Private-Key: (256 bit)
priv:
7a:f6:73:2f:58:1d:00:5a:fc:f2:16:f6:38:5f:f6:
37:10:29:24:2c:c6:08:40:dd:7d:2a:7a:55:03:b7:
d2:1c
pub:
04:11:5b:3f:a3:9f:ae:41:b4:e3:2f:77:21:ca:72:
f8:c1:78:14:83:64:7d:ab:d5:14:f0:8e:66:12:8b:
d4:7f:ce:90:67:b9:0e:04:88:c9:c2:a9:f3:0f:5a:
26:6a:07:84:1d:6c:07:74:13:ba:07:e7:45:69:b9:
9d:4f:d3:ce:c6
ASN1 OID: prime256v1
NIST CURVE: P-256
But with the priv key formatted as a more code friendly format, like so
0x7A, 0xF6...
I've been trying to use the pyOpenSSL library, cryptography library, and PyNaCl libraries without any success.
Is there a way to do this, or do I have to write a wrapper around the command line function that does this formatting for me?
Thanks!
I ended up just doing the wrapper around the openssl system library:
import os
def convert_key(keyfile):
stream = os.popen('openssl ec -in {} -noout -text'.format(keyfile))
output = stream.read().strip()
keylines = output.splitlines()
keystring = keylines[2].strip() + keylines[3].strip() + keylines[4].strip()
formatted_key = "0x" + keystring.replace(":", ", 0x")
return formatted_key
Which is a likely very brittle way to create the desired outcome, but I can't find a more pythonic way to do it.
Related
I am trying to convert .key formatted private key into .pem formatted key using python3. Before using python3 I was able to convert private key into .pem format by simply running openssl command as:
openssl rsa -in <private_key_file> -outform PEM -out <new_file>
I would like to know how people are using python3 to convert it private keys into .pem or other formats. So far I've tried running subprocess.run() command to execute openssl command from python. It would be nice to know if there is python way of converting into .pem format.
I also tried following using pycryptodrome library https://pycryptodome.readthedocs.io/en/latest/index.html as per this thread pyOpenSSL creating a pem file which creates in pem format but wasn't successful.
def convert_key_to_pem_format(key_file):
print("Converting to pem/rsa format")
pv_key_string = key_file.exportKey()
with open ("private.pem", "w") as prv_file:
print("{}".format(pv_key_string.decode()), file=prv_file)
This is my code. Any help would be appreciated.
From the Cryptodome library, you can use the import_key method, which supports the following formats for an RSA private key:
PKCS#1 RSAPrivateKey DER SEQUENCE (binary or PEM encoding)
PKCS#8_ PrivateKeyInfo or EncryptedPrivateKeyInfo DER SEQUENCE (binary or PEM encoding)
Here's how you could use it:
from Cryptodome.PublicKey import RSA
key = RSA.import_key(open('private.key', "rb").read(), passphrase="(passphrase in case you have one)")
pv_key_string = key.exportKey()
with open ("private.pem", "w") as prv_file:
print("{}".format(pv_key_string.decode()), file=prv_file)
There is already a lot of questions out there but the problem is, none of them have sufficient answers how to do it, especially when using python3.
Basically, I want to read JAR/APK certificates, like this one: Link to ASN1 Decoder, with Android Test Signing Key
There are now several alternatives:
pyasn1: seems to work, but only can parse the raw ASN.1 format
M2Crypto: only works on py2
Chilkat: Not free, although CkCert seems to be free
cryptography: Can not load the certificate, as the X509 certificate is inside the PKCS#7 container
I found a way to use pyasn1 to unpack the cert from the pkcs#7 message, then use cryptography to read it:
from pyasn1.codec.der.decoder import decode
from pyasn1.codec.der.encoder import encode
from cryptography import x509
from cryptography.hazmat.backends import default_backend
cdata = open("CERT.RSA", "rb").read()
cert, rest = decode(cdata)
# The cert should be located there
realcert = encode(cert[1][3])
realcert = realcert[2 + (realcert[1] & 0x7F) if realcert[1] & 0x80 > 1 else 2:] # remove the first DER identifier from the front
x509.load_der_x509_certificate(realcert, default_backend())
which gives
<Certificate(subject=<Name([<NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.6, name=countryName)>, value='US')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.8, name=stateOrProvinceName)>, value='California')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.7, name=localityName)>, value='Mountain View')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.10, name=organizationName)>, value='Android')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.11, name=organizationalUnitName)>, value='Android')>, <NameAttribute(oid=<ObjectIdentifier(oid=2.5.4.3, name=commonName)>, value='Android')>, <NameAttribute(oid=<ObjectIdentifier(oid=1.2.840.113549.1.9.1, name=emailAddress)>, value='android#android.com')>])>, ...)>
Is there no other way to have it clean and tidy?
There are now libraries to do this in pure python. One is asn1crypto: https://github.com/wbond/asn1crypto#readme
This is also impemented in androguard, including examples how to use it: https://androguard.readthedocs.io/en/latest/intro/certificates.html
I'm trying to get the serial number of a x509 with M2cryto the problem is that returns me different values to openssl.
from python
>>> from M2Crypto import X509
>>> cer = X509.load_cert(cerPath, X509.FORMAT_DER)
>>> cer.get_serial_number()
286524172099382162235533054529634765881071645240L
from openssl
openssl x509 -inform DER -in cer.cer -serial -noout
serial=3230303031303030303030323030303031343238
It is a matter of the number format, the number with the decimal presentation
286524172099382162235533054529634765881071645240
has this hexadecimal presentation
3230303031303030303030323030303031343238
it's merely a matter of base change.
Thanks. I used it to change number format
>>> '{0:x}'.format(int(cer.get_serial_number()))
3230303031303030303030323030303031343238
I can decrypt some data encrypted via openssl command line tool, but some 'extra' data is returned with the original data.
I've created a encrypted file like this:
$ echo this is it >file.txt
$ openssl rsautl -encrypt -pubin -inkey public.pem -in file.txt -out encrypted.txt
And I can access the original data with:
from Crypto.PublicKey import RSA
key = open('/tmp/.private.key').read()
rsakey = RSA.importKey(key, 'MyPassphrase')
data = open('/tmp/encrypted.txt', 'rb').read()
original = rsakey.decrypt(data)
But some extra data is returned and the output is something like this:
\x02h\x83\xcfx\x84,\xb1\xa6 [...] \xcf5\x9f\xbbG\xf1\x14\xd0\x8d\x1f\xfe\x9c4\xbb\x1aB\xfa\xc3b\xc2\xe0K\x85\xb5\x10y\xe1\x8e\x00this is this\n
Can I avoid to receive this raw data before the decrypted data?
Obs.: The keys were created with openssl tool
Tks.
You are getting PKCS#1 v1.5 padded plaintext back. You need to remove the PKCS#1 v1.5 padding first. Currently you are performing textbook (or "raw") RSA decryption, which is little more than modular exponentiation. Try a PKCS#1 v1.5 capable class instead, like this one
Does anyone know the simplest way to import an OpenSSL RSA private/public key (using a passphrase) with a Python library and use it to decrypt a message.
I've taken a look at ezPyCrypto, but can't seem to get it to recognise an OpenSSL RSA key, I've tried importing a key with importKey as follows:
key.importKey(myKey, passphrase='PASSPHRASE')
myKey in my case is an OpenSSL RSA public/private keypair represented as a string.
This balks with:
unbound method importKey() must be called with key instance as first
argument (got str instance instead)
The API doc says:
importKey(self, keystring, **kwds)
Can somebody suggest how I read a key in using ezPyCrypto? I've also tried:
key(key, passphrase='PASSPHRASE')
but this balks with:
ezPyCrypto.CryptoKeyError: Attempted
to import invalid key, or passphrase
is bad
Link to docs here:
http://www.freenet.org.nz/ezPyCrypto/detail/index.html
EDIT: Just an update on this. Successfully imported an RSA key, but had real problem decrypting because eqPyCrypto doesn't support the AES block cipher. Just so that people know. I successfully managed to do what I wanted using ncrypt (http://tachyon.in/ncrypt/). I had some compilation issues with M2Crypto because of SWIG and OpenSSL compilation problems, despite having versions installed that exceeded the minimum requirements. It would seem that the Python encryption/decryption frameworks are a bit of a minefield at the moment. Ho hum, thanks for your help.
The first error is telling you that importKey needs to be called on an instance of key.
k = key()
k.importKey(myKey, passphrase='PASSPHRASE')
However, the documentation seems to suggest that this is a better way of doing what you want:
k = key(keyobj=myKey, passphrase='PASSPHRASE')
It is not clear what are you trying to achieve, but you could give M2Crypto a try. From my point of view it is the best OpenSSL wrapper available for Python.
Here is a sample RSA encryption/decription code:
import M2Crypto as m2c
import textwrap
key = m2c.RSA.load_key('key.pem', lambda prompt: 'mypassword')
# encrypt something:
data = 'testing 123'
encrypted = key.public_encrypt(data, m2c.RSA.pkcs1_padding)
print "Encrypted data:"
print "\n".join(textwrap.wrap(' '.join(['%02x' % ord(b) for b in encrypted ])))
# and now decrypt it:
decrypted = key.private_decrypt(encrypted, m2c.RSA.pkcs1_padding)
print "Decrypted data:"
print decrypted
print data == decrypted