@ -6,7 +6,6 @@ package prober
import (
"bytes"
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
@ -20,8 +19,6 @@ import (
"strings"
"testing"
"time"
"golang.org/x/crypto/ocsp"
)
var leafCert = x509 . Certificate {
@ -118,11 +115,6 @@ func TestCertExpiration(t *testing.T) {
} ,
"one of the certs expires in" ,
} ,
{
"valid duration but no OCSP" ,
func ( ) * x509 . Certificate { return & leafCert } ,
"no OCSP server presented in leaf cert for CN=tlsprobe.test" ,
} ,
} {
t . Run ( tt . name , func ( t * testing . T ) {
cs := & tls . ConnectionState { PeerCertificates : [ ] * x509 . Certificate { tt . cert ( ) } }
@ -134,93 +126,150 @@ func TestCertExpiration(t *testing.T) {
}
}
type ocspServer struct {
issuer * x509 . Certificate
responderCert * x509 . Certificate
template * ocsp . Response
priv crypto . Signer
type CRLServer struct {
crlBytes [ ] byte
}
func ( s * ocsp Server) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
if s . template == nil {
func ( s * CRLServer ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
if s . crlBytes == nil {
w . WriteHeader ( http . StatusInternalServerError )
return
}
resp , err := ocsp . CreateResponse ( s . issuer , s . responderCert , * s . template , s . priv )
if err != nil {
panic ( err )
}
w . Write ( resp )
w . Header ( ) . Set ( "Content-Type" , "application/pkix-crl" )
w . WriteHeader ( http . StatusOK )
w . Write ( s . crlBytes )
}
func TestOCSP ( t * testing . T ) {
issuerKey , err := rsa . GenerateKey ( rand . Reader , 4096 )
func TestCRL ( t * testing . T ) {
// Generate CA key and self-signed CA cert
caKey , err := rsa . GenerateKey ( rand . Reader , 4096 )
if err != nil {
t . Fatal ( err )
}
issuerBytes , err := x509 . CreateCertificate ( rand . Reader , & issuerCertTpl , & issuerCertTpl , & issuerKey . PublicKey , issuerKey )
caTpl := issuerCertTpl
caTpl . BasicConstraintsValid = true
caTpl . IsCA = true
caTpl . KeyUsage = x509 . KeyUsageCertSign | x509 . KeyUsageCRLSign | x509 . KeyUsageDigitalSignature
caBytes , err := x509 . CreateCertificate ( rand . Reader , & caTpl , & caTpl , & caKey . PublicKey , caKey )
if err != nil {
t . Fatal ( err )
}
issuerCert , err := x509 . ParseCertificate ( issuerBytes )
caCert, err := x509 . ParseCertificate ( ca Bytes)
if err != nil {
t . Fatal ( err )
}
responderKey , err := rsa . GenerateKey ( rand . Reader , 4096 )
// Issue a leaf cert signed by the CA
leaf := leafCert
leaf . SerialNumber = big . NewInt ( 20001 )
leaf . Issuer = caCert . Subject
leafKey , err := rsa . GenerateKey ( rand . Reader , 4096 )
if err != nil {
t . Fatal ( err )
}
// issuer cert template re-used here, but with a different key
responderBytes , err := x509 . CreateCertificate ( rand . Reader , & issuerCertTpl , & issuerCertTpl , & responderKey . PublicKey , responderKey )
leafBytes , err := x509 . CreateCertificate ( rand . Reader , & leaf , caCert , & leafKey . PublicKey , caKey )
if err != nil {
t . Fatal ( err )
}
responderCert, err := x509 . ParseCertificate ( responder Bytes)
leafCertParsed, err := x509 . ParseCertificate ( leaf Bytes)
if err != nil {
t . Fatal ( err )
}
handler := & ocspServer {
issuer : issuerCert ,
responderCert : responderCert ,
priv : issuerKey ,
// Catch no CRL set by Let's Encrypt date.
noCRLCert := leafCert
noCRLCert . SerialNumber = big . NewInt ( 20002 )
noCRLCert . CRLDistributionPoints = [ ] string { }
noCRLCert . NotBefore = time . Unix ( letsEncryptStartedStaplingCRL , 0 ) . Add ( - 48 * time . Hour )
noCRLCert . Issuer = caCert . Subject
noCRLCertKey , err := rsa . GenerateKey ( rand . Reader , 4096 )
if err != nil {
t . Fatal ( err )
}
srv := httptest . NewUnstartedServer ( handler )
srv . Start ( )
defer srv . Close ( )
cert := leafCert
cert . OCSPServer = append ( cert . OCSPServer , srv . URL )
key , err := rsa . GenerateKey ( rand . Reader , 4096 )
noCRLStapledBytes , err := x509 . CreateCertificate ( rand . Reader , & noCRLCert , caCert , & noCRLCertKey . PublicKey , caKey )
if err != nil {
t . Fatal ( err )
}
certBytes, err := x509 . CreateCertificate ( rand . Reader , & cert , issuerCert , & key . PublicKey , issuerKey )
noCRLStapledParsed , err := x509 . ParseCertificate ( noCRLStapledBytes )
if err != nil {
t . Fatal ( err )
}
parsed , err := x509 . ParseCertificate ( certBytes )
crlServer := & CRLServer { crlBytes : nil }
srv := httptest . NewServer ( crlServer )
defer srv . Close ( )
// Create a CRL that revokes the leaf cert using x509.CreateRevocationList
now := time . Now ( )
revoked := [ ] x509 . RevocationListEntry { {
SerialNumber : leaf . SerialNumber ,
RevocationTime : now ,
ReasonCode : 1 , // Key compromise
} }
rl := x509 . RevocationList {
SignatureAlgorithm : caCert . SignatureAlgorithm ,
Issuer : caCert . Subject ,
ThisUpdate : now ,
NextUpdate : now . Add ( 24 * time . Hour ) ,
RevokedCertificateEntries : revoked ,
Number : big . NewInt ( 1 ) ,
}
rlBytes , err := x509 . CreateRevocationList ( rand . Reader , & rl , caCert , caKey )
if err != nil {
t . Fatal ( err )
}
emptyRlBytes , err := x509 . CreateRevocationList ( rand . Reader , & x509 . RevocationList { Number : big . NewInt ( 2 ) } , caCert , caKey )
if err != nil {
t . Fatal ( err )
}
for _ , tt := range [ ] struct {
name string
resp * ocsp . Response
wantErr string
name string
cert * x509 . Certificate
crlBytes [ ] byte
wantErr string
} {
{ "good response" , & ocsp . Response { Status : ocsp . Good } , "" } ,
{ "unknown response" , & ocsp . Response { Status : ocsp . Unknown } , "unknown OCSP verification status for CN=tlsprobe.test" } ,
{ "revoked response" , & ocsp . Response { Status : ocsp . Revoked } , "cert for CN=tlsprobe.test has been revoked" } ,
{ "error 500 from ocsp" , nil , "non-200 status code from OCSP" } ,
{
"ValidCert" ,
leafCertParsed ,
emptyRlBytes ,
"" ,
} ,
{
"RevokedCert" ,
leafCertParsed ,
rlBytes ,
"has been revoked on" ,
} ,
{
"EmptyCRL" ,
leafCertParsed ,
emptyRlBytes ,
"" ,
} ,
{
"NoCRL" ,
leafCertParsed ,
nil ,
"" ,
} ,
{
"NotBeforeCRLStaplingDate" ,
noCRLStapledParsed ,
nil ,
"" ,
} ,
} {
t . Run ( tt . name , func ( t * testing . T ) {
handler . template = tt . resp
if handler . template != nil {
handler . template . SerialNumber = big . NewInt ( 1337 )
cs := & tls . ConnectionState { PeerCertificates : [ ] * x509 . Certificate { tt . cert , caCert } }
if tt . crlBytes != nil {
crlServer . crlBytes = tt . crlBytes
tt . cert . CRLDistributionPoints = [ ] string { srv . URL }
} else {
crlServer . crlBytes = nil
tt . cert . CRLDistributionPoints = [ ] string { }
}
cs := & tls . ConnectionState { PeerCertificates : [ ] * x509 . Certificate { parsed , issuerCert } }
err := validateConnState ( context . Background ( ) , cs )
if err == nil && tt . wantErr == "" {