【问题标题】:parse a PEM certificate into JSON将 PEM 证书解析为 JSON
【发布时间】:2019-02-09 22:47:56
【问题描述】:

我有一个 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 没有办法做到这一点。您将不得不使用不同的实用程序。

标签: java bash openssl ssl-certificate pem


【解决方案1】:

因为我遇到了一个类似的问题,所以我编写了一个简单的(适合我的需要)bash 脚本。此脚本从标准输入获取 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’s certinfo的用法)

【讨论】:

    【解决方案2】:

    这是我自己的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()
    

    【讨论】:

      【解决方案3】:

      我正在寻找这个并在服务器上运行大量 PHP 脚本。

      @slm 的回答可能效果很好,但这里是内置的 PHP 函数,可以做你想做的事。

      https://secure.php.net/manual/en/function.openssl-x509-parse.php

      【讨论】:

        【解决方案4】:

        我能够构建一个 Python 脚本来满足您的需求。此脚本采用单个参数 &lt;PEM FILE&gt; 并返回带有此证书内容的 JSON 对象。

        $ ./pem2json.py <PEM FILE>
        

        注意:脚本可以选择采用第二个参数-d,如果您想查看更多转换,它将打印出调试信息。

        示例

        您可以从该网站下载示例 TLS 证书 - X509 certificate examples for testing and verification。具体来说,我将使用这个 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"]]]}
        

        参考文献

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-10-22
          • 1970-01-01
          • 1970-01-01
          • 2014-04-13
          • 1970-01-01
          • 1970-01-01
          • 2011-09-02
          相关资源
          最近更新 更多