将PEM证书解析为JSON

5

我有一个PEM证书,并使用openssl查看其内容。是否可以将输出解析为JSON格式?也许有一个Java库或Bash脚本可以实现这一点?

命令:$ openssl x509 -in sample.cer -noout -text

输出:

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/emailAddress=joshua.davies.tx@gmail.com
        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/emailAddress=joshua.davies.tx@gmail.com
        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/emailAddress=joshua.davies.tx@gmail.com
                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

为了什么目的?你的目标是什么?在Bash中没有办法做到这一点,你必须使用不同的工具。 - DTSCode
4个回答

2
我能够编写一个Python脚本来完成你所需的功能。该脚本接受一个参数<PEM文件>,并返回一个包含证书内容的JSON对象。请看下面的代码:

$ ./pem2json.py <PEM FILE>

注意: 脚本可以选择性地接受第二个参数-d,如果您想查看更多的转换信息,它将输出调试信息。

示例

您可以从此网站下载示例TLS证书 - 用于测试和验证的X509证书示例。具体来说,我将使用此PEM文件:

下载后,将其作为参数传递给Python脚本:

$ ./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", "support@frank4dd.com"]]]}

代码

$ 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'support@frank4dd.com'),)),
 '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", "support@frank4dd.com"]]]}

参考资料


1

1

由于我遇到了类似的问题,所以我编写了一个简单(适合我的需求)的bash脚本。该脚本从stdin中获取x509格式的证书,提取所需字段并创建一个简单的json输出。

#!/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"

您可以在以下 Github 存储库中找到该脚本:https://github.com/jsiegele/x509tojson 也许这篇博客文章也会有所帮助:https://prefetch.net/blog/2019/12/10/converting-x509-certificates-to-json-objects/(描述了使用 Cloudflare 的 certinfo 的用法)。

0

这是我的Python脚本(基于上面的脚本)

它可以处理包含多个证书的PEM文件。

#!/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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接