read certificate(.crt) and key(.key) file in python - python

So i'm using the JIRA-Python module to connect to my company's instance on JIRA and it requires me to pass the certificate and key for this.
However using the OpenSSL module,i'm unable to read my local certificate and key to pass it along the request.
the code for reading is below
import OpenSSL.crypto
c = open('/Users/mpadakan/.certs/mpadakan-blr-mpsot-20160704.crt').read()
cert = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, c)
the error i get is
Traceback (most recent call last):
File "flaskApp.py", line 19, in <module>
cert = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, c)
TypeError: must be X509, not str
could someone tell me how to read my local .crt and .key file into x509 objects?

#can-ibanoglu was right on:
import OpenSSL.crypto
cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM,
open('/tmp/server.crt').read()
)
>>> cert
<OpenSSL.crypto.X509 object at 0x7f79906a6f50>

Which format in your .crt file.
Are there:
text starting with -----BEGIN CERTIFICATE-----
base64 text started with MI chars
binary data starting with \x30 byte?
In first two case there are PEM format, but in second one you are missing staring line, just add it to get correct PEM file or convert file to binary with base64 and get third case.
In third case you have DER format, so to load it you should use OpenSSL.crypto.FILETYPE_ASN1

Related

How to generate self-signed cert using subjectAltName with dirName using OpenSSL?

