golang/go · critical
crypto/rsa: d does not match dQ
Error message
crypto/rsa: d does not match dQ
What it means
Mirror of the dP check for q: d mod (q-1) must equal the stored dQ. The library recomputes dQ1 = d mod (q-1) and compares; a mismatch means d and the CRT exponent dQ disagree, so the key is internally inconsistent.
Source
Thrown at src/crypto/internal/fips140/rsa/rsa.go:284
// 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")
}
// diff = 0 - p mod q = q - p
diff.ExpandFor(q).Sub(pQ, q)
} else {View on GitHub (pinned to b6b368adc5)
Solutions
- Recompute dQ = d mod (q-1) whenever d changes, or re-import the key atomically.
- Regenerate the key pair.
- Keep d, p, q, dP, dQ, qInv consistent as a unit.
Example fix
// before // d updated, dQ left stale // after qMinus1 := new(big.Int).Sub(q, big.NewInt(1)) dQ = new(big.Int).Mod(d, qMinus1)
Defensive patterns
Strategy: validation
Validate before calling
qMinus1 := new(big.Int).Sub(q, big.NewInt(1))
expected := new(big.Int).Mod(d, qMinus1)
if expected.Cmp(dQ) != 0 {
return errors.New("d mod (q-1) != dQ")
} Type guard
func dMatchesDQ(d, dQ, q *big.Int) bool {
qMinus1 := new(big.Int).Sub(q, big.NewInt(1))
return new(big.Int).Mod(d, qMinus1).Cmp(dQ) == 0
} Try / catch
err := validateKey(priv)
if err != nil && strings.Contains(err.Error(), "d does not match dQ") {
return err // recompute dQ from d, or regenerate
} Prevention
- Recompute dQ whenever d changes.
- Never migrate d between keys without recomputing CRT params.
- Round-trip the whole key through a standard parser.
When it happens
Trigger: Key validation's d/dQ consistency check: dQ1 = d mod (q-1); dQ1.Equal(dQ) != 1. Reached after the d/dP check.
Common situations: d changed without recomputing dQ. d sourced from a different key than q. Partial key migration/merge.
Related errors
- crypto/rsa: invalid CRT exponent
- crypto/rsa: invalid CRT coefficient
- crypto/rsa: d does not match dP
- crypto/rsa: p is even
- crypto/rsa: invalid prime
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/4184438e0a2ee7e2.
Report an issue: GitHub.