golang/go · critical
crypto/rsa: d does not match dP
Error message
crypto/rsa: d does not match dP
What it means
Thrown when d mod (p-1) does not equal the stored dP. The library recomputes the expected dP from d and compares; a mismatch means d and the CRT exponent dP disagree, so the key is internally inconsistent (even though dP may satisfy e*dP ≡ 1 mod p-1 on its own).
Source
Thrown at src/crypto/internal/fips140/rsa/rsa.go:280
if de.IsOne() != 1 {
return errors.New("crypto/rsa: invalid CRT exponent")
}
// Check that qInv * q ≡ 1 mod p.
qP, err := bigmod.NewNat().SetOverflowingBytes(q.Nat().Bytes(q), p)
if err != nil {
// q >= 2^⌈log2(p)⌉
qP = bigmod.NewNat().Mod(q.Nat(), p)
}
if qP.Mul(priv.qInv, p).IsOne() != 1 {
return errors.New("crypto/rsa: invalid CRT coefficient")
}
// Check d against dP and dQ, even though we never actually use d,
// to make sure the key is consistent.
dP1 := bigmod.NewNat().Mod(priv.d, pMinus1)
if dP1.Equal(dP) != 1 {
return errors.New("crypto/rsa: d does not match dP")
}
dQ1 := bigmod.NewNat().Mod(priv.d, qMinus1)
if dQ1.Equal(dQ) != 1 {
return errors.New("crypto/rsa: d does not match dQ")
}
// Check that |p - q| > 2^(nlen/2 - 100).
//
// If p and q are very close to each other, then N=pq can be trivially
// factored using Fermat's factorization method. Broken RSA implementations
// do generate such keys. See Hanno Böck, Fermat Factorization in the Wild,
// https://eprint.iacr.org/2023/026.pdf.
diff := bigmod.NewNat()
if qP, err := bigmod.NewNat().SetBytes(q.Nat().Bytes(q), p); err != nil {
// q > p
pQ, err := bigmod.NewNat().SetBytes(p.Nat().Bytes(p), q)
if err != nil {
return errors.New("crypto/rsa: p == q")View on GitHub (pinned to b6b368adc5)
Solutions
- Recompute dP = d mod (p-1) (and dQ = d mod (q-1)) whenever d changes, or re-import the whole key.
- Regenerate the key pair.
- Treat d, p, q, dP, dQ, qInv as atomic — never edit one without the others.
Example fix
// before // d updated, dP left stale // after pMinus1 := new(big.Int).Sub(p, big.NewInt(1)) dP = new(big.Int).Mod(d, pMinus1)
Defensive patterns
Strategy: validation
Validate before calling
pMinus1 := new(big.Int).Sub(p, big.NewInt(1))
expected := new(big.Int).Mod(d, pMinus1)
if expected.Cmp(dP) != 0 {
return errors.New("d mod (p-1) != dP")
} Type guard
func dMatchesDP(d, dP, p *big.Int) bool {
pMinus1 := new(big.Int).Sub(p, big.NewInt(1))
return new(big.Int).Mod(d, pMinus1).Cmp(dP) == 0
} Try / catch
err := validateKey(priv)
if err != nil && strings.Contains(err.Error(), "d does not match dP") {
return err // recompute dP from d, or regenerate
} Prevention
- Treat d, p, q, dP, dQ, qInv as one atomic unit.
- Recompute dP and dQ whenever d changes.
- Re-import the whole key rather than editing d alone.
When it happens
Trigger: Key validation's final consistency check: dP1 = d mod (p-1); dP1.Equal(dP) != 1. Reached after all prior CRT checks passed.
Common situations: d replaced/edited without recomputing dP. Key assembled from a d belonging to a different key than p. A partially-redacted or migrated key.
Related errors
- crypto/rsa: invalid CRT exponent
- crypto/rsa: invalid CRT coefficient
- crypto/rsa: d does not match dQ
- crypto/rsa: p is even
- crypto/rsa: invalid prime
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/2745bc9afbafb9a5.
Report an issue: GitHub.