There are some cool hacks out there that will help you extract X.509 certificate metadata to JSON values. Since jc
converts so many other things to JSON, I figured it would make sense to add this functionality. I wanted to make sure jc
could handle both binary and text-encoded certificates of most any type, well-known and user-defined extensions, and also ensure the output was convenient for use in scripts.
At first, I considered parsing -text
output from openssl
. It would not have been too hard to do – except for finding a way to reliably parse unknown certificate extensions. Ultimately I wanted to not only support openssl
output, but even native certificate file formats so you could pipe the certificate file directly to jc
like: cat certificate.crt | jc --x509-cert
.
If I had gone the original route I would have needed two parsers: one openssl
parser and another X.509 certificate parser – maybe even multiple parsers for different certificate formats.
I started building an X.509 certificate file parser that supports DER and PEM-encoded certificates first. Serendipitously, I found that this method provides all of the desired functionality in a single parser! This method suports:
- Most any binary certificate format (DER, PKCS #7, PKCS #12, etc.)
- PEM-encoded certificates
openssl
command output (and any other command that can output DER and PEM)- Allows conversion of password-protected certificate files to JSON
- Allows conversion of most any certificate format to JSON
- Well-known and user-defined X.509 certificate extensions
- Certificate files with multiple certificates bundled
- Convenience fields (e.g. dates in timestamp format as well as ISO format)
Converting DER Certificate Files to JSON
The most basic (but not necessarily the most popular) X.509 certificate file is simply a binary DER format certificate. Certificate file extensions are arbitrary, so there is no guarantee you have simple DER-encoded certificate by looking at the extension. But typical file extensions for DER-encoded certificates can be .der
, .cer
, .crt
, etc.
jc
can natively convert DER-encoded certificate files to JSON:
$ cat certificate.crt | jc --x509-cert
Converting PEM Certificate Files to JSON
PEM encoded certificate files are pretty much just base64-encoded DER certificates and will many times have a .pem
file extension. But, again, certificate file extensions are arbitrary, so a valid PEM file could also have a .crt
, .cer
, or any other extension.
PEM files can also contain more than one certificate. For instance, there might be a certificate chain with the web server certificate and one or more intermediate certificates encoded in the file. Also, a PEM file can include other objects like private keys. Don’t worry – jc
can handle multiple certificates and will ignore anything but certificates in the JSON output.
jc
can natively convert PEM-encoded certificates to JSON:
$ cat certificate.pem | jc --x509-cert
Converting PKCS #7 Certificate Files to JSON
PKCS #7 certificates will typically have a .p7b
or .p7c
file extension and can be either binary DER-encoded or text PEM-encoded.
jc
will not natively convert PKCS #7 certificate files to JSON, but don’t worry! You can easily convert the PKCS #7 file to vanilla X.509 DER or PEM with openssl
so jc
can convert it to JSON:
$ openssl pkcs7 \ -in certificate.p7b \ -inform der \ -print_certs | jc --x509-cert
Note that the -inform
argument is not needed if the PKCS #7 file is PEM encoded.
Converting PKCS #12 Certificate Files to JSON
PKCS #12 files are a password-protected binary format that can contain certificates, private keys, and other objects. You will typically see a .pfx
or .p12
extension on these files.
jc
will not natively convert binary PKCS #12 certificate files to JSON, but don’t worry! You can easily convert the PKCS #12 file to vanilla X.509 DER or PEM with openssl
so jc
can convert it to JSON:
$ openssl pkcs12 \ -info \ -in certificate.pfx \ -passin pass:abc123 \ -passout pass: | jc --x509-cert
Note that you need to specify the certificate file password in the -passin
parameter. You can set any password to the -passout
parameter so you won’t be prompted for one when the command is run. In this example we set it to blank.
Converting Certificate Signing Request (CSR) Files to JSON
As of version v1.23.3 jc
now also supports converting CSR files to JSON. Both PEM and DER format are supported using the new x509-csr
parser:
$ cat mycsr.pem | jc --x509-csr
Using in a Script
Let’s put all of the pieces together and show how you can use JSON output in a script.
No matter the certificate type, the JSON output will be consistent. The schema can be found in the jc
documentation for the X.509 certificate parser. Here is an example of a Certificate Authority certificate converted from a PKCS #12 file:
[ { "tbs_certificate": { "version": "v3", "serial_number": "e1:3f:bc:97:7c:10:1d:b8", "signature": { "algorithm": "sha1_rsa", "parameters": null }, "issuer": { "country_name": "FR", "state_or_province_name": "Alsace", "locality_name": "Strasbourg", "organization_name": "www.freelan.org", "organizational_unit_name": "freelan", "common_name": "Freelan Sample Certificate Authority", "email_address": "contact@freelan.org" }, "validity": { "not_before": 1335521864, "not_after": 1338113864, "not_before_iso": "2012-04-27T10:17:44+00:00", "not_after_iso": "2012-05-27T10:17:44+00:00" }, "subject": { "country_name": "FR", "state_or_province_name": "Alsace", "locality_name": "Strasbourg", "organization_name": "www.freelan.org", "organizational_unit_name": "freelan", "common_name": "Freelan Sample Certificate Authority", "email_address": "contact@freelan.org" }, "subject_public_key_info": { "algorithm": { "algorithm": "rsa", "parameters": null }, "public_key": { "modulus": "e0:e9:fb:ca:10:70:af:8c:4e:e5:8f:65:5c:49:65:1e:f9:a5:a2:b8:cd:c5:27:82:ea:58:5d:64:86:58:55:cf:4d:5e:ef:b2:c1:64:ea:f2:27:78:f0:2b:4c:bf:93:...", "public_exponent": 65537 } }, "issuer_unique_id": null, "subject_unique_id": null, "extensions": [ { "extn_id": "key_identifier", "critical": false, "extn_value": "23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53" }, { "extn_id": "authority_key_identifier", "critical": false, "extn_value": { "key_identifier": "23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53", "authority_cert_issuer": null, "authority_cert_serial_number": null } }, { "extn_id": "basic_constraints", "critical": false, "extn_value": { "ca": true, "path_len_constraint": null } } ] }, "signature_algorithm": { "algorithm": "sha1_rsa", "parameters": null }, "signature_value": "b0:44:9a:49:0a:0a:7b:4b:e9:3d:05:3e:97:de:40:5e:7e:89:c4:10:e6:2d:c9:65:c1:3e:9b:b2:1b:74:25:9b:5a:dd:85:ce:ba:0c:21:85:a2:b0:e6:4f:18:cc:98:..." } ]
Note:
jc
does not verify the integrity of the certificate, which requires calculating the hash of the certificate body and comparing it to the the hash in the certificate’s signature after it (the hash) is decrypted with the issuer certificate’s public key.
Notice the first (and only) certificate in this JSON array has a tbs_certificate.validity
object that contains not_before
and not_after
values in both epoch timestamp and ISO formats. This should make it easy for us to check whether the certificate is valid in a Bash script using a JSON parser like jq
:
#!/bin/bash # grab the validity information from the first certificate in the pkcs12 file cert_json=$( openssl pkcs12 \ -info \ -in certificate.pfx \ -passin pass:abc123 \ -passout pass: | jc --x509-cert ) not_before=$( echo "$cert_json" | jq .[0].tbs_certificate.validity.not_before ) not_after=$( echo "$cert_json" | jq .[0].tbs_certificate.validity.not_after ) # compare the timestamps to the current time current_time=$(date '+%s') if [[ "$not_before" -lt "$current_time" ]] && [[ "$not_after" -gt "$current_time" ]]; then echo "Certificate is valid" else echo "Certificate is invalid" fi
And here is the output for an expired certificate. (note the STDERR
and STDOUT
lines have been distinguished):
$ ./checkcert.sh MAC Iteration 2048 # STDERR MAC verified OK # STDERR PKCS7 Encrypted data: pbeWithSHA1And40BitRC2-CBC, Iteration 2048 # STDERR Certificate bag # STDERR PKCS7 Data # STDERR Shrouded Keybag: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048 # STDERR Certificate is invalid # STDOUT
There are a lot more things to check than just the not_before
and not_after
fields for a true certificate validation, so this should be considered a toy example to get you started. I hope this new jc
X.509 certificate parser helps you in your automation scripts!