golang/go · critical
tls: certificate private key does not implement crypto.Decry
Error message
tls: certificate private key does not implement crypto.Decrypter
What it means
During TLS 1.0–1.2 RSA key exchange, the server's certificate private key does not implement the crypto.Decrypter interface. The RSA key agreement requires decrypting the client's encrypted pre-master secret using priv.Decrypt(). If the private key type (even though it's an RSA key) doesn't implement crypto.Decrypter, decryption cannot proceed. Standard *rsa.PrivateKey implements crypto.Decrypter, so this error implies a custom key type.
Source
Thrown at src/crypto/tls/key_agreement.go:62
type rsaKeyAgreement struct{}
func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
return nil, nil
}
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
if len(ckx.ciphertext) < 2 {
return nil, errClientKeyExchange
}
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 {
return nil, errClientKeyExchange
}
ciphertext := ckx.ciphertext[2:]
priv, ok := cert.PrivateKey.(crypto.Decrypter)
if !ok {
return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
}
// Perform constant time RSA PKCS #1 v1.5 decryption
preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
if err != nil {
return nil, err
}
// We don't check the version number in the premaster secret. For one,
// by checking it, we would leak information about the validity of the
// encrypted pre-master secret. Secondly, it provides only a small
// benefit against a downgrade attack and some implementations send the
// wrong version anyway. See the discussion at the end of section
// 7.4.7.1 of RFC 4346.
return preMasterSecret, nil
}
func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
return errors.New("tls: unexpected ServerKeyExchange")
}View on GitHub (pinned to b6b368adc5)
Solutions
- Use a standard *rsa.PrivateKey loaded via tls.LoadX509KeyPair or x509.ParsePKCS1PrivateKey.
- If using a custom key type, implement crypto.Decrypter (Decrypt method) on it.
- For HSM keys: ensure the HSM/PKCS#11 provider supports RSA decryption and exposes it via crypto.Decrypter.
- Switch to an ECDHE cipher suite that only requires signing (crypto.Signer), not decryption.
- Provide the key via a tls.Certificate where PrivateKey fully implements crypto.Decrypter.
Example fix
// before: custom key type only implements crypto.Signer
cert := tls.Certificate{
PrivateKey: myCustomSignerOnlyKey,
}
// after: implement crypto.Decrypter or use standard key
cert := tls.Certificate{
PrivateKey: parsedRSAKey, // *rsa.PrivateKey implements crypto.Decrypter
}
// Or implement Decrypt on your custom type:
// func (k *CustomKey) Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) ([]byte, error) { ... } Defensive patterns
Strategy: type-guard
Validate before calling
// Verify the private key implements crypto.Decrypter before starting the server
func verifyDecrypter(cert *tls.Certificate) error {
_, ok := cert.PrivateKey.(crypto.Decrypter)
if !ok {
return errors.New("private key must implement crypto.Decrypter for RSA key exchange")
}
return nil
} Type guard
func isDecrypter(key any) bool {
_, ok := key.(crypto.Decrypter)
return ok
} Try / catch
// Check at server startup
if err := verifyDecrypter(&cert); err != nil {
log.Fatal("certificate key not suitable for RSA key exchange: ", err)
}
// Or handle at handshake time:
if err := conn.Handshake(); err != nil {
if strings.Contains(err.Error(), "crypto.Decrypter") {
log.Printf("key type issue: %v", err)
}
} Prevention
- Use standard *rsa.PrivateKey from tls.LoadX509KeyPair for RSA cipher suites.
- If using custom key types, implement crypto.Decrypter.
- Prefer ECDHE cipher suites that only require crypto.Signer.
- Verify key capabilities at startup, not at handshake time.
When it happens
Trigger: Server calls rsaKeyAgreement.processClientKeyExchange and attempts cert.PrivateKey.(crypto.Decrypter). The type assertion fails because the private key is a custom type that wraps an RSA key but doesn't expose the Decrypt method, or the key is stored in a way (e.g. some HSM integrations) that only implements crypto.Signer but not crypto.Decrypter.
Common situations: Custom private key type (e.g. PKCS#11 wrapper, cloud KMS proxy) that implements crypto.Signer but not crypto.Decrypter; an HSM-backed key that only supports signing; a key loaded from an unusual source that doesn't fully implement the standard interfaces.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- tls: server certificate contains incorrect key type for sele
- tls: failed to sign handshake: %s
- tls: invalid ClientKeyExchange message
- tls: unexpected ServerKeyExchange
- tls: no supported elliptic curves offered
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/001c02836b8c6ed2.
Report an issue: GitHub.