检查 SSH 私钥是否加密

5 c macos ssh swift

在 macOS 上生成的密钥对ssh-keygen可以具有不同的格式。

  • 标准 PEM ASN.1 对象可由 macOS 的SecKeyAPI读取
  • 带有文本标题的 PEM
  • OpenSSH 密钥
  • OpenSSH 加密密钥

OpenSSH/BSD在这里使用这种非标准化格式。

现在我只需要能够检查私钥是否设置了密码短语,这样我就可以提示用户输入它,而不必处理不同密钥格式的复杂性。

在 macOS 上有没有一种通过 Swift 或 C API 快速检查密钥是否设置了密码的方法?

小智 0

我对 2 种最常见的格式实施了自己的 OpenSSH 检查

  • 其一,我正在检查 Linux 风格 SSH PEM 的 DEK-Info 的 PEM 标头
  • 对于 OpenSSH 样式密钥,我使用下面的类手动解析格式
import Foundation

private let opensshMagic = "openssh-key-v1"

public class SSHPrivateKey {

    public struct OpenSSHKey {
        let cipherName: String
        let kdfName: String
        let kdfOptions: Data
        let numKeys: Int

        var isEncrypted: Bool {
            return cipherName != "none"
        }
    }

    public let data: Data

    public init(data: Data) {
        self.data = data
    }

    public func openSSHKey() -> OpenSSHKey? {
        // #define AUTH_MAGIC      "openssh-key-v1"
        //
        // byte[]  AUTH_MAGIC
        // string  ciphername
        // string  kdfname
        // string  kdfoptions
        // int     number of keys N
        // string  publickey1
        // string  publickey2
        // ...
        // string  publickeyN
        // string  encrypted, padded list of private keys

        guard let magic = opensshMagic.data(using: .utf8) else {
            return nil
        }

        if data.prefix(magic.count) != magic {
            return nil
        }

        var offset = magic.count + 1

        guard let cipherName = data.consumeString(offset: &offset),
            let kdfName = data.consumeString(offset: &offset) else {
                return nil
        }

        let kdfOptions = data.consumeBytes(offset: &offset)
        let numKeys = data.consumeUInt32(offset: &offset)

        return OpenSSHKey(cipherName: cipherName,
                          kdfName: kdfName,
                          kdfOptions: kdfOptions,
                          numKeys: Int(numKeys))
    }
}

private extension Data {

    func consumeBytes(offset: inout Int) -> Data {
        let n = Int(consumeUInt32(offset: &offset))
        let b = Data(self[offset..<offset + n])
        offset += n
        return b
    }

    func consumeString(offset: inout Int) -> String? {
        return consumeBytes(offset: &offset).utf8String
    }

    func consumeUInt8(offset: inout Int) -> UInt8 {
        let v = self[offset] & 0xFF
        offset += 1

        return v
    }

    func consumeUInt32(offset: inout Int) -> UInt32 {
        var i: UInt32 = 0

        i = (i << 8) | UInt32(consumeUInt8(offset: &offset))
        i = (i << 8) | UInt32(consumeUInt8(offset: &offset))
        i = (i << 8) | UInt32(consumeUInt8(offset: &offset))
        i = (i << 8) | UInt32(consumeUInt8(offset: &offset))

        return i
    }
}

Run Code Online (Sandbox Code Playgroud)