Reputation: 87
I've got a PEM certificate and I'm using openssl
to view its content. Is it possible to parse the output into a JSON format? Maybe there's a Java library or Bash script that can do this?
command: $ openssl x509 -in sample.cer -noout -text
output:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
af:69:46:11:10:bd:82:88
Signature Algorithm: sha1WithRSAEncryption
Issuer: C=US, ST=Texas, L=Plano, O=2xoffice, OU=Architecture, CN=Joshua Davies/[email protected]
Validity
Not Before: May 21 21:49:10 2014 GMT
Not After : Jun 20 21:49:10 2014 GMT
Subject: C=US, ST=Texas, L=Plano, O=2xoffice, OU=Architecture, CN=Joshua Davies/[email protected]
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (512 bit)
Modulus (512 bit):
00:b7:38:0d:e0:ab:37:18:a7:26:95:9d:9e:6f:a2:
69:b1:b9:ee:b3:7f:29:04:fb:f0:94:b3:d0:d5:55:
c0:d8:6b:14:7f:94:13:3c:d9:a2:61:bf:ba:3f:0a:
44:37:dc:18:b5:23:c7:ee:96:2d:7c:d8:92:04:48:
74:f8:c6:46:a5
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
1A:A5:C9:C8:36:EA:7D:FA:B4:DF:A4:9C:11:F9:C1:BE:78:C4:42:DD
X509v3 Authority Key Identifier:
keyid:1A:A5:C9:C8:36:EA:7D:FA:B4:DF:A4:9C:11:F9:C1:BE:78:C4:42:DD
DirName:/C=US/ST=Texas/L=Plano/O=2xoffice/OU=Architecture/CN=Joshua Davies/[email protected]
serial:AF:69:46:11:10:BD:82:88
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha1WithRSAEncryption
56:32:44:76:86:8c:08:92:74:71:0e:ac:a6:7d:ba:1d:7c:d3:
b6:74:ef:27:7a:5e:53:21:fc:8e:eb:26:58:e0:6e:4f:5c:01:
f1:40:ca:0a:e9:d2:0e:00:60:ae:1f:f6:a5:a4:4c:47:fb:e0:
68:7f:25:63:ab:60:38:0f:74:94
Upvotes: 5
Views: 16222
Reputation: 11
Since i faced a simular issue so i wrote a simple (fits my needs) bash script. This script takes from stdin the x509 formated certificate, extracts the required fields and creates a simple json output.
#!/bin/bash
IFS=''
CERT=$(timeout 3s cat)
# TODO: INPUT Validierung:
# Input überhaupt vorhanden?
# Input valides x509 Format?
if [ -z "$CERT" ]; then
echo "NO STDIN Input -> EXIT"
exit 12
fi
getCertSubject() {
echo $CERT | awk 'BEGIN{FS="Subject: "} NF==2{print $2}'
}
getCertSignatureAlgorithm() {
echo $CERT | awk 'BEGIN{FS="Signature Algorithm: "} NF==2{print $2}'|head -n 1
}
getCertIssuer() {
echo $CERT | awk 'BEGIN{FS="Issuer: "} NF==2{print $2}'
}
getCertNotBefore() {
echo $CERT | awk 'BEGIN{FS="Not Before: "} NF==2{print $2}'
}
getCertNotAfter() {
echo $CERT | awk 'BEGIN{FS="Not After : "} NF==2{print $2}'
}
getCertIssuerURL() {
echo $CERT | awk 'BEGIN{FS="CA Issuers - URI:"} NF==2{print $2}'
}
getCertDNS() {
echo $CERT | sed -n '/Subject Alternative Name:/{n;p;}' | xargs | sed "s/DNS://g" | sed "s/,//g"
}
getCertSerialNumber() {
echo $CERT | sed -n '/Serial Number:/{n;p;}' | xargs
}
getCertSubjectKeyIdentifier() {
echo $CERT | sed -n '/Subject Key Identifier:/{n;p;}' | xargs
}
getCertAuthorityKeyIdentifier() {
echo $CERT | sed -n '/Authority Key Identifier:/{n;p;}' | xargs
}
getCommonName(){
echo $1 | awk 'BEGIN{FS="(^| )CN( )*="} NF==2{print $2}' | awk -F, '{print $1}'| xargs
}
getOrganisation(){
echo $1 | awk 'BEGIN{FS="(^| )O( )*="} NF==2{print $2}' | awk -F, '{print $1}'| xargs
}
getCountry(){
echo $1 | awk 'BEGIN{FS="(^| )C( )*="} NF==2{print $2}' | awk -F, '{print $1}'| xargs
}
getDNSArray(){
echo $1 | sed 's/ /\", \"/g;s/^/\"/;s/$/\"/'
}
SUBJECT=$(getCertSubject)
ISSUER=$(getCertIssuer)
read -r -d '' JSON << EOM
{
"label": "$(getCommonName $SUBJECT)",
"node": "$(hostname)",
"date": "$(date)",
"subject": {
"raw": "$SUBJECT",
"common_name": "$(getCommonName $SUBJECT)",
"country": "$(getCountry $SUBJECT)",
"organization": "$(getOrganisation $SUBJECT)",
"names": [
$(getDNSArray $(getCertDNS))
]
},
"issuer": {
"raw": "$ISSUER",
"common_name": "$(getCommonName $ISSUER)",
"country": "$(getCountry $ISSUER)",
"organization": "$(getOrganisation $ISSUER)",
"url": "$(getCertIssuerURL)"
},
"serial_number": "$(getCertSerialNumber)",
"sans": [
$(getDNSArray $(getCertDNS))
],
"not_before": "$(getCertNotBefore)",
"not_after": "$(getCertNotAfter)",
"sigalg": "$(getCertSignatureAlgorithm)",
"authority_key_id": "$(getCertAuthorityKeyIdentifier)",
"subject_key_id": "$(getCertSubjectKeyIdentifier)"
}
EOM
echo "$JSON"
You can find the script in the following github repo: https://github.com/jsiegele/x509tojson
Maybe also this Blogpost helps: https://prefetch.net/blog/2019/12/10/converting-x509-certificates-to-json-objects/
(describes the usage of Cloudflare’s certinfo
)
Upvotes: 1
Reputation: 596
Here is my own python script (derived from script above)
It can take pems with multiple certs as well.
#!/usr/bin/env python3
import os, sys
import tempfile
import json
import pem
import ssl
def main():
cert_file_name = sys.argv[1]
try:
pems = pem.parse_file(cert_file_name)
except Exception as e:
print(f"Error decoding pem: {e}\n")
sys.exit(1)
res = []
for p in pems:
f = tempfile.NamedTemporaryFile(mode='w', delete=False)
f.write(p.as_text())
f.close()
try:
cert = ssl._ssl._test_decode_cert(f.name)
# remap array mess in subject and issuer into dicts
for item in ['subject', 'issuer']:
if item in cert:
item_dict = {}
for s in cert[item]:
for i in s:
if i[0] not in item_dict:
item_dict[i[0]] = []
item_dict[i[0]].append(i[1])
# collapse single item arrays into scalars
for k,v in item_dict.items():
if len(v) == 1:
item_dict[k] = v[0]
cert[item] = item_dict
# remap array mess in subjectAltName into a dict
if 'subjectAltName' in cert:
item = 'subjectAltName'
san_dict = {}
for i in cert[item]:
if i[0] not in san_dict:
san_dict[i[0]] = []
san_dict[i[0]].append(i[1])
cert[item] = san_dict
res.append(cert)
except Exception as e:
print(f"Error decoding certificate: {e}\n")
os.unlink(f.name)
print(json.dumps(res))
if __name__ == "__main__":
main()
Upvotes: 0
Reputation: 46
I was looking for exactly this and run a lot of PHP scripts on the server.
The answer from @slm may work well but here is the built in PHP function that can do what you want.
https://secure.php.net/manual/en/function.openssl-x509-parse.php
Upvotes: 1
Reputation: 16406
I was able to construct a Python script that does what you're looking for. This script takes a single argument, <PEM FILE>
and returns a JSON object with this certificates contents.
$ ./pem2json.py <PEM FILE>
NOTE: The script can optionally take a second argument, -d
which will print debugging info out if you want to see more of the transformation.
You can download sample TLS certificates from this website - X509 certificate examples for testing and verification. Specifically I'm going to use this PEM file:
After downloading it I pass it in as an argument to the Python script:
$ ./pem2json.py 2048b-dsa-example-cert.pem
{"notBefore": "Aug 22 07:27:22 2012 GMT", "serialNumber": "0E02", "notAfter": "Aug 21 07:27:22 2017 GMT", "version": 1, "subject": [[["countryName", "JP"]], [["stateOrProvinceName", "Tokyo"]], [["organizationName", "Frank4DD"]], [["commonName", "www.example.com"]]], "issuer": [[["countryName", "JP"]], [["stateOrProvinceName", "Tokyo"]], [["localityName", "Chuo-ku"]], [["organizationName", "Frank4DD"]], [["organizationalUnitName", "WebCert Support"]], [["commonName", "Frank4DD Web CA"]], [["emailAddress", "[email protected]"]]]}
$ cat pem2json.py
#!/usr/bin/python
import json
import os
import ssl
import sys
from collections import OrderedDict
from pprint import pprint as pp
def main():
debug = False
if len(sys.argv) == 3:
if sys.argv[2] == "-d":
debug = True
if debug:
print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
print("cli arg1: {:s}\n".format(sys.argv[1]))
cert_file_name = os.path.join(os.path.dirname(__file__), sys.argv[1])
try:
ordered_dict = OrderedDict()
ordered_dict = ssl._ssl._test_decode_cert(cert_file_name)
if debug: pp(ordered_dict)
except Exception as e:
print("Error decoding certificate: {:s}\n".format(e))
print(json.dumps(ordered_dict))
if __name__ == "__main__":
main()
$ ./pem2json.py 2048b-dsa-example-cert.pem -d
Python 2.7.5 (default, Jul 13 2018, 13:06:57)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
cli arg1: 2048b-dsa-example-cert.pem
{'issuer': ((('countryName', u'JP'),),
(('stateOrProvinceName', u'Tokyo'),),
(('localityName', u'Chuo-ku'),),
(('organizationName', u'Frank4DD'),),
(('organizationalUnitName', u'WebCert Support'),),
(('commonName', u'Frank4DD Web CA'),),
(('emailAddress', u'[email protected]'),)),
'notAfter': 'Aug 21 07:27:22 2017 GMT',
'notBefore': u'Aug 22 07:27:22 2012 GMT',
'serialNumber': u'0E02',
'subject': ((('countryName', u'JP'),),
(('stateOrProvinceName', u'Tokyo'),),
(('organizationName', u'Frank4DD'),),
(('commonName', u'www.example.com'),)),
'version': 1L}
{"notBefore": "Aug 22 07:27:22 2012 GMT", "serialNumber": "0E02", "notAfter": "Aug 21 07:27:22 2017 GMT", "version": 1, "subject": [[["countryName", "JP"]], [["stateOrProvinceName", "Tokyo"]], [["organizationName", "Frank4DD"]], [["commonName", "www.example.com"]]], "issuer": [[["countryName", "JP"]], [["stateOrProvinceName", "Tokyo"]], [["localityName", "Chuo-ku"]], [["organizationName", "Frank4DD"]], [["organizationalUnitName", "WebCert Support"]], [["commonName", "Frank4DD Web CA"]], [["emailAddress", "[email protected]"]]]}
Upvotes: 2