ECDSA account key support

This commit is contained in:
Hugo Landau 2016-01-07 23:43:15 +00:00
parent b92a876bd8
commit 5c9be5385b
4 changed files with 146 additions and 11 deletions

View File

@ -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
}

View File

@ -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")
}

View File

@ -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)
}
}

View File

@ -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()), &reg)
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()