forked from TrueCloudLab/lego
Compare commits
1 commit
master
...
add-tlssni
Author | SHA1 | Date | |
---|---|---|---|
|
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
|
||||
// Note: TLSSNI01ChallengeCert returns a certificate to fulfill this challenge
|
||||
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
|
||||
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
||||
DNS01 = Challenge("dns-01")
|
||||
|
|
|
@ -92,6 +92,7 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
|
|||
solvers := make(map[Challenge]solver)
|
||||
solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
|
||||
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
|
||||
}
|
||||
|
@ -139,6 +140,10 @@ func (c *Client) SetTLSAddress(iface string) error {
|
|||
if chlng, ok := c.solvers[TLSSNI01]; ok {
|
||||
chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port)
|
||||
}
|
||||
|
||||
if chlng, ok := c.solvers[TLSSNI02]; ok {
|
||||
chlng.(*tlsSNI02Challenge).provider = NewTLSProviderServer(host, port)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ func TestNewClient(t *testing.T) {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -298,8 +298,8 @@ func getCertExpiration(cert []byte) (time.Time, error) {
|
|||
return pCert.NotAfter, nil
|
||||
}
|
||||
|
||||
func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
||||
derBytes, err := generateDerCert(privKey, time.Time{}, domain)
|
||||
func generatePemCert(privKey *rsa.PrivateKey, domains ...string) ([]byte, error) {
|
||||
derBytes, err := generateDerCert(privKey, time.Time{}, domains...)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
|
@ -328,7 +328,7 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin
|
|||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{domain},
|
||||
DNSNames: domains,
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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)
|
||||
|
||||
|
|
Loading…
Reference in a new issue