package acme import ( "crypto" "crypto/ecdsa" "crypto/rsa" "encoding/base64" "errors" "io" "net/http" "strings" "github.com/square/go-jose" ) // KeySize is a default RSA key size const KeySize = 2048 var errNoNonces = errors.New("out of nonces") // Signer ... type Signer struct { signer jose.Signer thumb string nonces chan string } func NewSigner(privKey crypto.PrivateKey) (*Signer, error) { thumb := func(pubKey crypto.PublicKey, alg string) (string, error) { wk := &jose.JsonWebKey{Key: pubKey, Algorithm: alg} t, err := wk.Thumbprint(crypto.SHA256) return base64.RawURLEncoding.EncodeToString(t), err } switch k := privKey.(type) { case *rsa.PrivateKey: s, err := jose.NewSigner(jose.RS256, k) if err != nil { return nil, err } t, err := thumb(k.Public(), "RSA") if err != nil { return nil, err } sig := &Signer{signer: s, thumb: t, nonces: make(chan string, 100)} sig.signer.SetNonceSource(sig) return sig, nil case *ecdsa.PrivateKey: s, err := jose.NewSigner(jose.ES384, k) if err != nil { return nil, err } t, err := thumb(k.Public(), "EC") if err != nil { return nil, err } sig := &Signer{signer: s, thumb: t, nonces: make(chan string, 100)} sig.signer.SetNonceSource(sig) return sig, nil default: return nil, ErrKeyType } } // Nonce implements jose nonce provider func (s Signer) Nonce() (string, error) { select { case nonce := <-s.nonces: return nonce, nil default: return "", errNoNonces } } // RoundTrip extracts nonces from HTTP response func (s Signer) RoundTrip(req *http.Request) (*http.Response, error) { resp, err := http.DefaultTransport.RoundTrip(req) if err != nil { return nil, err } nonce := resp.Header.Get("Replay-Nonce") if nonce == "" { return nil, errNoNonces } if len(s.nonces) == cap(s.nonces) { <-s.nonces // drop oldest } s.nonces <- nonce return resp, nil } // Sign implements Signer interface func (s Signer) Sign(msg []byte) (io.Reader, error) { obj, err := s.signer.Sign(msg) if err != nil { return nil, err } return strings.NewReader(obj.FullSerialize()), nil } func (s Signer) KeyAuth(token string) string { return token + "." + s.thumb }