Check CA cert and key match in nebula-cert sign (#503)
`func (nc *NebulaCertificate) VerifyPrivateKey(key []byte) error` would previously return an error even if passed the correct private key for a CA certificate `nc`. That function has been updated to support CA certificates, and nebula-cert now calls it before signing a new certificate. Previously, it would perform all constraint checks against the CA certificate provided, take a SHA256 fingerprint of the provided certificate, insert it into the new node certificate, and then finally sign it with the mismatching private key provided.
This commit is contained in:
parent
9f34c5e2ba
commit
34d002d695
|
@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- SSH server handles single `exec` requests correctly. (#483)
|
- SSH server handles single `exec` requests correctly. (#483)
|
||||||
|
|
||||||
|
- Signing a certificate with `nebula-cert sign` now verifies that the supplied
|
||||||
|
ca-key matches the ca-crt. (#503)
|
||||||
|
|
||||||
## [1.4.0] - 2021-05-11
|
## [1.4.0] - 2021-05-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
13
cert/cert.go
13
cert/cert.go
|
@ -325,12 +325,25 @@ func (nc *NebulaCertificate) CheckRootConstrains(signer *NebulaCertificate) erro
|
||||||
|
|
||||||
// VerifyPrivateKey checks that the public key in the Nebula certificate and a supplied private key match
|
// VerifyPrivateKey checks that the public key in the Nebula certificate and a supplied private key match
|
||||||
func (nc *NebulaCertificate) VerifyPrivateKey(key []byte) error {
|
func (nc *NebulaCertificate) VerifyPrivateKey(key []byte) error {
|
||||||
|
if nc.Details.IsCA {
|
||||||
|
// the call to PublicKey below will panic slice bounds out of range otherwise
|
||||||
|
if len(key) != ed25519.PrivateKeySize {
|
||||||
|
return fmt.Errorf("key was not 64 bytes, is invalid ed25519 private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ed25519.PublicKey(nc.Details.PublicKey).Equal(ed25519.PrivateKey(key).Public()) {
|
||||||
|
return fmt.Errorf("public key in cert and private key supplied don't match")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var dst, key32 [32]byte
|
var dst, key32 [32]byte
|
||||||
copy(key32[:], key)
|
copy(key32[:], key)
|
||||||
curve25519.ScalarBaseMult(&dst, &key32)
|
curve25519.ScalarBaseMult(&dst, &key32)
|
||||||
if !bytes.Equal(dst[:], nc.Details.PublicKey) {
|
if !bytes.Equal(dst[:], nc.Details.PublicKey) {
|
||||||
return fmt.Errorf("public key in cert and private key supplied don't match")
|
return fmt.Errorf("public key in cert and private key supplied don't match")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -375,9 +375,16 @@ func TestNebulaCertificate_Verify_Subnets(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNebulaVerifyPrivateKey(t *testing.T) {
|
func TestNebulaCertificate_VerifyPrivateKey(t *testing.T) {
|
||||||
ca, _, caKey, err := newTestCaCert(time.Time{}, time.Time{}, []*net.IPNet{}, []*net.IPNet{}, []string{})
|
ca, _, caKey, err := newTestCaCert(time.Time{}, time.Time{}, []*net.IPNet{}, []*net.IPNet{}, []string{})
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
err = ca.VerifyPrivateKey(caKey)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, _, caKey2, err := newTestCaCert(time.Time{}, time.Time{}, []*net.IPNet{}, []*net.IPNet{}, []string{})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
err = ca.VerifyPrivateKey(caKey2)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
c, _, priv, err := newTestCert(ca, caKey, time.Time{}, time.Time{}, []*net.IPNet{}, []*net.IPNet{}, []string{})
|
c, _, priv, err := newTestCert(ca, caKey, time.Time{}, time.Time{}, []*net.IPNet{}, []*net.IPNet{}, []string{})
|
||||||
err = c.VerifyPrivateKey(priv)
|
err = c.VerifyPrivateKey(priv)
|
||||||
|
|
|
@ -92,6 +92,10 @@ func signCert(args []string, out io.Writer, errOut io.Writer) error {
|
||||||
return fmt.Errorf("error while parsing ca-crt: %s", err)
|
return fmt.Errorf("error while parsing ca-crt: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := caCert.VerifyPrivateKey(caKey); err != nil {
|
||||||
|
return fmt.Errorf("refusing to sign, root certificate does not match private key")
|
||||||
|
}
|
||||||
|
|
||||||
issuer, err := caCert.Sha256Sum()
|
issuer, err := caCert.Sha256Sum()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while getting -ca-crt fingerprint: %s", err)
|
return fmt.Errorf("error while getting -ca-crt fingerprint: %s", err)
|
||||||
|
|
|
@ -167,6 +167,20 @@ func Test_signCert(t *testing.T) {
|
||||||
assert.Empty(t, ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Empty(t, eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
|
// mismatched ca key
|
||||||
|
_, caPriv2, _ := ed25519.GenerateKey(rand.Reader)
|
||||||
|
caKeyF2, err := ioutil.TempFile("", "sign-cert-2.key")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.Remove(caKeyF2.Name())
|
||||||
|
caKeyF2.Write(cert.MarshalEd25519PrivateKey(caPriv2))
|
||||||
|
|
||||||
|
ob.Reset()
|
||||||
|
eb.Reset()
|
||||||
|
args = []string{"-ca-crt", caCrtF.Name(), "-ca-key", caKeyF2.Name(), "-name", "test", "-ip", "1.1.1.1/24", "-out-crt", "nope", "-out-key", "nope", "-duration", "100m", "-subnets", "a"}
|
||||||
|
assert.EqualError(t, signCert(args, ob, eb), "refusing to sign, root certificate does not match private key")
|
||||||
|
assert.Empty(t, ob.String())
|
||||||
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// failed key write
|
// failed key write
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
eb.Reset()
|
eb.Reset()
|
||||||
|
|
Loading…
Reference in New Issue