forked from TrueCloudLab/lego
Add initial TLS-SNI-02 challenge
This commit is contained in:
parent
0eba8326e9
commit
a42a5f66d7
6 changed files with 85 additions and 7 deletions
|
@ -10,6 +10,9 @@ const (
|
||||||
// TLSSNI01 is the "tls-sni-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni
|
// TLSSNI01 is the "tls-sni-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni
|
||||||
// Note: TLSSNI01ChallengeCert returns a certificate to fulfill this challenge
|
// Note: TLSSNI01ChallengeCert returns a certificate to fulfill this challenge
|
||||||
TLSSNI01 = Challenge("tls-sni-01")
|
TLSSNI01 = Challenge("tls-sni-01")
|
||||||
|
// TLSSNI02 is the "tls-sni-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni
|
||||||
|
// Note: TLSSNI02ChallengeCert returns a certificate to fulfill this challenge
|
||||||
|
TLSSNI02 = Challenge("tls-sni-02")
|
||||||
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns
|
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns
|
||||||
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
||||||
DNS01 = Challenge("dns-01")
|
DNS01 = Challenge("dns-01")
|
||||||
|
|
|
@ -92,6 +92,7 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
|
||||||
solvers := make(map[Challenge]solver)
|
solvers := make(map[Challenge]solver)
|
||||||
solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
|
solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
|
||||||
solvers[TLSSNI01] = &tlsSNIChallenge{jws: jws, validate: validate, provider: &TLSProviderServer{}}
|
solvers[TLSSNI01] = &tlsSNIChallenge{jws: jws, validate: validate, provider: &TLSProviderServer{}}
|
||||||
|
solvers[TLSSNI02] = &tlsSNI02Challenge{jws: jws, validate: validate, provider: &TLSProviderServer{}}
|
||||||
|
|
||||||
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
|
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
|
||||||
}
|
}
|
||||||
|
@ -139,6 +140,10 @@ func (c *Client) SetTLSAddress(iface string) error {
|
||||||
if chlng, ok := c.solvers[TLSSNI01]; ok {
|
if chlng, ok := c.solvers[TLSSNI01]; ok {
|
||||||
chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port)
|
chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if chlng, ok := c.solvers[TLSSNI02]; ok {
|
||||||
|
chlng.(*tlsSNI02Challenge).provider = NewTLSProviderServer(host, port)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ func TestNewClient(t *testing.T) {
|
||||||
t.Errorf("Expected keyType to be %s but was %s", keyType, client.keyType)
|
t.Errorf("Expected keyType to be %s but was %s", keyType, client.keyType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expected, actual := 2, len(client.solvers); actual != expected {
|
if expected, actual := 3, len(client.solvers); actual != expected {
|
||||||
t.Fatalf("Expected %d solver(s), got %d", expected, actual)
|
t.Fatalf("Expected %d solver(s), got %d", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,8 +298,8 @@ func getCertExpiration(cert []byte) (time.Time, error) {
|
||||||
return pCert.NotAfter, nil
|
return pCert.NotAfter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
func generatePemCert(privKey *rsa.PrivateKey, domains ...string) ([]byte, error) {
|
||||||
derBytes, err := generateDerCert(privKey, time.Time{}, domain)
|
derBytes, err := generateDerCert(privKey, time.Time{}, domains...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
||||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domains ...string) ([]byte, error) {
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -328,7 +328,7 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin
|
||||||
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
DNSNames: []string{domain},
|
DNSNames: domains,
|
||||||
}
|
}
|
||||||
|
|
||||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
||||||
|
|
72
acme/tls_sni_02_challenge.go
Normal file
72
acme/tls_sni_02_challenge.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tlsSNI02Challenge struct {
|
||||||
|
jws *jws
|
||||||
|
validate validateFunc
|
||||||
|
provider ChallengeProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tlsSNI02Challenge) Solve(chlng challenge, domain string) error {
|
||||||
|
logf("[INFO][%s] acme: Trying to solve TLS-SNI-02", domain)
|
||||||
|
|
||||||
|
// Generate the Key Authorization for the challenge
|
||||||
|
keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.provider.Present(domain, chlng.Token, keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := t.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[%s] error cleaning up: %v", domain, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return t.validate(t.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSSNI02ChallengeCert returns a certificate for the `tls-sni-02` challenge
|
||||||
|
func TLSSNI02ChallengeCert(token, keyAuth string) (tls.Certificate, error) {
|
||||||
|
|
||||||
|
// Construct SanA value from token sha256
|
||||||
|
tokenShaBytes := sha256.Sum256([]byte(token))
|
||||||
|
tokenSha := hex.EncodeToString(tokenShaBytes[:sha256.Size])
|
||||||
|
sanA := fmt.Sprintf("%s.%s.token.acme.invalid", tokenSha[:32], tokenSha[32:])
|
||||||
|
|
||||||
|
// Construct SanB value from keyAuth sha256
|
||||||
|
keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
|
||||||
|
keyAuthSha := hex.EncodeToString(keyAuthShaBytes[:sha256.Size])
|
||||||
|
sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", keyAuthSha[:32], keyAuthSha[32:])
|
||||||
|
|
||||||
|
// generate a new RSA key for the certificate
|
||||||
|
tempPrivKey, err := generatePrivateKey(RSA2048)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
||||||
|
rsaPrivPEM := pemEncode(rsaPrivKey)
|
||||||
|
|
||||||
|
tempCertPEM, err := generatePemCert(rsaPrivKey, sanA, sanB)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificate, nil
|
||||||
|
}
|
|
@ -16,8 +16,6 @@ type tlsSNIChallenge struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tlsSNIChallenge) Solve(chlng challenge, domain string) error {
|
func (t *tlsSNIChallenge) Solve(chlng challenge, domain string) error {
|
||||||
// FIXME: https://github.com/ietf-wg-acme/acme/pull/22
|
|
||||||
// Currently we implement this challenge to track boulder, not the current spec!
|
|
||||||
|
|
||||||
logf("[INFO][%s] acme: Trying to solve TLS-SNI-01", domain)
|
logf("[INFO][%s] acme: Trying to solve TLS-SNI-01", domain)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue