"""Optional RSA key loading. Non-JWT authentication does not need cryptography."""
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
[docs]
def load_private_key(
private_key: bytes | str | RSAPrivateKey,
passphrase: bytes | str | None = None,
) -> RSAPrivateKey:
"""Load PEM/OpenSSH RSA material, or validate an already loaded RSA key.
The decrypted key stays in memory; no files are read or written. Exceptions
exclude key/passphrase values. At least 2048 bits are required.
"""
try:
from cryptography.exceptions import UnsupportedAlgorithm
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
except ImportError:
raise ImportError("RSA JWT authentication requires sf-toolkit[jwt]") from None
if isinstance(private_key, RSAPrivateKey):
if passphrase is not None:
raise ValueError("A loaded private key does not take a passphrase")
key = private_key
else:
if not isinstance(private_key, (str, bytes)) or not isinstance(
passphrase, (str, bytes, type(None))
):
raise ValueError("Invalid RSA private key or passphrase")
raw = private_key.encode() if isinstance(private_key, str) else private_key
raw = raw.strip()
password = passphrase.encode() if isinstance(passphrase, str) else passphrase
loader = (
serialization.load_ssh_private_key
if raw.startswith(b"-----BEGIN OPENSSH PRIVATE KEY-----")
else serialization.load_pem_private_key
)
try:
key = loader(raw, password=password)
except (ValueError, TypeError, UnsupportedAlgorithm):
raise ValueError("Invalid RSA private key or passphrase") from None
if not isinstance(key, RSAPrivateKey):
raise ValueError("Salesforce JWT authentication requires an RSA private key")
if key.key_size < 2048:
raise ValueError("Salesforce JWT RSA keys must be at least 2048 bits")
return key