golang/go · error

crypto/rsa: input must be hashed message

Error message

crypto/rsa: input must be hashed message

What it means

Thrown by SignPKCS1v15 when hash is non-zero and len(hashed) != hash.Size(). PKCS#1 v1.5 signing requires the input to be exactly the digest of the named hash function; a mismatched length means the caller passed the wrong data (e.g. the raw message instead of its hash, or a different hash's output). This is basic input validation, not FIPS-specific.

Source

Thrown at src/crypto/rsa/fips.go:339

}

// SignPKCS1v15 calculates the signature of hashed using
// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS #1 v1.5.  Note that hashed must
// be the result of hashing the input message using the given hash
// function. If hash is zero, hashed is signed directly. This isn't
// advisable except for interoperability.
//
// The random parameter is legacy and ignored, and it can be nil.
//
// This function is deterministic. Thus, if the set of possible
// messages is small, an attacker may be able to build a map from
// messages to signatures and identify the signed messages. As ever,
// signatures provide authenticity, not confidentiality.
func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
	var hashName string
	if hash != crypto.Hash(0) {
		if len(hashed) != hash.Size() {
			return nil, errors.New("crypto/rsa: input must be hashed message")
		}
		hashName = hash.String()
	}

	if err := checkPublicKeySize(&priv.PublicKey); err != nil {
		return nil, err
	}

	if boring.Enabled && priv.N.BitLen() >= 1024 {
		bkey, err := boringPrivateKey(priv)
		if err != nil {
			return nil, err
		}
		return boring.SignRSAPKCS1v15(bkey, hash, hashed)
	}

	if err := checkFIPS140OnlyPrivateKey(priv); err != nil {
		return nil, err

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Hash first: h := sha256.Sum256(msg); then pass h[:] with crypto.SHA256.
  2. Ensure the hash constant passed as 'hash' matches the function that produced 'hashed'.
  3. If hash == 0 (sign pre-hashed/raw), pass the data directly — but only when you intentionally bypass the digest requirement.

Example fix

// before
sig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, message)

// after
digest := sha256.Sum256(message)
sig, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, digest[:])
Defensive patterns

Strategy: validation

Validate before calling

if hash != 0 && len(hashed) != hash.Size() {
    return fmt.Errorf("hashed input is %d bytes, expected %d for %s", len(hashed), hash.Size(), hash)
}
sig, err := rsa.SignPKCS1v15(rand.Reader, priv, hash, hashed)

Type guard

func digestMatchesHash(hash crypto.Hash, hashed []byte) bool {
    return hash == 0 || len(hashed) == hash.Size()
}

Prevention

When it happens

Trigger: Calling rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, rawMessageBytes) — passing the plaintext instead of sha256.Sum256(...). Passing a SHA-512 digest (64 bytes) with crypto.SHA256 (expects 32 bytes). Passing a truncated or padded hash slice.

Common situations: Forgetting to hash the message first. Mismatch between the hash used to produce 'hashed' and the 'hash' argument. Off-by-slice (passing a 33-byte slice that includes a length prefix).

Related errors


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