golang/go · error
crypto/rsa: missing private exponent
Error message
crypto/rsa: missing private exponent
What it means
Thrown by rsa.PrivateKey.precompute() when priv.D (the private exponent) is nil. precompute() runs lazily during Sign, Decrypt, Validate, or Precompute, so a PrivateKey missing D is unusable for any private operation. The check is an early guard before any math touches D.
Source
Thrown at src/crypto/rsa/rsa.go:588
// We don't have a way to report errors, so just leave Precomputed.fips
// nil. Validate will re-run precompute and report its error.
priv.Precomputed.fips = nil
return
}
priv.Precomputed = precomputed
}
// precompute calculates the PrecomputedValues for priv and returns them.
//
// It does NOT modify priv and is safe for concurrent use.
func (priv *PrivateKey) precompute() (PrecomputedValues, error) {
var precomputed PrecomputedValues
if priv.N == nil {
return precomputed, errors.New("crypto/rsa: missing public modulus")
}
if priv.D == nil {
return precomputed, errors.New("crypto/rsa: missing private exponent")
}
if len(priv.Primes) != 2 {
return priv.precomputeLegacy()
}
if priv.Primes[0] == nil {
return precomputed, errors.New("crypto/rsa: prime P is nil")
}
if priv.Primes[1] == nil {
return precomputed, errors.New("crypto/rsa: prime Q is nil")
}
// If the CRT values are already set, use them.
if priv.Precomputed.Dp != nil && priv.Precomputed.Dq != nil && priv.Precomputed.Qinv != nil {
k, err := rsa.NewPrivateKeyWithPrecomputation(priv.N.Bytes(), priv.E, priv.D.Bytes(),
priv.Primes[0].Bytes(), priv.Primes[1].Bytes(),
priv.Precomputed.Dp.Bytes(), priv.Precomputed.Dq.Bytes(), priv.Precomputed.Qinv.Bytes())
if err != nil {
return precomputed, errView on GitHub (pinned to b6b368adc5)
Solutions
- Verify the source PEM/DER is a private key (BEGIN RSA/RSA PRIVATE KEY or PKCS8) and parse it with the matching x509 parser.
- Call priv.Validate() right after parsing to surface malformation before use.
- Ensure D is populated when constructing keys manually: priv.D must be a non-nil *big.Int.
- If only a public key is available, do not call Sign/Decrypt; use the PublicKey for verification only.
Example fix
// before
block, _ := pem.Decode(data)
priv, _ := x509.ParsePKCS1PublicKey(block.Bytes) // public key only
sig, err := rsa.SignASN1(rand, priv, hash, digest) // D is nil -> error
// after
block, _ := pem.Decode(data)
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil { return err }
if err := priv.Validate(); err != nil { return err }
sig, err := rsa.SignASN1(rand, priv, hash, digest) Defensive patterns
Strategy: validation
Validate before calling
// Validate an RSA private key before any private operation.
func mustRSAPrivate(priv *rsa.PrivateKey) error {
if priv == nil || priv.D == nil {
return errors.New("rsa: private key missing D (public key only?)")
}
return priv.Validate()
}
// usage:
if err := mustRSAPrivate(priv); err != nil { return err } Type guard
func isRSAPrivateKey(v any) bool {
k, ok := v.(*rsa.PrivateKey)
return ok && k != nil && k.D != nil && k.N != nil
} Prevention
- Parse keys with the x509 function matching the PEM block type; never feed a public key into a private-key parser.
- Call priv.Validate() immediately after parsing to catch missing fields before first use.
- When constructing keys manually, populate N, D, E, and Primes together or use rsa.GenerateKey.
- Unit-test key loading with a fixture that has D stripped to ensure your error path triggers.
When it happens
Trigger: Calling SignASN1/Sign, DecryptASN1/Decrypt, Validate(), or Precompute() on an *rsa.PrivateKey whose D field is nil. Typically a key built by hand, parsed from incomplete DER/PEM, or a PublicKey accidentally stored where a PrivateKey is expected.
Common situations: Loading a public-key-only PEM (BEGIN PUBLIC KEY) into an x509.ParsePKCS1PrivateKey path; constructing PrivateKey{PublicKey:...} without setting D; truncated or corrupted key file; marshaling/unmarshaling that dropped D.
Related errors
- crypto/rsa: invalid prime
- crypto/rsa: p * q != n
- crypto/rsa: invalid CRT exponent
- crypto/rsa: invalid CRT coefficient
- crypto/rsa: d does not match dP
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/7053a798eda5c5a1.
Report an issue: GitHub.