mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	- Using xenolf/lego's likely-temporary acmev2 branch - Cleaned up vendor folder a little bit (probably more to do) - Temporarily set default CA URL to v2 staging endpoint - Refactored user management a bit; updated tests (biggest change is how we get the email address, which now requires being able to make an ACME client with a User with a private key so that we can get the current ToS URL) - Automatic HTTPS now allows specific wildcard pattern hostnames - Commented out (but kept) the TLS-SNI code, as the challenge type may return in the future in a similar form
		
			
				
	
	
		
			139 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package acme
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"crypto"
 | 
						|
	"crypto/ecdsa"
 | 
						|
	"crypto/elliptic"
 | 
						|
	"crypto/rsa"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"gopkg.in/square/go-jose.v2"
 | 
						|
)
 | 
						|
 | 
						|
type jws struct {
 | 
						|
	getNonceURL string
 | 
						|
	privKey     crypto.PrivateKey
 | 
						|
	kid         string
 | 
						|
	nonces      nonceManager
 | 
						|
}
 | 
						|
 | 
						|
// Posts a JWS signed message to the specified URL.
 | 
						|
// It does NOT close the response body, so the caller must
 | 
						|
// do that if no error was returned.
 | 
						|
func (j *jws) post(url string, content []byte) (*http.Response, error) {
 | 
						|
	signedContent, err := j.signContent(url, content)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	data := bytes.NewBuffer([]byte(signedContent.FullSerialize()))
 | 
						|
	resp, err := httpPost(url, "application/jose+json", data)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("Failed to HTTP POST to %s -> %s", url, err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	nonce, nonceErr := getNonceFromResponse(resp)
 | 
						|
	if nonceErr == nil {
 | 
						|
		j.nonces.Push(nonce)
 | 
						|
	}
 | 
						|
 | 
						|
	return resp, nil
 | 
						|
}
 | 
						|
 | 
						|
func (j *jws) signContent(url string, content []byte) (*jose.JSONWebSignature, error) {
 | 
						|
 | 
						|
	var alg jose.SignatureAlgorithm
 | 
						|
	switch k := j.privKey.(type) {
 | 
						|
	case *rsa.PrivateKey:
 | 
						|
		alg = jose.RS256
 | 
						|
	case *ecdsa.PrivateKey:
 | 
						|
		if k.Curve == elliptic.P256() {
 | 
						|
			alg = jose.ES256
 | 
						|
		} else if k.Curve == elliptic.P384() {
 | 
						|
			alg = jose.ES384
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	jsonKey := jose.JSONWebKey{
 | 
						|
		Key:   j.privKey,
 | 
						|
		KeyID: j.kid,
 | 
						|
	}
 | 
						|
 | 
						|
	signKey := jose.SigningKey{
 | 
						|
		Algorithm: alg,
 | 
						|
		Key:       jsonKey,
 | 
						|
	}
 | 
						|
	options := jose.SignerOptions{
 | 
						|
		NonceSource:  j,
 | 
						|
		ExtraHeaders: make(map[jose.HeaderKey]interface{}),
 | 
						|
	}
 | 
						|
	options.ExtraHeaders["url"] = url
 | 
						|
	if j.kid == "" {
 | 
						|
		options.EmbedJWK = true
 | 
						|
	}
 | 
						|
 | 
						|
	signer, err := jose.NewSigner(signKey, &options)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("Failed to create jose signer -> %s", err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	signed, err := signer.Sign(content)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
 | 
						|
	}
 | 
						|
	return signed, nil
 | 
						|
}
 | 
						|
 | 
						|
func (j *jws) Nonce() (string, error) {
 | 
						|
	if nonce, ok := j.nonces.Pop(); ok {
 | 
						|
		return nonce, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return getNonce(j.getNonceURL)
 | 
						|
}
 | 
						|
 | 
						|
type nonceManager struct {
 | 
						|
	nonces []string
 | 
						|
	sync.Mutex
 | 
						|
}
 | 
						|
 | 
						|
func (n *nonceManager) Pop() (string, bool) {
 | 
						|
	n.Lock()
 | 
						|
	defer n.Unlock()
 | 
						|
 | 
						|
	if len(n.nonces) == 0 {
 | 
						|
		return "", false
 | 
						|
	}
 | 
						|
 | 
						|
	nonce := n.nonces[len(n.nonces)-1]
 | 
						|
	n.nonces = n.nonces[:len(n.nonces)-1]
 | 
						|
	return nonce, true
 | 
						|
}
 | 
						|
 | 
						|
func (n *nonceManager) Push(nonce string) {
 | 
						|
	n.Lock()
 | 
						|
	defer n.Unlock()
 | 
						|
	n.nonces = append(n.nonces, nonce)
 | 
						|
}
 | 
						|
 | 
						|
func getNonce(url string) (string, error) {
 | 
						|
	resp, err := httpHead(url)
 | 
						|
	if err != nil {
 | 
						|
		return "", fmt.Errorf("Failed to get nonce from HTTP HEAD -> %s", err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	return getNonceFromResponse(resp)
 | 
						|
}
 | 
						|
 | 
						|
func getNonceFromResponse(resp *http.Response) (string, error) {
 | 
						|
	nonce := resp.Header.Get("Replay-Nonce")
 | 
						|
	if nonce == "" {
 | 
						|
		return "", fmt.Errorf("Server did not respond with a proper nonce header")
 | 
						|
	}
 | 
						|
 | 
						|
	return nonce, nil
 | 
						|
}
 |