Add support for getting OCSP responses for OCSPStapling
This commit is contained in:
parent
4d99c9e543
commit
d6f4e42b13
1 changed files with 96 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
||||||
package acme
|
package acme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
|
@ -10,10 +11,15 @@ import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,6 +31,69 @@ const (
|
||||||
rsakey
|
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
|
// Derive the shared secret according to acme spec 5.6
|
||||||
func performECDH(priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, outLen int, label string) []byte {
|
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
|
// 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
|
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) {
|
func generatePrivateKey(t keyType, keyLength int) (crypto.PrivateKey, error) {
|
||||||
switch t {
|
switch t {
|
||||||
case eckey:
|
case eckey:
|
||||||
|
|
Loading…
Reference in a new issue