I am attempting to generate a self-signed cert with a SubjectAltName of type DirName. Other types of SubjectAltName like DNS work just fine, but DirName will not work. The code to reproduce fairly simple (python 3.8.5)
import string
from OpenSSL import crypto
def _create_csr():
key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, 2048)
csr = crypto.X509Req()
csr.set_pubkey(key)
works = "DNS:abc.xyz"
fails = "dirName:MyGeneratedCert"
csr.add_extensions([crypto.X509Extension(b"subjectAltName", False, fails.encode("ascii"))])
csr.sign(key, "sha256")
if __name__=="__main__":
_create_csr()
The exception I am receiving is as the following
Traceback (most recent call last):
File "tests/createcert.py", line 16, in <module>
_create_csr()
File "tests/createcert.py", line 12, in _create_csr
csr.add_extensions([crypto.X509Extension(b"subjectAltName", False, fails.encode("ascii"))])
File "/usr/lib/python3/dist-packages/OpenSSL/crypto.py", line 779, in __init__
_raise_current_error()
File "/usr/lib/python3/dist-packages/OpenSSL/_util.py", line 54, in exception_from_error_queue
raise exception_type(errors)
OpenSSL.crypto.Error: [('X509 V3 routines', 'X509V3_get_section', 'operation not defined'), ('X509 V3 routines', 'do_dirname', 'section not found'), ('X509 V3 routines', 'a2i_GENERAL_NAME', 'dirname error'), ('X509 V3 routines', 'X509V3_EXT_nconf', 'error in extension')]
The call is making it into OpenSSL's do_dirname function (stack trace). I assume that the value is not being passed in in correct way, but I cannot understand how to pass it as desired.
Any help would be appreciated.
You cannot, via python, because dirName references a value in the configuration database, but pyOpenSSL does not provide an interface to create a configuration database.
Background: dirName references a section in the database, which could be a config file. Reference the x509v3_config tool, for example (https://github.com/openssl/openssl/blob/master/doc/man5/x509v3_config.pod) where you may use a config file:
[extensions]
subjectAltName = dirName:dir_sect
[dir_sect]
C = UK
O = My Org
OU = My Unit
CN = My Name
Note how dirName simply refers to a different section in the configuration database.
But pyopenssl does not have a provision for creating such a database, so your dirName reference won't be found -- hence your error.
Note this is a known limitation. The Python code itself includes mention of lack of configuration database: See comment in code: https://github.com/pyca/pyopenssl/blob/master/src/OpenSSL/crypto.py, line approx 754:
# We have no configuration database - but perhaps we should (some
# extensions may require it).

How can a GRIB file be opened with pygrib without first downloading the file?

The documentation for pygrib shows a function called fromstring which creates a gribmessage instance from a python bytes object representing a binary grib message. I might be misunderstanding the purpose of this function, but it leads me to believe I can use it in place of downloading a GRIB file and using the open function on it. Unfortunately, my attempts to open a multi-message GRIB file from NLDAS2 have failed. Does anyone else know how to use pygrib on GRIB data without first saving the file? My code below shows how I would like it to work. Instead, it gives the error TypeError: expected bytes, int found on the line for grib in gribs:
from urllib import request
import pygrib
url = "<remote address of desired file>"
username = "<username>"
password = "<password>"
redirectHandler = request.HTTPRedirectHandler()
cookieProcessor = request.HTTPCookieProcessor()
passwordManager = request.HTTPPasswordMgrWithDefaultRealm()
passwordManager.add_password(None, "https://urls.earthdata.nasa.gov", username, password)
authHandler = request.HTTPBasicAuthHandler(passwordManager)
opener = request.build_opener(redirectHandler, cookieProcessor, authHandler)
request.install_opener(opener)
with request.urlopen(url) as response:
data = response.read()
gribs = pygrib.fromstring(data)
for grib in gribs:
print(grib)
Edit to add the entire error output:
Traceback (most recent call last):
File ".\example.py", line 19, in <module>
for grb in grbs:
File "pygrib.pyx", line 1194, in pygrib.gribmessage.__getitem__
TypeError: expected bytes, int found
Edit: This interface does not support multi-message GRIB files, but the authors are open to a pull request if anyone wants to write up the code. Unfortunately, my research focus has shifted and I don't have time to contribute myself.
As stated by jasonharper you can use pygrib.fromstring(). I just tried it myself and this works.
Here is the link to the documentation.
Starting with pygrib v2.1.4, the changelog says that pygrib.open() now accepts io.BufferedReader object as an input argument.
see pygrib changelog here
That would theoretically allow you to read a GRIB2 file from RAM memory without writing it to disk.
I think the usage is supposed to be the following :
binary_io = io.BytesIO(bytes_data)
buffer_io = io.BufferedReader(binary_io)
grib_file = pygrib.open(buffer_io)
But I was not able to make it work on my side !

Parsing DNS RDATA using python

I'm trying to use Python to parse hex-formated DNS RDATA-values (should be RFC1035-compliant) that's generated in audit logs from Windows DNS Server when a record is created or deleted. I've tried a couple of Python dns-modules and think I'm getting close with dnslib, however all the documentation I find is for parsing a complete DNS-packet captured from the network including question and answer header ++.
The audit log only provides the class type and the RDATA it stores in AD (Active Directory-integrated zone), so I figured I might be able to use the parse(buffer,length) method of the individual record type-classes to parse it, but so far all my attempts are failing.
Sample data:
Type = MX
RDATA = 0A0011737276312E636F6E746F736F2E636F6D2E
Which should be parsed to:
preference = 10
mx = srv1.contoso.com.
Latest attempt:
import dnslib
import binascii
mxrdata = binascii.unhexlify(b'0A0011737276312E636F6E746F736F2E636F6D2E')
b = dnslib.DNSBuffer(mxrdata)
mx = dnslib.MX.parse(b,len(b))
this fails with:
Traceback (most recent call last):
File "C:\Python37-32\lib\site-packages\dnslib\dns.py", line 1250, in parse
mx = buffer.decode_name()
File "C:\Python37-32\lib\site-packages\dnslib\label.py", line 235, in decode_name
(length,) = self.unpack("!B")
File "C:\Python37-32\lib\site-packages\dnslib\buffer.py", line 103, in unpack
data = self.get(struct.calcsize(fmt))
File "C:\Python37-32\lib\site-packages\dnslib\buffer.py", line 64, in get
(self.offset,self.remaining(),length))
dnslib.buffer.BufferError: Not enough bytes [offset=20,remaining=0,requested=1]
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python37-32\lib\site-packages\dnslib\dns.py", line 1254, in parse
(buffer.offset,e))
dnslib.dns.DNSError: Error unpacking MX [offset=20]: Not enough bytes [offset=20,remaining=0,requested=1]
Can anyone help me? Is it even possible using this module?
You are encoding the RDATA wrongly a bit:
First, you specify the preference:
0A00
However, this is not 10 (because integer are encoded MSB first, not LSB first), but 2560. So this should be
000A
Then, you try to encode the hostname here:
11737276312E636F6E746F736F2E636F6D2E
0x11 should be the length byte and the rest is the domain name srv1.contoso.com.. But this is not how encoding a hostname works. You have to encode each label separately with a length byte and terminate the hostname with a 0-length label. So this should be:
04 73727631 07 636F6E746F736F 03 636F6D 00
s r v 1 . c o n t o s o . c o m .
This adds up to:
mxrdata = binascii.unhexlify(b'000A047372763107636F6E746F736F03636F6D00')
Them the parser should succeed. So if you really get the RDATA in such an invalid format, you have to convert it first in to make it rfc1035-compliant.

PyCryptodome Error: MAC Check Failed

I am working on an encryption program with Pycryptodome in Python 3. I am trying to encrypt a (byte) string and then decrypt it and verify the MAC tag. When I get to verify it, an error is thrown.
This is the code:
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
aes_key = get_random_bytes(24)
aes_cipher = AES.new(aes_key, AES.MODE_GCM)
encrypted, MACtag = aes_cipher.encrypt_and_digest(b"A random thirty two byte string.")
# Imagine this is happening somewhere else
new_aes_cipher = AES.new(aes_key, AES.MODE_GCM, nonce=aes_cipher.nonce)
new_aes_cipher.verify(MACtag)
decrypted = new_aes_cipher.decrypt(encrypted)
And this is the error:
Traceback (most recent call last):
File "aespractice.py", line 10, in <module>
new_aes_cipher.verify(tag)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-
packages/Crypto/Cipher/_mode_gcm.py", line 441, in verify
raise ValueError("MAC check failed")
ValueError: MAC check failed
I've looked at the documentation, and I it looks to me like everything is all right. Why do you think the program is acting this way? Any help would be appreciated.
If you look at the state diagram for authenticated modes:
You see that verify() should be called at the very end, after any decrypt() has taken place.
So, either you invert the calls or you replace them with a combined decrypt_and_verify().

convert the key in MIME encoded form in python

I need to convert the key in MIME encoded form which is presently comes in (ascii armored) radix 64 format. For that, I have to get this radix64 format in its binary form and also need to remove its header and checksum than coversion in MIME format, but I didnt find any method which can do this conversion.
f = urllib.urlopen('http://pool.sks-keyservers.net:11371/pks/lookup?op=get&search= 0x58e9390daf8c5bf3') #Retrieve the public key from PKS
data = f.read()
decoded_bytes = base64.b64decode(data)
print decoded_bytes
I used the base64.b64decode method and it gives me the following error:
Traceback (most recent call last):
File "RetEnc.py", line 12, in ?
decoded_bytes = base64.b64decode(data)
File "/usr/lib/python2.4/base64.py", line 76, in b64decode
raise TypeError(msg)
TypeError: Incorrect padding
Why am I get this TypeError: Incorrect padding error, and how cn I fix it?
For a start, when you do use a valid search value ("jay" returns an error stating "too many values"), you will receive an HTML page from which you need to extract the actual key. Trying a search value of "jaysh" I get the following response:
>>> print urllib.urlopen('http://pool.sks-keyservers.net:11371/pks/lookup?op=get&search=jaysh').read()
<html><head><title>Public Key Server -- Get ``jaysh ''</title></head>
<body><h1>Public Key Server -- Get ``jaysh ''</h1>
<pre>
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.1.1
mQGiBEpz7VIRBADAt9YpYfYHJeGA6d+G261FHW1uA0YXltCWa7TL6JnIsuxvh9vImUoyMJd6
1xEW4TuROTxGcMMiDemQq6HfV9tLi7ptVBLf/8nUEFoGhxS+DPJsy46WmlscKHRIEdIkTYhp
uAIMim0q5HWymEqqAfBLwJTOY9sR+nelh0NKepcCqwCgvenJ2R5UgmAh+sOhIBrh3OahZEED
/2sRGHi4xRWKePFpttXfb2hry2/jURPae/wYfuI6Xw3k5EO593veGS7Zyjnt+7mVY1N5V/ey
rfXaS3R6GsByG/eRVzRJGU2DSQvmF+q2NC6v2s4KSzr5CVKpn586SGUSg/aKvXY3EIrpvAGP
rHum1wt6P9m9kr/4X8SdVhj7Jti6A/0TA8C2KYhOn/hSYAMTmhisHan3g2Cm6yNzKeTiq6/0
ooG/ffcY81zC6+Kw236VGy2bLrMLkboXPuecvaRfz14gJA9SGyInIGQcd78BrX8KZDUpF1Ek
KxQqL97YRMQevYV89uQADKT1rDBJPNZ+o9f59WT04tClphk/quvMMuSVILQaamF5c2ggPGph
eXNocmVlQGdtYWlsLmNvbT6IZgQTEQIAJgUCSnPtUgIbAwUJAAFRgAYLCQgHAwIEFQIIAwQW
AgMBAh4BAheAAAoJEFjpOQ2vjFvzS0wAn3vf1A8npIY/DMIFFw0/eGf0FNekAKCBJnub9GVu
9OUY0nISQf7uZZVyI7kBDQRKc+1SEAQAm7Pink6S5+kfHeUoJVldb+VAlHdf7BdvKjVeiKAb
dFUa6vR9az+wn8V5asNy/npEAYnHG2nVFpR8DTlN0eO35p78qXkuWkkpNocLIB3bFwkOCbff
P3yaCZp27Vq+9182bAR2Ah10T1KShjWTS/wfRpSVECYUGUMSh4bJTnbDA2MAAwUEAIcRhF9N
OxAsOezkiZBm+tG4BgT0+uWchY7fItJdEqrdrROuCFqWkJLY2uTbhtZ5RMceFAW3s+IYDHLL
PwM1O+ZojhvAkGwLyC4F+6RCE62mscvDJQsdwS4L25CaG2Aw97HhY7+bG00TWqGLb9JibKie
X1Lk+W8Sde/4UK3Q8tpbiE8EGBECAA8FAkpz7VICGwwFCQABUYAACgkQWOk5Da+MW/MAAgCg
tfUKLOsrFjmyFu7biv7ZwVfejaMAn1QXEJw6hpvte60WZrL0CpS60A6Q
=tvYU
-----END PGP PUBLIC KEY BLOCK-----
</pre>
</body></html>
So you need to look only at the key which is wrapped by the <pre> HTML tags.
By the way, there are other issues that you will need to contend with such as multiple keys being returned because you are searching by "name", when you should be searching by keyID. For example, keyID 0x58E9390DAF8C5BF3 will return the public key for jaysh and only jaysh and the corresponsing URL is http://pool.sks-keyservers.net:11371/pks/lookup?op=get&search=0x58E9390DAF8C5BF3.
This was mostly covered in my earlier answer to this question which I presume you also asked.

Categories