From b28ff410c9e92f3d0314daf513468742cff7727f Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sun, 24 Aug 2025 13:27:47 -0700 Subject: [PATCH] Fix rsa public key gen --- .../SecretKit/OpenSSH/OpenSSHKeyWriter.swift | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift b/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift index 776c732..3bc74a6 100644 --- a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift +++ b/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHKeyWriter.swift @@ -11,13 +11,17 @@ public struct OpenSSHKeyWriter: Sendable { /// Generates an OpenSSH data payload identifying the secret. /// - Returns: OpenSSH data payload identifying the secret. public func data(secret: SecretType) -> Data { - if secret.keyType.algorithm == .ecdsa { + switch secret.keyType.algorithm { + case .ecdsa: lengthAndData(of: Data(curveType(for: secret.keyType).utf8)) + lengthAndData(of: Data(curveIdentifier(for: secret.keyType).utf8)) + lengthAndData(of: secret.publicKey) - } else { + case .mldsa: lengthAndData(of: Data(curveType(for: secret.keyType).utf8)) + lengthAndData(of: secret.publicKey) + case .rsa: + lengthAndData(of: Data(curveType(for: secret.keyType).utf8)) + + rsa(secret: secret) } } @@ -83,9 +87,7 @@ extension OpenSSHKeyWriter { case (.mldsa, 65), (.mldsa, 87): "ssh-mldsa" + String(describing: keyType.size) case (.rsa, _): - // All RSA keys use the same 512 bit hash function, per - // https://security.stackexchange.com/questions/255074/why-are-rsa-sha2-512-and-rsa-sha2-256-supported-but-not-reported-by-ssh-q-key - "rsa-sha2-512" + "ssh-rsa" default: "unknown" } @@ -108,4 +110,18 @@ extension OpenSSHKeyWriter { } } + public func rsa(secret: SecretType) -> Data { + // Cheap way to pull out e and n as defined in https://datatracker.ietf.org/doc/html/rfc4253 + // Keychain stores it as a thin ASN.1 wrapper with this format: + // [4 byte prefix][2 byte prefix][n][2 byte prefix][e] + // Rather than parse out the whole ASN.1 blob, we know how this should be formatted, so pull values directly. + let keySize = secret.keyType.size + guard secret.keyType.algorithm == .rsa && (keySize == 1024 || keySize == 2048) else { fatalError() } + let length = secret.keyType.size/8 + let data = secret.publicKey + let n = Data(data[8..<(9+length)]) + let e = Data(data[(2+9+length)...]) + return lengthAndData(of: e) + lengthAndData(of: n) + } + }