(get_cert_chain.rb)
#!/usr/bin/env ruby require 'socket' require 'openssl' #ARGV[0]:FQDN ssl_client = OpenSSL::SSL::SSLSocket.new(TCPSocket.open(ARGV[0],443)) ssl_client.connect ssl_client.peer_cert_chain.each do |c| puts c.to_text end
実行してみる。
$ ./get_cert_chain.rb www.google.com Certificate: Data: Version: 3 (0x2) Serial Number: 05:5e:b0:df:e0:dc:e3:a5:05:00:00:00:00:87:7f:a3 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=Google Trust Services, CN=GTS CA 1O1 Validity Not Before: Feb 23 15:43:15 2021 GMT Not After : May 18 15:43:14 2021 GMT Subject: C=US, ST=California, L=Mountain View, O=Google LLC, CN=www.google.com Subject Public Key Info: ... Certificate: Data: Version: 3 (0x2) Serial Number: 01:e3:b4:9a:a1:8d:8a:a9:81:25:69:50:b8 Signature Algorithm: sha256WithRSAEncryption Issuer: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign Validity Not Before: Jun 15 00:00:42 2017 GMT Not After : Dec 15 00:00:42 2021 GMT Subject: C=US, O=Google Trust Services, CN=GTS CA 1O1 Subject Public Key Info: ...
instance method OpenSSL::SSL::SSLSocket#peer_cert_chain
https://docs.ruby-lang.org/ja/latest/method/OpenSSL=3a=3aSSL=3a=3aSSLSocket/i/peer_cert_chain.html
class OpenSSL::X509::Certificate
https://docs.ruby-lang.org/ja/latest/class/OpenSSL=3a=3aX509=3a=3aCertificate.html
(get_cert_chain.py)
#!/usr/bin/env python3 import sys import OpenSSL import socket #sys.argv[1]:FQDN context = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_2_METHOD) conn = OpenSSL.SSL.Connection(context,socket.socket(socket.AF_INET,socket.SOCK_STREAM)) conn.connect((sys.argv[1],443)) conn.do_handshake() for cert in conn.get_peer_cert_chain(): print(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,cert))
実行してみる。
$ ./get_cert_chain.py www.google.com b'-----BEGIN CERTIFICATE-----\nMIIFkzCCBHugAwIBAgIQY8hcdGWM+v0DAAAAAMudizANBgkqhkiG9w0BAQsFADBC\nMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZpY2VzMRMw\nEQYDVQQDEwpHVFMgQ0EgMU8xMB4XDTIxMDIyMzE1MzAzNFoXDTIxMDUxODE1MzAz\nM1owaDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcT\nDU1vdW50YWluIFZpZXcxEzARBgNVBAoTCkdvb2dsZSBMTEMxFzAVBgNVBAMTDnd3\ndy5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsPTx\nwWJ27WX3EsXKJThR29GL+OHAt3KFiEkkOsL1NmMVZ+36BYhfSUB7sEwF0dYtp9Af\nxEMZ0ogCPUKqb2Set2bph37BY90li/k3ncev2L1ABV7ew0t7qpagULo/YU0mdq64\nFiiwZnyuznkJ9cxHFB39E79VmLifIdc/9+e+2tHesOEW/ROolGbNYqzd1uZ2RgNz\nTmyaMs9BiKB7oM3gV/tCo+E5dzht380a1NyS9v/rz2k/7Xx2/iZlYnA3YeGiKzCv\n+V14YNRAb8dZvA6Uyh6DJu/hLKG0BbbPY3oWw/Y8Mpr1ZF1uKsoRsqaVbcbpEzUB\nbwZFh0vglfhMC2s5iwIDAQABo4ICXTCCAlkwDgYDVR0PAQH/BAQDAgWgMBMGA1Ud\nJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFCVZcW7pLGEz\nCOpThDpv6TeKUApnMB8GA1UdIwQYMBaAFJjR+G4Q68+b7GCfGJAboOt9Cf0rMGgG\nCCsGAQUFBwEBBFwwWjArBggrBgEFBQcwAYYfaHR0cDovL29jc3AucGtpLmdvb2cv\nZ3RzMW8xY29yZTArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nL2dzcjIvR1RT\nMU8xLmNydDAZBgNVHREEEjAQgg53d3cuZ29vZ2xlLmNvbTAhBgNVHSAEGjAYMAgG\nBmeBDAECAjAMBgorBgEEAdZ5AgUDMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9j\ncmwucGtpLmdvb2cvR1RTMU8xY29yZS5jcmwwggEFBgorBgEEAdZ5AgQCBIH2BIHz\nAPEAdgD2XJQv0XcwIhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXfPumPSAAAE\nAwBHMEUCIFeHa+sa/BN+qvWRr/LyAgxp9HbPNpjZTjX70iOGoQDiAiEA5o2EZj0S\nGwrDoT2v750IPdRG5NosNLFvtnQJVu7Jqq8AdwDuwJXujXJkD5Ljw7kbxxKjaWoJ\ne0tqGhQ45keyy+3F+QAAAXfPumP6AAAEAwBIMEYCIQCu8QnYK9bBGZOQrcBBq/PV\noDdjbE18FO9GPiMnmjEmIQIhAJrRAosmhv6VqWzJege86E/b2/SPEP1SWI74gw+O\nMizLMA0GCSqGSIb3DQEBCwUAA4IBAQBav+WI0p3KaYVqJSl4qhoqXUkrnX/P5FjR\n0stmcUVAA5uH4lwCjvHkxkYfCKfVrEmZ36SeN7EUNivCamkCyM94Ln+nUcgqk4pq\nOm93QFcx11ZWZCGIEh9+ekzxTIfJdvz/ridcsp0DqVl0nNp09HARe5phszskqEn2\nDAUUwOdt0xJ8XIYpP//HglXwsTBvIMtUO0s3WFPbyuVUnoffsK/aNAZpNXRv2SsY\nVe6KHbbO14CRCdC1Ie2gKlIj7aUqSO2+DC0an1ekJFNhSaos4qbeTEpxrqn/Aqa3\nZE07rNtR52ow1uxeX/OPDEDKs6jdxDwJy6cnidqtZXramaX6kOzR\n-----END CERTIFICATE-----\n' b'-----BEGIN CERTIFICATE-----\nMIIESjCCAzKgAwIBAgINAeO0mqGNiqmBJWlQuDANBgkqhkiG9w0BAQsFADBMMSAw\nHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs\nU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy\nMTUwMDAwNDJaMEIxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg\nU2VydmljZXMxEzARBgNVBAMTCkdUUyBDQSAxTzEwggEiMA0GCSqGSIb3DQEBAQUA\nA4IBDwAwggEKAoIBAQDQGM9F1IvN05zkQO9+tN1pIRvJzzyOTHW5DzEZhD2ePCnv\nUA0Qk28FgICfKqC9EksC4T2fWBYk/jCfC3R3VZMdS/dN4ZKCEPZRrAzDsiKUDzRr\nmBBJ5wudgzndIMYcLe/RGGFl5yODIKgjEv/SJH/UL+dEaltN11BmsK+eQmMF++Ac\nxGNhr59qM/9il71I2dN8FGfcddwuaej4bXhp0LcQBbjxMcI7JP0aM3T4I+DsaxmK\nFsbjzaTNC9uzpFlgOIg7rR25xoynUxv8vNmkq7zdPGHXkxWY7oG9j+JkRyBABk7X\nrJfoucBZEqFJJSPk7XA0LKW0Y3z5oz2D0c1tJKwHAgMBAAGjggEzMIIBLzAOBgNV\nHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1Ud\nEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFJjR+G4Q68+b7GCfGJAboOt9Cf0rMB8G\nA1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMDUGCCsGAQUFBwEBBCkwJzAl\nBggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdvb2cvZ3NyMjAyBgNVHR8EKzAp\nMCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dzcjIvZ3NyMi5jcmwwPwYDVR0g\nBDgwNjA0BgZngQwBAgIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly9wa2kuZ29vZy9y\nZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAGoA+Nnn78y6pRjd9XlQWNa7H\nTgiZ/r3RNGkmUmYHPQq6Scti9PEajvwRT2iWTHQr02fesqOqBY2ETUwgZQ+lltoN\nFvhsO9tvBCOIazpswWC9aJ9xju4tWDQH8NVU6YZZ/XteDSGU9YzJqPjY8q3MDxrz\nmqepBCf5o8mw/wJ4a2G6xzUr6Fb6T8McDO22PLRL6u3M4Tzs3A2M1j6bykJYi8wW\nIRdAvKLWZu/axBVbzYmqmwkm5zLSDW5nIAJbELCQCZwMH56t2Dvqofxs6BBcCFIZ\nUSpxu6x6td0V7SvJCCosirSmIatj/9dSSVDQibet8q/7UK4v4ZUN80atnZz1yg==\n-----END CERTIFICATE-----\n'
SSL — An interface to the SSL-specific parts of OpenSSL
https://www.pyopenssl.org/en/stable/api/ssl.html
crypto — Generic cryptographic module
https://www.pyopenssl.org/en/stable/api/crypto.html