Perform OpenSSH private key PEM encoding in Rust (#15056)
Move the final PEM wrapping (base64 encoding and BEGIN/END markers) of
OpenSSH-format private keys out of Python and into Rust via the existing
encode_der_data helper. The Python serializer now returns the raw OpenSSH key
bytes. As a side effect the base64 body is now wrapped at 64 characters per
line instead of 76.
Fixes #15055
Claude-Session: https://claude.ai/code/session_019igBue8PBCWCi5PD6FqpNt
Co-authored-by: Claude <noreply@anthropic.com>
diff --git a/src/cryptography/hazmat/primitives/serialization/ssh.py b/src/cryptography/hazmat/primitives/serialization/ssh.py
index 411113b..c30f937 100644
--- a/src/cryptography/hazmat/primitives/serialization/ssh.py
+++ b/src/cryptography/hazmat/primitives/serialization/ssh.py
@@ -10,7 +10,6 @@
import re
import typing
import warnings
-from base64 import encodebytes as _base64_encode
from dataclasses import dataclass
from cryptography import utils
@@ -168,14 +167,6 @@
return _ECDSA_KEY_TYPE[curve.name]
-def _ssh_pem_encode(
- data: utils.Buffer,
- prefix: bytes = _SK_START + b"\n",
- suffix: bytes = _SK_END + b"\n",
-) -> bytes:
- return b"".join([prefix, _base64_encode(data), suffix])
-
-
def _check_block_size(data: utils.Buffer, block_len: int) -> None:
"""Require data to be full blocks"""
if not data or len(data) % block_len != 0:
@@ -869,7 +860,7 @@
if ciph is not None:
ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:])
- return _ssh_pem_encode(buf[:mlen])
+ return bytes(buf[:mlen])
SSHPublicKeyTypes = typing.Union[
diff --git a/src/rust/src/backend/utils.rs b/src/rust/src/backend/utils.rs
index 8f3909d..dfa7a43 100644
--- a/src/rust/src/backend/utils.rs
+++ b/src/rust/src/backend/utils.rs
@@ -191,10 +191,16 @@
// OpenSSH + PEM
if openssh_allowed && format == PrivateFormat::OpenSSH {
if encoding == Encoding::PEM {
- return Ok(types::SERIALIZE_SSH_PRIVATE_KEY
+ let raw_bytes = types::SERIALIZE_SSH_PRIVATE_KEY
.get(py)?
.call1((key_obj, password, encryption_algorithm))?
- .extract()?);
+ .extract()?;
+ return crate::asn1::encode_der_data(
+ py,
+ "OPENSSH PRIVATE KEY".to_string(),
+ raw_bytes,
+ encoding,
+ );
}
return Err(CryptographyError::from(
diff --git a/tests/hazmat/primitives/test_ssh.py b/tests/hazmat/primitives/test_ssh.py
index d9d3e4a..64eb2f8 100644
--- a/tests/hazmat/primitives/test_ssh.py
+++ b/tests/hazmat/primitives/test_ssh.py
@@ -444,7 +444,7 @@
main.put_sshstr(secret)
res = main.tobytes()
- return ssh._ssh_pem_encode(res[:cut], header, footer)
+ return header + base64.encodebytes(res[:cut]) + footer
def test_ssh_make_file(self, backend):
# check if works by default