slackhq/nebula · critical

private key is not a pair with public key in nebula cert: %w

Error message

private key is not a pair with public key in nebula cert: %w

What it means

newCertState calls v1.VerifyPrivateKey(curve, privateKey) and the private key in pki.key does not correspond to the public key inside the v1 nebula certificate. The %w wraps the underlying mismatch error.

Source

Thrown at pki.go:423

		}

		if v1.Curve() != v2.Curve() {
			return nil, util.NewContextualError("v1 and v2 curve are not the same, ignoring", nil, nil)
		}

		if v1.Networks()[0] != v2.Networks()[0] {
			return nil, util.NewContextualError("v1 and v2 networks are not the same", nil, nil)
		}

		cs.initiatingVersion = dv
	}

	if v1 != nil {
		if pkcs11backed {
			//NOTE: We do not currently have a method to verify a public private key pair when the private key is in an hsm
		} else {
			if err := v1.VerifyPrivateKey(privateKeyCurve, privateKey); err != nil {
				return nil, fmt.Errorf("private key is not a pair with public key in nebula cert: %w", err)
			}
		}

		v1hs, err := v1.MarshalForHandshakes()
		if err != nil {
			return nil, fmt.Errorf("error marshalling v1 certificate for handshake: %w", err)
		}
		ncs, err := newCipherSuite(v1.Curve(), pkcs11backed, cipher, fips140.Enforced())
		if err != nil {
			return nil, err
		}
		cs.v1Cert = v1
		cs.v1Credential = handshake.NewCredential(v1, v1hs, privateKey, ncs)

		if cs.initiatingVersion == 0 {
			cs.initiatingVersion = cert.Version1
		}
	}

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Re-issue the host certificate signed from the existing pki.key (nebula-cert sign -key pki.key ...) so cert and key match
  2. Or restore the pki.key that pairs with the current certificate
  3. Confirm pairing by comparing public keys: nebula-cert print -path pki.cert vs the key's public key

Example fix

// before: cert for host A + key for host B
pki:
  cert: /etc/nebula/hostA.crt
  key:  /etc/nebula/hostB.key
// after: regenerate cert from the key you keep
nebula-cert sign -ca-crt ca.crt -ca-key ca.key -name hostB -ip 10.0.0.2/24 -key /etc/nebula/pki.key -out-crt /etc/nebula/pki.cert
Defensive patterns

Strategy: validation

Validate before calling

// verify key/cert pairing before starting nebula
func keyMatchesCert(certPath, keyPath string) error {
    crtPEM, _ := os.ReadFile(certPath)
    keyPEM, _ := os.ReadFile(keyPath)
    crt, _, err := cert.UnmarshalCertificateFromPEM(certPEM); if err != nil { return err }
    _, _, curve, err := cert.UnmarshalPrivateKeyFromPEM(keyPEM); if err != nil { return err }
    return crt.VerifyPrivateKey(curve, keyPEM)
}

Try / catch

if err := reloadCerts(); err != nil {
    if strings.Contains(err.Error(), "private key is not a pair") {
        log.Fatalf("pki.cert and pki.key do not match; re-issue the certificate for this key: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: newCertState (from newCertStateFromConfig / TestNewFirewallFromConfig): a v1 certificate is present and the loaded pki.key fails VerifyPrivateKey — key and cert belong to different identities.

Common situations: Replaced pki.cert but not pki.key (or vice versa); files from different hosts mixed by config management; wrong key file path pointing to another node's key.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/4e44dce79f47965f. Report an issue: GitHub.