diff --git a/acme/crypto.go b/acme/crypto.go index d595874f..89fcf9d9 100644 --- a/acme/crypto.go +++ b/acme/crypto.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "fmt" "math/big" "time" ) @@ -39,8 +40,37 @@ func pemEncode(data interface{}) []byte { return pem.EncodeToMemory(pemBlock) } -// GetCertExpiration returns the "NotAfter" date of a DER encoded certificate. -func GetCertExpiration(cert []byte) (time.Time, error) { +func pemDecode(data []byte) (*pem.Block, error) { + pemBlock, _ := pem.Decode(data) + if pemBlock == nil { + return nil, fmt.Errorf("Pem decode did not yield a valid block. Is the certificate in the right format?") + } + + return pemBlock, nil +} + +func pemDecodeTox509(pem []byte) (*x509.Certificate, error) { + pemBlock, err := pemDecode(pem) + if pemBlock == nil { + return nil, err + } + + return x509.ParseCertificate(pemBlock.Bytes) +} + +// GetPEMCertExpiration returns the "NotAfter" date of a PEM encoded certificate. +// The certificate has to be PEM encoded. Any other encodings like DER will fail. +func GetPEMCertExpiration(cert []byte) (time.Time, error) { + pemBlock, err := pemDecode(cert) + if pemBlock == nil { + return time.Time{}, err + } + + return getCertExpiration(pemBlock.Bytes) +} + +// getCertExpiration returns the "NotAfter" date of a DER encoded certificate. +func getCertExpiration(cert []byte) (time.Time, error) { pCert, err := x509.ParseCertificate(cert) if err != nil { return time.Time{}, err diff --git a/acme/crypto_test.go b/acme/crypto_test.go index b1e8713f..2e0bb32e 100644 --- a/acme/crypto_test.go +++ b/acme/crypto_test.go @@ -51,7 +51,7 @@ func TestPEMEncode(t *testing.T) { } } -func TestCertExpiration(t *testing.T) { +func TestPEMCertExpiration(t *testing.T) { privKey, err := generatePrivateKey(2048) if err != nil { t.Fatal("Error generating private key:", err) @@ -66,12 +66,20 @@ func TestCertExpiration(t *testing.T) { buf := bytes.NewBufferString("TestingRSAIsSoMuchFun") - if ctime, err := GetCertExpiration(buf.Bytes()); err == nil { + // Some random string should return an error. + if ctime, err := GetPEMCertExpiration(buf.Bytes()); err == nil { t.Errorf("Expected getCertExpiration to return an error for garbage string but returned %v", ctime) } - if ctime, err := GetCertExpiration(certBytes); err != nil || ctime != expiration.UTC() { - t.Errorf("Expected getCertExpiration to return %v but returned: %v, err: %v", expiration.UTC(), ctime, err) + // A DER encoded certificate should return an error. + if _, err := GetPEMCertExpiration(certBytes); err == nil { + t.Errorf("Expected getCertExpiration to return an error for DER certificates but returned none.") + } + + // A PEM encoded certificate should work ok. + pemCert := pemEncode(derCertificateBytes(certBytes)) + if ctime, err := GetPEMCertExpiration(pemCert); err != nil || !ctime.Equal(expiration.UTC()) { + t.Errorf("Expected getCertExpiration to return %v but returned %v. Error: %v", expiration, ctime, err) } }