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