golang/go · critical
crypto/rsa: invalid CRT exponent
Error message
crypto/rsa: invalid CRT exponent
What it means
Thrown when SetBytes rejects priv.dP into the (p-1) modulus, meaning dP >= p-1 or its byte length is wrong. dP is d mod (p-1), a CRT exponent, and must be a valid residue in [0, p-1). An out-of-range dP means the stored CRT params are inconsistent with p.
Source
Thrown at src/crypto/internal/fips140/rsa/rsa.go:243
if pN.Mul(qN, N).IsZero() != 1 {
return errors.New("crypto/rsa: p * q != n")
}
// Check that de ≡ 1 mod p-1, and de ≡ 1 mod q-1.
//
// This implies that e is coprime to each p-1 as e has a multiplicative
// inverse. Therefore e is coprime to lcm(p-1,q-1) = λ(N).
// It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1 mod p. Thus a^de ≡ a
// mod n for all a coprime to n, as required.
//
// This checks dP, dQ, and e.
pMinus1, err := bigmod.NewModulus(p.Nat().SubOne(p).Bytes(p))
if err != nil {
return errors.New("crypto/rsa: invalid prime")
}
dP, err := bigmod.NewNat().SetBytes(priv.dP, pMinus1)
if err != nil {
return errors.New("crypto/rsa: invalid CRT exponent")
}
de := bigmod.NewNat()
de.SetUint(uint(priv.pub.E)).ExpandFor(pMinus1)
de.Mul(dP, pMinus1)
if de.IsOne() != 1 {
return errors.New("crypto/rsa: invalid CRT exponent")
}
qMinus1, err := bigmod.NewModulus(q.Nat().SubOne(q).Bytes(q))
if err != nil {
return errors.New("crypto/rsa: invalid prime")
}
dQ, err := bigmod.NewNat().SetBytes(priv.dQ, qMinus1)
if err != nil {
return errors.New("crypto/rsa: invalid CRT exponent")
}
de.SetUint(uint(priv.pub.E)).ExpandFor(qMinus1)
de.Mul(dQ, qMinus1)View on GitHub (pinned to b6b368adc5)
Solutions
- Recompute dP = d mod (p-1) from the true d and p and overwrite the stored value, or re-import the full key.
- Regenerate the key pair entirely.
- Use a standard parser that round-trips all CRT fields atomically.
Example fix
// before // dP loaded independently and inconsistent with p // after pMinus1 := new(big.Int).Sub(p, big.NewInt(1)) dP := new(big.Int).Mod(d, pMinus1) // store dP alongside the matching p
Defensive patterns
Strategy: validation
Validate before calling
pMinus1 := new(big.Int).Sub(p, big.NewInt(1))
if new(big.Int).Mod(dP, pMinus1).Cmp(dP) != 0 || dP.Cmp(pMinus1) >= 0 {
return errors.New("dP must be a valid residue mod (p-1)")
} Type guard
func dPValidForP(dP, p *big.Int) bool {
pMinus1 := new(big.Int).Sub(p, big.NewInt(1))
return dP.Cmp(pMinus1) < 0
} Try / catch
err := validateKey(priv)
if err != nil && strings.Contains(err.Error(), "invalid CRT exponent") {
// recompute dP from d and p, or regenerate
return err
} Prevention
- Recompute dP = d mod (p-1) whenever p or d changes.
- Keep all CRT fields atomic with the primes they were derived from.
- Round-trip keys through standard PKCS#1 serialization.
When it happens
Trigger: Key validation's CRT exponent check: dP does not fit modulus (p-1). Reached after the (p-1) modulus was successfully built.
Common situations: dP computed against a different p than the one stored (mismatched CRT params). dP byte slice truncated or extended during serialization. Key assembled from parts of different keys.
Related errors
- crypto/rsa: invalid CRT coefficient
- crypto/rsa: d does not match dP
- 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/f78cff5a7886f18f.
Report an issue: GitHub.