forked from TrueCloudLab/certificates
Add support for TLS-ALPN-01 challenge
This commit is contained in:
parent
76dcf542d4
commit
2f40011da8
1 changed files with 57 additions and 10 deletions
|
@ -107,23 +107,30 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb
|
||||||
}
|
}
|
||||||
|
|
||||||
func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error {
|
func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error {
|
||||||
|
|
||||||
|
var serverName string
|
||||||
|
|
||||||
|
// RFC8738 states that, if HostName is IP, it should be the ARPA
|
||||||
|
// address https://datatracker.ietf.org/doc/html/rfc8738#section-6.
|
||||||
|
// It also references TLS Extensions [RFC6066].
|
||||||
|
if ip := net.ParseIP(ch.Value); ip != nil {
|
||||||
|
serverName = reverseAddr(ip)
|
||||||
|
} else {
|
||||||
|
serverName = ch.Value
|
||||||
|
}
|
||||||
|
|
||||||
config := &tls.Config{
|
config := &tls.Config{
|
||||||
NextProtos: []string{"acme-tls/1"},
|
NextProtos: []string{"acme-tls/1"},
|
||||||
// https://tools.ietf.org/html/rfc8737#section-4
|
// https://tools.ietf.org/html/rfc8737#section-4
|
||||||
// ACME servers that implement "acme-tls/1" MUST only negotiate TLS 1.2
|
// ACME servers that implement "acme-tls/1" MUST only negotiate TLS 1.2
|
||||||
// [RFC5246] or higher when connecting to clients for validation.
|
// [RFC5246] or higher when connecting to clients for validation.
|
||||||
MinVersion: tls.VersionTLS12,
|
MinVersion: tls.VersionTLS12,
|
||||||
ServerName: ch.Value,
|
ServerName: serverName,
|
||||||
InsecureSkipVerify: true, // we expect a self-signed challenge certificate
|
InsecureSkipVerify: true, // we expect a self-signed challenge certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
hostPort := net.JoinHostPort(ch.Value, "443")
|
hostPort := net.JoinHostPort(ch.Value, "443")
|
||||||
|
|
||||||
fmt.Println(hostPort)
|
|
||||||
fmt.Println(fmt.Sprintf("%#+v", config))
|
|
||||||
|
|
||||||
time.Sleep(5 * time.Second) // TODO: remove this; client seems to take a while to start serving; the server does not seem to retry the conn
|
|
||||||
|
|
||||||
conn, err := vo.TLSDial("tcp", hostPort, config)
|
conn, err := vo.TLSDial("tcp", hostPort, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -135,9 +142,6 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||||
cs := conn.ConnectionState()
|
cs := conn.ConnectionState()
|
||||||
certs := cs.PeerCertificates
|
certs := cs.PeerCertificates
|
||||||
|
|
||||||
fmt.Println(fmt.Sprintf("%#+v", cs))
|
|
||||||
fmt.Println(fmt.Sprintf("%#+v", certs))
|
|
||||||
|
|
||||||
if len(certs) == 0 {
|
if len(certs) == 0 {
|
||||||
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
|
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
|
||||||
"%s challenge for %s resulted in no certificates", ch.Type, ch.Value))
|
"%s challenge for %s resulted in no certificates", ch.Type, ch.Value))
|
||||||
|
@ -149,7 +153,6 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON
|
||||||
}
|
}
|
||||||
|
|
||||||
leafCert := certs[0]
|
leafCert := certs[0]
|
||||||
fmt.Println(fmt.Sprintf("%#+v", leafCert))
|
|
||||||
|
|
||||||
// if no DNS names present, look for IP address and verify that exactly one exists
|
// if no DNS names present, look for IP address and verify that exactly one exists
|
||||||
if len(leafCert.DNSNames) == 0 {
|
if len(leafCert.DNSNames) == 0 {
|
||||||
|
@ -262,6 +265,50 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP
|
||||||
|
// address addr suitable for rDNS (PTR) record lookup or an error if it fails
|
||||||
|
// to parse the IP address.
|
||||||
|
// Implementation taken and adapted from https://golang.org/src/net/dnsclient.go?s=780:834#L20
|
||||||
|
func reverseAddr(ip net.IP) (arpa string) {
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return uitoa(uint(ip[15])) + "." + uitoa(uint(ip[14])) + "." + uitoa(uint(ip[13])) + "." + uitoa(uint(ip[12])) + ".in-addr.arpa."
|
||||||
|
}
|
||||||
|
// Must be IPv6
|
||||||
|
buf := make([]byte, 0, len(ip)*4+len("ip6.arpa."))
|
||||||
|
// Add it, in reverse, to the buffer
|
||||||
|
for i := len(ip) - 1; i >= 0; i-- {
|
||||||
|
v := ip[i]
|
||||||
|
buf = append(buf, hexit[v&0xF],
|
||||||
|
'.',
|
||||||
|
hexit[v>>4],
|
||||||
|
'.')
|
||||||
|
}
|
||||||
|
// Append "ip6.arpa." and return (buf already has the final .)
|
||||||
|
buf = append(buf, "ip6.arpa."...)
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert unsigned integer to decimal string.
|
||||||
|
// Implementation taken from https://golang.org/src/net/parse.go
|
||||||
|
func uitoa(val uint) string {
|
||||||
|
if val == 0 { // avoid string allocation
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
var buf [20]byte // big enough for 64bit value base 10
|
||||||
|
i := len(buf) - 1
|
||||||
|
for val >= 10 {
|
||||||
|
q := val / 10
|
||||||
|
buf[i] = byte('0' + val - q*10)
|
||||||
|
i--
|
||||||
|
val = q
|
||||||
|
}
|
||||||
|
// val < 10
|
||||||
|
buf[i] = byte('0' + val)
|
||||||
|
return string(buf[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
const hexit = "0123456789abcdef"
|
||||||
|
|
||||||
// KeyAuthorization creates the ACME key authorization value from a token
|
// KeyAuthorization creates the ACME key authorization value from a token
|
||||||
// and a jwk.
|
// and a jwk.
|
||||||
func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) {
|
func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) {
|
||||||
|
|
Loading…
Reference in a new issue