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, err

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Verify the source PEM/DER is a private key (BEGIN RSA/RSA PRIVATE KEY or PKCS8) and parse it with the matching x509 parser.
  2. Call priv.Validate() right after parsing to surface malformation before use.
  3. Ensure D is populated when constructing keys manually: priv.D must be a non-nil *big.Int.
  4. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/7053a798eda5c5a1. Report an issue: GitHub.