Support for RecoveryKey (not enabled). But not supported server side...

This commit is contained in:
xenolf 2015-10-23 16:24:02 +02:00
parent 3d311b9882
commit 4d99c9e543
7 changed files with 97 additions and 18 deletions

View file

@ -91,7 +91,11 @@ func NewClient(caURL string, usr User, keyBits int, optPort string, devMode bool
// Register the current account to the ACME server.
func (c *Client) Register() (*RegistrationResource, error) {
logger().Print("Registering account ... ")
jsonBytes, err := json.Marshal(registrationMessage{Resource: "new-reg", Contact: []string{"mailto:" + c.user.GetEmail()}})
jsonBytes, err := json.Marshal(registrationMessage{
Resource: "new-reg",
Contact: []string{"mailto:" + c.user.GetEmail()},
})
if err != nil {
return nil, err
}
@ -380,13 +384,13 @@ func (c *Client) requestCertificates(challenges []*authorizationResource) ([]Cer
}
func (c *Client) requestCertificate(authz *authorizationResource, result chan CertificateResource, errc chan error) {
privKey, err := generatePrivateKey(c.keyBits)
privKey, err := generatePrivateKey(rsakey, c.keyBits)
if err != nil {
errc <- err
return
}
csr, err := generateCsr(privKey, authz.Domain)
csr, err := generateCsr(privKey.(*rsa.PrivateKey), authz.Domain)
if err != nil {
errc <- err
return

View file

@ -1,20 +1,72 @@
package acme
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/binary"
"encoding/pem"
"fmt"
"math/big"
"time"
"golang.org/x/crypto/sha3"
)
type keyType int
type derCertificateBytes []byte
func generatePrivateKey(keyLength int) (*rsa.PrivateKey, error) {
const (
eckey keyType = iota
rsakey
)
// Derive the shared secret according to acme spec 5.6
func performECDH(priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, outLen int, label string) []byte {
// Derive Z from the private and public keys according to SEC 1 Ver. 2.0 - 3.3.1
Z, _ := priv.PublicKey.ScalarMult(pub.X, pub.Y, priv.D.Bytes())
if len(Z.Bytes())+len(label)+4 > 384 {
return nil
}
if outLen < 384*(2^32-1) {
return nil
}
// Derive the shared secret key using the ANS X9.63 KDF - SEC 1 Ver. 2.0 - 3.6.1
hasher := sha3.New384()
buffer := make([]byte, outLen)
bufferLen := 0
for i := 0; i < outLen/384; i++ {
hasher.Reset()
// Ki = Hash(Z || Counter || [SharedInfo])
hasher.Write(Z.Bytes())
binary.Write(hasher, binary.BigEndian, i)
hasher.Write([]byte(label))
hash := hasher.Sum(nil)
copied := copy(buffer[bufferLen:], hash)
bufferLen += copied
}
return buffer
}
func generatePrivateKey(t keyType, keyLength int) (crypto.PrivateKey, error) {
switch t {
case eckey:
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
case rsakey:
return rsa.GenerateKey(rand.Reader, keyLength)
}
return nil, fmt.Errorf("Invalid keytype: %d", t)
}
func generateCsr(privateKey *rsa.PrivateKey, domain string) ([]byte, error) {

View file

@ -8,7 +8,7 @@ import (
)
func TestGeneratePrivateKey(t *testing.T) {
key, err := generatePrivateKey(32)
key, err := generatePrivateKey(rsakey, 32)
if err != nil {
t.Error("Error generating private key:", err)
}
@ -18,12 +18,12 @@ func TestGeneratePrivateKey(t *testing.T) {
}
func TestGenerateCSR(t *testing.T) {
key, err := generatePrivateKey(512)
key, err := generatePrivateKey(rsakey, 512)
if err != nil {
t.Fatal("Error generating private key:", err)
}
csr, err := generateCsr(key, "fizz.buzz")
csr, err := generateCsr(key.(*rsa.PrivateKey), "fizz.buzz")
if err != nil {
t.Error("Error generating CSR:", err)
}
@ -52,14 +52,14 @@ func TestPEMEncode(t *testing.T) {
}
func TestPEMCertExpiration(t *testing.T) {
privKey, err := generatePrivateKey(2048)
privKey, err := generatePrivateKey(rsakey, 2048)
if err != nil {
t.Fatal("Error generating private key:", err)
}
expiration := time.Now().Add(365)
expiration = expiration.Round(time.Second)
certBytes, err := generateDerCert(privKey, expiration, "test.com")
certBytes, err := generateDerCert(privKey.(*rsa.PrivateKey), expiration, "test.com")
if err != nil {
t.Fatal("Error generating cert:", err)
}

View file

@ -2,6 +2,7 @@ package acme
import (
"bytes"
"crypto/ecdsa"
"crypto/rsa"
"fmt"
"net/http"
@ -14,6 +15,13 @@ type jws struct {
nonces []string
}
func keyAsJWK(key *ecdsa.PublicKey) jose.JsonWebKey {
return jose.JsonWebKey{
Key: key,
Algorithm: "EC",
}
}
// Posts a JWS signed message to the specified URL
func (j *jws) post(url string, content []byte) (*http.Response, error) {
if len(j.nonces) == 0 {

View file

@ -1,6 +1,10 @@
package acme
import "time"
import (
"time"
"github.com/letsencrypt/go-jose"
)
type directory struct {
NewAuthzURL string `json:"new-authz"`
@ -9,9 +13,16 @@ type directory struct {
RevokeCertURL string `json:"revoke-cert"`
}
type recoveryKeyMessage struct {
Length int `json:"length,omitempty"`
Client jose.JsonWebKey `json:"client,omitempty"`
Server jose.JsonWebKey `json:"client,omitempty"`
}
type registrationMessage struct {
Resource string `json:"resource"`
Contact []string `json:"contact"`
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
}
// Registration is returned by the ACME server after the registration
@ -28,6 +39,7 @@ type Registration struct {
Agreement string `json:"agreement,omitempty"`
Authorizations string `json:"authorizations,omitempty"`
Certificates string `json:"certificates,omitempty"`
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
}
// RegistrationResource represents all important informations about a registration

View file

@ -2,6 +2,7 @@ package acme
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/json"
@ -117,15 +118,16 @@ Loop:
func (s *simpleHTTPChallenge) startHTTPSServer(domain string, token string) (net.Listener, error) {
// Generate a new RSA key and a self-signed certificate.
tempPrivKey, err := generatePrivateKey(2048)
tempPrivKey, err := generatePrivateKey(rsakey, 2048)
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
if err != nil {
return nil, err
}
tempCertPEM, err := generatePemCert(tempPrivKey, domain)
tempCertPEM, err := generatePemCert(rsaPrivKey, domain)
if err != nil {
return nil, err
}
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(tempPrivKey)})
pemBytes := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
tempKeyPair, err := tls.X509KeyPair(
tempCertPEM,
pemBytes)

View file

@ -1,6 +1,7 @@
package acme
import (
"crypto/rsa"
"crypto/tls"
"encoding/json"
"io/ioutil"
@ -39,11 +40,11 @@ func TestSimpleHTTPCanSolve(t *testing.T) {
}
func TestSimpleHTTP(t *testing.T) {
privKey, err := generatePrivateKey(512)
privKey, err := generatePrivateKey(rsakey, 512)
if err != nil {
t.Errorf("Could not generate public key -> %v", err)
}
jws := &jws{privKey: privKey}
jws := &jws{privKey: privKey.(*rsa.PrivateKey)}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Replay-Nonce", "12345")
@ -103,7 +104,7 @@ func TestSimpleHTTP(t *testing.T) {
t.Errorf("Client sent invalid JWS to the server.\n\t%v", err)
return
}
output, err := j.Verify(&privKey.PublicKey)
output, err := j.Verify(&privKey.(*rsa.PrivateKey).PublicKey)
if err != nil {
t.Errorf("Unable to verify client data -> %v", err)
}
@ -129,7 +130,7 @@ func TestSimpleHTTP(t *testing.T) {
t.Errorf("Client answered with invalid JWS.\n\t%v", err)
return
}
_, err = clientResponse.Verify(&privKey.PublicKey)
_, err = clientResponse.Verify(&privKey.(*rsa.PrivateKey).PublicKey)
if err != nil {
t.Errorf("Unable to verify client data -> %v", err)
}