Edited at

Chromeで旧シマンテック/ジオトラスト社のSSL証明書が信頼されたものでなくなる日に向けて...

More than 1 year has passed since last update.

昨年から、なんだかゴタゴタしていたやつ。

長めの期間で証明書を取得していると、再発行の該当になっているパターンがある。


信頼されなくなっちゃうパターン

ref: https://security.googleblog.com/2017/09/chromes-plan-to-distrust-symantec.html


対象1:2016年5月31日以前に発行されたSSLサーバ証明書(ラピッドSSL、クイックSSLプレミアム)

Chrome66からSSLサーバ証明書が無効化される(2018年4月17日に正式版リリース予定)


  • 有効期間が2018年3月14日以前の場合は、対応不要

  • 有効期間が2018年3月15日以降の場合は、2017年12月1日以降に再発行が必要


対象2:2016年6月1日2017年11月30日までに発行されたSSLサーバ証明書(ラピッドSSL、クイックSSLプレミアム、セキュア・サーバID、セキュア・サーバID EV、グローバル・サーバID)

Chrome70からSSLサーバ証明書が無効化される(2018年10月23日に正式版リリース予定)


  • 有効期間が2018年9月12日以前の場合は、対応不要

  • 有効期間が2018年9月13日以降の場合は、2017年12月1日以降に再発行が必要


簡易的なチェカースクリプト作った

管理対象の証明書がそれなりにあると、手作業チェックは漏れもありそうなので、確認の意味もこめて、チェックスクリプト(簡易版)を作ってみた。

Gist:kacchan822/check_cert_chrome.py 記事末尾にも載っけてある

以下の具合で、対象1または2で対応が必要だと、それぞれCRITICAL-1CRITICAL-2と教えてくれる。対象1または2だけど、対応不要な場合も、一応WARNINGになる。

$ python3 check_cert_chrome.py www.example.com

[ OK ] www.example.com: Cert was published at 2017-12-15 20:03:18. (Let's Encrypt, Let's Encrypt Authority X3)

$ python3 check_cert_chrome.py www.example.com
[ WARNING ] www.example.com: Cert was published at 2016-03-16 00:00:00; before 2016-05-31. (GeoTrust Inc., RapidSSL SHA256 CA)

$ python3 check_cert_chrome.py www.example.com
[ WARNING ] www.example.com: Cert was published at 2017-06-10 00:00:00; between 2016-06-01 and 2017-11-30. (Symantec Corporation, Symantec Class 3 Secure Server CA - G4)

$ python3 check_cert_chrome.py www.example.com
[ CRITICAL-1 ] www.example.com: Cert was published at 2016-03-16 00:00:00; before 2016-05-31. (GeoTrust Inc., RapidSSL SHA256 CA)

$ python3 check_cert_chrome.py www.example.com
[ CRITICAL-2 ] www.example.com: Cert was published at 2017-04-01 00:00:00; between 2016-06-01 and 2017-11-30. (Symantec Corporation, Symantec Class 3 Secure Server CA - G4)

すべての証明書を正しく判定できるわけではないので、あしからず…。


Gist:kacchan822/check_cert_chrome.py


check_cert_chrome.py

#!/usr/bin/env python3

# This software is released under the MIT License.
# http://opensource.org/licenses/mit-license.php

import datetime
import re
import socket
import ssl
import sys

timeout = 5

def get_cert(cn, port=443):
"""
return: dict
{'OCSP': ('http://ocsp.int-x3.letsencrypt.org',),
'caIssuers': ('http://cert.int-x3.letsencrypt.org/',),
'issuer': ((('countryName', 'US'),),
(('organizationName', "Let's Encrypt"),),
(('commonName', "Let's Encrypt Authority X3"),)),
'notAfter': 'Mar 15 20:46:19 2018 GMT',
'notBefore': 'Dec 15 20:46:19 2017 GMT',
'serialNumber': '0467F6AD9DE0A1D776DC5688B22569094A49',
'subject': ((('commonName', 'ip.ksn.cloud'),),),
'subjectAltName': (('DNS', 'ip.ksn.cloud'),
('DNS', 'ip4.ksn.cloud'),
('DNS', 'ip6.ksn.cloud')),
'version': 3}
"""

context = ssl.create_default_context()
try:
with context.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM),
server_hostname=cn) as conn:
conn.settimeout(timeout)
conn.connect((cn, port))
return conn.getpeercert()
except ssl.CertificateError as e:
print('Errorr:', e)
sys.exit(1)

def _conv_dt(datetime_string):
cert_date_format = '%b %d %H:%M:%S %Y %Z'
return datetime.datetime.strptime(datetime_string, cert_date_format)

def _get_cert_datetime(cert):
return _conv_dt(cert['notBefore']), _conv_dt(cert['notAfter'])

def check_issuer(cert):
if cert.get('issuer'):
issuer = {key_val[0][0]: key_val[0][1] for key_val in cert.get('issuer')}
else:
issuer = {}
issuer['checkFlag'] = True
if re.match(r'(Symantec|GeoTrust)', issuer.get('organizationName', '')):
issuer['checkFlag'] = False
if re.match(r'RapidSSL', issuer.get('commonName', '')):
issuer['checkFlag'] = False
return issuer

def check_cert_datetime(cert):
start, end = _get_cert_datetime(cert)
if start < datetime.datetime(2016, 6, 1, 0, 0, 0):
flag = 1
msg = 'Cert was published at {}; before 2016-05-31.'
elif (datetime.datetime(2016, 5, 31, 23, 59, 59) < start and
start < datetime.datetime(2017, 12, 1, 0, 0, 0)):
flag = 2
msg = 'Cert was published at {}; between 2016-06-01 and 2017-11-30.'
else:
flag = 0
msg = 'Cert was published at {}.'
return flag, msg.format(str(start))

def main(cn):
try:
cn, port = cn.strip().split(':')
except ValueError:
cn = cn.strip()
port = 443

cert = get_cert(cn, int(port))
issuer = check_issuer(cert)
cert_datetime = check_cert_datetime(cert)
cert_start, cert_end = _get_cert_datetime(cert)

if not issuer['checkFlag']:
if (cert_datetime[0] == 1 and
cert_end > datetime.datetime(2016, 6, 1, 0, 0, 0)):
state_msg = 'CRITICAL-1'
elif (cert_datetime[0] == 2 and
cert_end > datetime.datetime(2018, 9, 12, 23, 59, 59)):
state_msg = 'CRITICAL-2'
else:
state_msg = 'WARNING'
else:
state_msg = 'OK'

message = '[{: ^12}] {}: {} ({}, {})'.format(
state_msg, cn, cert_datetime[1], issuer.get('organizationName', '-'),
issuer.get('commonName', '-')
)
return message

if __name__ == '__main__':
print(main(sys.argv[1]))



参考