Obtendo informações de certificados em sites com OpenSSL

Imagine que você precise verificar a data de expiração de diversos certificados instalados em sites na sua rede. Alguém te pede para lista as datas de expiração de 1243 sites mantidos pela sua empresa (tá bom… exagerei… que sejam 12 então!).

Você pode usar o seu browser e clicar no “cadeadinho” usando o método Windows Maniac: Point and click. Ou, pode fazer um script que retorne essa informação para você, colocando-o em um loop (Linux Maniac Mode: modo automatizado!). Eis um script:

#!/bin/bash

# O primeiro parâmetro tem que ser uma URL.
[ -z "$1" ] && ( 
  echo "Uso: `basename "$0"` <site>"; 
  exit 1; 
)

# Obtem os dados do certificado vindos do site.
DT="`openssl s_client -connect ${1}:443 < /dev/null 2>&1 | \
  openssl x509 -noout -text | \
  sed -n 's/^[ \t]\+Not After : \(.\+ GMT\).*$/\1/p'`"

# Mostra a data/hora UTC e BRT.
echo "\"$1\" expires at $DT ($(date --date="$DT"))"

Quando você usa o primeiro comando openssl, obtem algo assim:

$ openssl s_client -connect www.facebook.com:443 < /dev/null
CONNECTED(00000003)
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance CA-3
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/C=US/ST=CA/L=Menlo Park/O=Facebook, Inc./CN=*.facebook.com
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
 1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
   i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFWzCCBEOgAwIBAgIQB3FDDi2yH7Cylc25WcmEVDANBgkqhkiG9w0BAQUFADBm
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBDQS0zMB4XDTE0MDMwMTAwMDAwMFoXDTE1MDQxMzEyMDAwMFowYTELMAkGA1UE
BhMCVVMxCzAJBgNVBAgTAkNBMRMwEQYDVQQHEwpNZW5sbyBQYXJrMRcwFQYDVQQK
Ew5GYWNlYm9vaywgSW5jLjEXMBUGA1UEAwwOKi5mYWNlYm9vay5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNV46wilELaZ37u2f1EuoNEBgFOl3K
qL1MrgA+DYegp4ifz3OcOYyqKUoi67UHDOqGMgSuvKyKO3Ib6Omg7GBqr8tyr8ob
0iKFQv05gMXBM1+9PoJ8YbIfVVwxGg5S5HfvC20x8qNdTfUqv8mp5l7GX1KTffbQ
cjIjsSDDq3TV8Ea5Yxel/GmtZ5qwg46l883D09O9YpYFXOT29tea1y+hsgV1eyJm
ckacOq19mEhKvluQOdhaeDGg4yE7g/oLOqd2kDb2Jy+h0kqOzitXl9pGqSN01m4r
F1A8RlEDoaUr+nBrhL7VAK9RNoBAFa/xysa0NMIjLxX9bNBqEvKMN2PhAgMBAAGj
ggIIMIICBDAfBgNVHSMEGDAWgBRQ6nOJ2yn7EI+e5QEg1N55mUiD9zAdBgNVHQ4E
FgQUpF8sOQJ4rjhX0UJdO0POT6/wl3IwYQYDVR0RBFowWIIOKi5mYWNlYm9vay5j
b22CDGZhY2Vib29rLmNvbYILKi5mYnNieC5jb22CCyouZmJjZG4ubmV0gg4qLnh4
LmZiY2RuLm5ldIIOKi54eS5mYmNkbi5uZXQwDgYDVR0PAQH/BAQDAgWgMB0GA1Ud
JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBhBgNVHR8EWjBYMCqgKKAmhiRodHRw
Oi8vY3JsMy5kaWdpY2VydC5jb20vY2EzLWcyNy5jcmwwKqAooCaGJGh0dHA6Ly9j
cmw0LmRpZ2ljZXJ0LmNvbS9jYTMtZzI3LmNybDBCBgNVHSAEOzA5MDcGCWCGSAGG
/WwBATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BT
MHsGCCsGAQUFBwEBBG8wbTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl
cnQuY29tMEUGCCsGAQUFBzAChjlodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v
RGlnaUNlcnRIaWdoQXNzdXJhbmNlQ0EtMy5jcnQwDAYDVR0TAQH/BAIwADANBgkq
hkiG9w0BAQUFAAOCAQEAe3Wz3fT4H8PTQMx6tJigaANz9mFI2q81gBhNnwJsvROs
myhEI0/Wsc6QmqdyzULSIYvgOaOFaZmJZYEqRV7viXApkN/M4gROv06O34LVdJfE
iyrtDEpSgQ+Bf5DNJ1iGCStU+n/ZjhVTW/5qgDqj8jyOeg6ohibYc8XlYc8V6A9m
QdOKDBcLRT5X0bDb272nbM4gcSIxsye5OYdSW6GetCJGpihu2KYWRhjNtmqU/IPB
qpeOCoqKk9GQEiMkENONIsDAOsxOsmPK4lqMN92gFBO4QxN4iIZbGTIB5LqulTES
aNlDDAsSMYv/zUFLQhGuWRw+E5fk4ysDb0rySieNsg==
-----END CERTIFICATE-----
subject=/C=US/ST=CA/L=Menlo Park/O=Facebook, Inc./CN=*.facebook.com
issuer=/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance CA-3
---
No client certificate CA names sent
---
SSL handshake has read 3676 bytes and written 446 bytes
---
...

E um monte de outras linhas de informação sobre a sessão SSL.

Notou que o certificado que queremos checar está dentro de um bloco de texto cercado por “—–BEGIN CERTIFICATE—–” e “—–END CERTIFICATE—–“? Este é o bloco de texto que o openssl procurará na sua segunda chamada (via pipe). É como se tivéssemos salvado o certificado num arquivo temporário e chamássemos:

$ openssl x509 -in tmp$$.txt -inform PEM -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            07:71:43:0e:2d:b2:1f:b0:b2:95:cd:b9:59:c9:84:54
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3
        Validity
            Not Before: Mar  1 00:00:00 2014 GMT
            Not After : Apr 13 12:00:00 2015 GMT
        Subject: C=US, ST=CA, L=Menlo Park, O=Facebook, Inc., CN=*.facebook.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:cd:57:8e:b0:8a:51:0b:69:9d:fb:bb:67:f5:12:
                    ea:0d:10:18:05:3a:5d:ca:a8:bd:4c:ae:00:3e:0d:
                    87:a0:a7:88:9f:cf:73:9c:39:8c:aa:29:4a:22:eb:
                    b5:07:0c:ea:86:32:04:ae:bc:ac:8a:3b:72:1b:e8:
                    ...

Tudo o que temos que fazer é obter a linha contendo a string “Not After :” e separar a data do resto do texto. Faço isso com o sed.

Essa data e hora de expiração é relativa ao Universal Time Cordinator, anteriormente conhecido como Greenwich Mean Time (GMT) e não corresponde ao horário do Brasil. Assim, usei date para converter para BRT (BRazillian Time).

Usando o script:

Basta passar a URI do site para o script. Por exemplo:

$ ./check-cert meusite.com.br
"meusite.com.br" expires at Jul 21 23:59:59 2017 GMT (Sex Jul 21 20:59:59 BRT 2017)
Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s