ECDSA account key support
This commit is contained in:
parent
b92a876bd8
commit
5c9be5385b
|
|
@ -48,6 +48,20 @@ const (
|
|||
"n":"qnARLrT7Xz4gRcKyLdydmCr-ey9OuPImX4X40thk3on26FkMznR3fRjs66eLK7mmPcBZ6uOJseURU6wAaZNmemoYx1dMvqvWWIyiQleHSD7Q8vBrhR6uIoO4jAzJZR-ChzZuSDt7iHN-3xUVspu5XGwXU_MVJZshTwp4TaFx5elHIT_ObnTvTOU3Xhish07AbgZKmWsVbXh5s-CrIicU4OexJPgunWZ_YJJueOKmTvnLlTV4MzKR2oZlBKZ27S0-SfdV_QDx_ydle5oMAyKVtlAV35cyPMIsYNwgUGBCdY_2Uzi5eX0lTc7MPRwz6qR1kip-i59VcGcUQgqHV6Fyqw",
|
||||
"e":"AAEAAQ"
|
||||
}`
|
||||
|
||||
testE1KeyPublicJSON = `{
|
||||
"kty":"EC",
|
||||
"crv":"P-256",
|
||||
"x":"FwvSZpu06i3frSk_mz9HcD9nETn4wf3mQ-zDtG21Gao",
|
||||
"y":"S8rR-0dWa8nAcw1fbunF_ajS3PQZ-QwLps-2adgLgPk"
|
||||
}`
|
||||
testE2KeyPublicJSON = `{
|
||||
"kty":"EC",
|
||||
"crv":"P-256",
|
||||
"x":"S8FOmrZ3ywj4yyFqt0etAD90U-EnkNaOBSLfQmf7pNg",
|
||||
"y":"vMvpDyqFDRHjGfZ1siDOm5LS6xNdR5xTpyoQGLDOX2Q"
|
||||
}`
|
||||
|
||||
agreementURL = "http://example.invalid/terms"
|
||||
)
|
||||
|
||||
|
|
@ -79,8 +93,12 @@ func (sa *StorageAuthority) GetRegistration(id int64) (core.Registration, error)
|
|||
func (sa *StorageAuthority) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration, error) {
|
||||
var test1KeyPublic jose.JsonWebKey
|
||||
var test2KeyPublic jose.JsonWebKey
|
||||
var testE1KeyPublic jose.JsonWebKey
|
||||
var testE2KeyPublic jose.JsonWebKey
|
||||
test1KeyPublic.UnmarshalJSON([]byte(test1KeyPublicJSON))
|
||||
test2KeyPublic.UnmarshalJSON([]byte(test2KeyPublicJSON))
|
||||
testE1KeyPublic.UnmarshalJSON([]byte(testE1KeyPublicJSON))
|
||||
testE2KeyPublic.UnmarshalJSON([]byte(testE2KeyPublicJSON))
|
||||
|
||||
if core.KeyDigestEquals(jwk, test1KeyPublic) {
|
||||
return core.Registration{ID: 1, Key: jwk, Agreement: agreementURL}, nil
|
||||
|
|
@ -91,6 +109,14 @@ func (sa *StorageAuthority) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Regi
|
|||
return core.Registration{ID: 2}, core.NoSuchRegistrationError("reg not found")
|
||||
}
|
||||
|
||||
if core.KeyDigestEquals(jwk, testE1KeyPublic) {
|
||||
return core.Registration{ID: 3, Key: jwk, Agreement: agreementURL}, nil
|
||||
}
|
||||
|
||||
if core.KeyDigestEquals(jwk, testE2KeyPublic) {
|
||||
return core.Registration{ID: 4}, core.NoSuchRegistrationError("reg not found")
|
||||
}
|
||||
|
||||
// Return a fake registration. Make sure to fill the key field to avoid marshaling errors.
|
||||
return core.Registration{ID: 1, Key: test1KeyPublic, Agreement: agreementURL}, nil
|
||||
}
|
||||
|
|
|
|||
13
wfe/jose.go
13
wfe/jose.go
|
|
@ -1,6 +1,7 @@
|
|||
package wfe
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
|
||||
|
|
@ -9,10 +10,18 @@ import (
|
|||
)
|
||||
|
||||
func algorithmForKey(key *jose.JsonWebKey) (string, error) {
|
||||
// TODO(https://github.com/letsencrypt/boulder/issues/792): Support EC.
|
||||
switch key.Key.(type) {
|
||||
switch k := key.Key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
return string(jose.RS256), nil
|
||||
case *ecdsa.PublicKey:
|
||||
switch k.Params().Name {
|
||||
case "P-256":
|
||||
return string(jose.ES256), nil
|
||||
case "P-384":
|
||||
return string(jose.ES384), nil
|
||||
case "P-521":
|
||||
return string(jose.ES512), nil
|
||||
}
|
||||
}
|
||||
return "", core.SignatureValidationError("no signature algorithms suitable for given key type")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package wfe
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"testing"
|
||||
|
||||
|
|
@ -64,15 +65,6 @@ func TestCheckAlgorithm(t *testing.T) {
|
|||
expectedErr string
|
||||
expectedStat string
|
||||
}{
|
||||
{
|
||||
jose.JsonWebKey{
|
||||
Algorithm: "ES256",
|
||||
Key: &ecdsa.PublicKey{},
|
||||
},
|
||||
jose.JsonWebSignature{},
|
||||
"no signature algorithms suitable for given key type",
|
||||
"WFE.Errors.NoAlgorithmForKey",
|
||||
},
|
||||
{
|
||||
jose.JsonWebKey{
|
||||
Algorithm: "HS256",
|
||||
|
|
@ -173,4 +165,39 @@ func TestCheckAlgorithmSuccess(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("RS256 key: Expected nil error, got '%s'", err)
|
||||
}
|
||||
|
||||
_, err = checkAlgorithm(&jose.JsonWebKey{
|
||||
Algorithm: "ES256",
|
||||
Key: &ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
},
|
||||
}, &jose.JsonWebSignature{
|
||||
Signatures: []jose.Signature{
|
||||
jose.Signature{
|
||||
Header: jose.JoseHeader{
|
||||
Algorithm: "ES256",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("ES256 key: Expected nil error, got '%s'", err)
|
||||
}
|
||||
|
||||
_, err = checkAlgorithm(&jose.JsonWebKey{
|
||||
Key: &ecdsa.PublicKey{
|
||||
Curve: elliptic.P256(),
|
||||
},
|
||||
}, &jose.JsonWebSignature{
|
||||
Signatures: []jose.Signature{
|
||||
jose.Signature{
|
||||
Header: jose.JoseHeader{
|
||||
Algorithm: "ES256",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("ES256 key: Expected nil error, got '%s'", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package wfe
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
|
|
@ -111,6 +112,32 @@ TkLlEeVOuQfxTadw05gzKX0jKkMC4igGxvEeilYc6NR6a4nvRulG84Q8VV9Sy9Ie
|
|||
wk6Oiadty3eQqSBJv0HnpmiEdQVffIK5Pg4M8Dd+aOBnEkbopAJOuA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
testE1KeyPublicJSON = `{
|
||||
"kty":"EC",
|
||||
"crv":"P-256",
|
||||
"x":"FwvSZpu06i3frSk_mz9HcD9nETn4wf3mQ-zDtG21Gao",
|
||||
"y":"S8rR-0dWa8nAcw1fbunF_ajS3PQZ-QwLps-2adgLgPk"
|
||||
}`
|
||||
testE1KeyPrivatePEM = `
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIH+p32RUnqT/iICBEGKrLIWFcyButv0S0lU/BLPOyHn2oAoGCCqGSM49
|
||||
AwEHoUQDQgAEFwvSZpu06i3frSk/mz9HcD9nETn4wf3mQ+zDtG21GapLytH7R1Zr
|
||||
ycBzDV9u6cX9qNLc9Bn5DAumz7Zp2AuA+Q==
|
||||
-----END EC PRIVATE KEY-----
|
||||
`
|
||||
|
||||
testE2KeyPublicJSON = `{
|
||||
"kty":"EC",
|
||||
"crv":"P-256",
|
||||
"x":"S8FOmrZ3ywj4yyFqt0etAD90U-EnkNaOBSLfQmf7pNg",
|
||||
"y":"vMvpDyqFDRHjGfZ1siDOm5LS6xNdR5xTpyoQGLDOX2Q"
|
||||
}`
|
||||
testE2KeyPrivatePEM = `
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIFRcPxQ989AY6se2RyIoF1ll9O6gHev4oY15SWJ+Jf5eoAoGCCqGSM49
|
||||
AwEHoUQDQgAES8FOmrZ3ywj4yyFqt0etAD90U+EnkNaOBSLfQmf7pNi8y+kPKoUN
|
||||
EeMZ9nWyIM6bktLrE11HnFOnKhAYsM5fZA==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
)
|
||||
|
||||
type MockRegistrationAuthority struct{}
|
||||
|
|
@ -766,6 +793,52 @@ func TestBadNonce(t *testing.T) {
|
|||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:badNonce","detail":"JWS has no anti-replay nonce","status":400}`)
|
||||
}
|
||||
|
||||
func TestNewECDSARegistration(t *testing.T) {
|
||||
wfe, _ := setupWFE(t)
|
||||
|
||||
// E1 always exists; E2 never exists
|
||||
key, err := jose.LoadPrivateKey([]byte(testE2KeyPrivatePEM))
|
||||
test.AssertNotError(t, err, "Failed to load key")
|
||||
ecdsaKey, ok := key.(*ecdsa.PrivateKey)
|
||||
test.Assert(t, ok, "Couldn't load ECDSA key")
|
||||
signer, err := jose.NewSigner("ES256", ecdsaKey)
|
||||
test.AssertNotError(t, err, "Failed to make signer")
|
||||
signer.SetNonceSource(wfe.nonceService)
|
||||
|
||||
responseWriter := httptest.NewRecorder()
|
||||
result, err := signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"` + agreementURL + `"}`))
|
||||
test.AssertNotError(t, err, "Failed to sign")
|
||||
wfe.NewRegistration(newRequestEvent(), responseWriter, makePostRequest(result.FullSerialize()))
|
||||
|
||||
var reg core.Registration
|
||||
err = json.Unmarshal([]byte(responseWriter.Body.String()), ®)
|
||||
test.AssertNotError(t, err, "Couldn't unmarshal returned registration object")
|
||||
test.Assert(t, len(reg.Contact) >= 1, "No contact field in registration")
|
||||
test.AssertEquals(t, reg.Contact[0].String(), "tel:123456789")
|
||||
test.AssertEquals(t, reg.Agreement, "http://example.invalid/terms")
|
||||
test.AssertEquals(t, reg.InitialIP.String(), "1.1.1.1")
|
||||
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Location"), "/acme/reg/0")
|
||||
|
||||
key, err = jose.LoadPrivateKey([]byte(testE1KeyPrivatePEM))
|
||||
test.AssertNotError(t, err, "Failed to load key")
|
||||
ecdsaKey, ok = key.(*ecdsa.PrivateKey)
|
||||
test.Assert(t, ok, "Couldn't load ECDSA key")
|
||||
signer, err = jose.NewSigner("ES256", ecdsaKey)
|
||||
test.AssertNotError(t, err, "Failed to make signer")
|
||||
signer.SetNonceSource(wfe.nonceService)
|
||||
|
||||
// Reset the body and status code
|
||||
responseWriter = httptest.NewRecorder()
|
||||
// POST, Valid JSON, Key already in use
|
||||
result, err = signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"` + agreementURL + `"}`))
|
||||
|
||||
wfe.NewRegistration(newRequestEvent(), responseWriter, makePostRequest(result.FullSerialize()))
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Registration key is already in use","status":409}`)
|
||||
test.AssertEquals(t, responseWriter.Header().Get("Location"), "/acme/reg/3")
|
||||
test.AssertEquals(t, responseWriter.Code, 409)
|
||||
}
|
||||
|
||||
func TestNewRegistration(t *testing.T) {
|
||||
wfe, _ := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
|
|
|
|||
Loading…
Reference in New Issue