{"record":{"id":"8c6b79e55deb8496","repo":"slackhq/nebula","slug":"public-key-in-cert-and-private-key-supplied-don-t","errorCode":null,"errorMessage":"public key in cert and private key supplied don't match","messagePattern":"public key in cert and private key supplied don't match","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cert/cert_v1.go","lineNumber":148,"sourceCode":"\nfunc (c *certificateV1) Expired(t time.Time) bool {\n\treturn c.details.notBefore.After(t) || c.details.notAfter.Before(t)\n}\n\nfunc (c *certificateV1) VerifyPrivateKey(curve Curve, key []byte) error {\n\tif curve != c.details.curve {\n\t\treturn fmt.Errorf(\"curve in cert and private key supplied don't match\")\n\t}\n\tif c.details.isCA {\n\t\tswitch curve {\n\t\tcase Curve_CURVE25519:\n\t\t\t// the call to PublicKey below will panic slice bounds out of range otherwise\n\t\t\tif len(key) != ed25519.PrivateKeySize {\n\t\t\t\treturn fmt.Errorf(\"key was not 64 bytes, is invalid ed25519 private key\")\n\t\t\t}\n\n\t\t\tif !ed25519.PublicKey(c.details.publicKey).Equal(ed25519.PrivateKey(key).Public()) {\n\t\t\t\treturn fmt.Errorf(\"public key in cert and private key supplied don't match\")\n\t\t\t}\n\t\tcase Curve_P256:\n\t\t\tprivkey, err := ecdh.P256().NewPrivateKey(key)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"cannot parse private key as P256: %w\", err)\n\t\t\t}\n\t\t\tpub := privkey.PublicKey().Bytes()\n\t\t\tif !bytes.Equal(pub, c.details.publicKey) {\n\t\t\t\treturn fmt.Errorf(\"public key in cert and private key supplied don't match\")\n\t\t\t}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid curve: %s\", curve)\n\t\t}\n\t\treturn nil\n\t}\n\n\tvar pub []byte\n\tswitch curve {","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/cert/cert_v1.go#L130-L166","documentation":"VerifyPrivateKey checks that the private key bytes supplied by the caller correspond to the public key embedded in the certificate. For Curve_Ed25519 it requires the key to be exactly ed25519.PrivateKeySize (64) bytes and derives the public key from it; when the derived public key does not match the certificate's public key, the certificate and key are not a pair, so verification fails with this error.","triggerScenarios":"Calling (*certificateV1).VerifyPrivateKey(pubkey, curve=Curve_Ed25519, key) where key is 64 bytes but is the private key of a different keypair than the one that signed/backs this cert, or a corrupted/transposed key.","commonSituations":"Mixing up host.key and host.crt files across hosts during provisioning; rotating the CA or keypair and updating only one of cert/key on disk; copying config files between nodes; supplying a P256 private key while the cert is Ed25519.","solutions":["Regenerate the certificate from the same private key you pass in (or vice versa) so the keypair matches","Verify you are pointing at the correct key file for this host, not another host's key","Check the curve argument matches the certificate's actual curve (Ed25519 vs P256)","Re-enroll the host or re-issue the certificate if the key was rotated"],"exampleFix":"// before\ncert.VerifyPrivateKey(mismatchedKey, cert.Curve_Ed25519, keyBytes) // errors\n// after\n// ensure keyBytes is the private key whose ed25519.PublicKey equals cert's public key\ncert.VerifyPrivateKey(mismatchedKey, cert.Curve_Ed25519, correctKeyBytes)","handlingStrategy":"validation","validationCode":"func validEd25519KeyPair(cert *cert.CertificateV1, key []byte) bool {\n    if len(key) != ed25519.PrivateKeySize {\n        return false\n    }\n    pub, ok := ed25519.PrivateKey(key).Public().(ed25519.PublicKey)\n    return ok && ed25519.PublicKey(certDetailsPublicKey(cert)).Equal(pub)\n}\nif !validEd25519KeyPair(c, keyBytes) {\n    return fmt.Errorf(\"cert and key are not a matching pair\")\n}\nerr := c.VerifyPrivateKey(key, cert.Curve_Ed25519, keyBytes)","typeGuard":"func isEd25519PrivateKey(b []byte) bool { return len(b) == ed25519.PrivateKeySize }","tryCatchPattern":null,"preventionTips":["Always issue the certificate from the exact key file you deploy alongside it","Deploy cert and key atomically from the same enrollment step","Checksum and log both files at deployment to detect swaps","Keep one keypair per host and never reuse keys across certificates"],"tags":["certificate","ed25519","key-mismatch","crypto"],"backgroundTag":"certificate-key-mismatch","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}