forked from TrueCloudLab/lego
Support for RecoveryKey (not enabled). But not supported server side...
This commit is contained in:
parent
3d311b9882
commit
4d99c9e543
7 changed files with 97 additions and 18 deletions
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
return rsa.GenerateKey(rand.Reader, keyLength)
|
||||
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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue