Add support for getting OCSP responses for OCSPStapling

This commit is contained in:
xenolf 2015-10-24 01:13:50 +02:00
parent 4d99c9e543
commit d6f4e42b13

View file

@ -1,6 +1,7 @@
package acme
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
@ -10,10 +11,15 @@ import (
"crypto/x509/pkix"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"log"
"math/big"
"net/http"
"time"
"golang.org/x/crypto/ocsp"
"golang.org/x/crypto/sha3"
)
@ -25,6 +31,69 @@ const (
rsakey
)
// GetOCSPForCert takes a PEM encoded cert or cert bundle and returns a OCSP
// response from the OCSP endpoint in the certificate.
// This []byte can be passed directly into the OCSPStaple property of a tls.Certificate.
// If the bundle only contains the issued certificate, this function will try
// to get the issuer certificate from the IssuingCertificateURL in the certificate.
func GetOCSPForCert(bundle []byte) ([]byte, error) {
certificates, err := parsePEMBundle(bundle)
if err != nil {
return nil, err
}
// We only got one certificate, means we have no issuer certificate - get it.
if len(certificates) == 1 {
// TODO: build fallback. If this fails, check the remaining array entries.
resp, err := http.Get(certificates[0].IssuingCertificateURL[0])
if err != nil {
return nil, err
}
issuerBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
issuerCert, err := x509.ParseCertificate(issuerBytes)
if err != nil {
return nil, err
}
// Insert it into the slice on position 0
certificates = append(certificates, nil)
copy(certificates[1:], certificates[0:])
certificates[0] = issuerCert
}
// We expect the certificate slice to be ordered downwards the chain.
// CA -> CRT. We need to pull the cert and issuer cert out of it, which should
// always be the last two certificates.
issuedCert := certificates[len(certificates)-1]
issuerCert := certificates[len(certificates)-2]
// Finally kick off the OCSP request.
ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
if err != nil {
return nil, err
}
reader := bytes.NewReader(ocspReq)
req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader)
if err != nil {
return nil, err
}
ocspResBytes, err := ioutil.ReadAll(req.Body)
_, err = ocsp.ParseResponse(ocspResBytes, nil)
if err != nil {
log.Printf("OCSPParse Error: %v", err)
return nil, err
}
return ocspResBytes, nil
}
// Derive the shared secret according to acme spec 5.6
func performECDH(priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, outLen int, label string) []byte {
// Derive Z from the private and public keys according to SEC 1 Ver. 2.0 - 3.3.1
@ -58,6 +127,33 @@ func performECDH(priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, outLen int, label
return buffer
}
func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
var certificates []*x509.Certificate
remaining := bundle
for len(remaining) != 0 {
certBlock, rem := pem.Decode(remaining)
// Thanks golang for having me do this :[
remaining = rem
if certBlock == nil {
return nil, errors.New("Could not decode certificate.")
}
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, err
}
certificates = append(certificates, cert)
}
if len(certificates) == 0 {
return nil, errors.New("No certificates were found while parsing the bundle.")
}
return certificates, nil
}
func generatePrivateKey(t keyType, keyLength int) (crypto.PrivateKey, error) {
switch t {
case eckey: