Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Export OpenSSH Public Key #132

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions SwiftyRSA.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
48461D06FA57F540A6140BC66F457877 /* SwiftyRSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E84F551B29670A456D12366AA4C8E5 /* SwiftyRSA.swift */; };
54EC57310DAFFE47CAA52CAED8F387B9 /* SwiftyRSA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E84F551B29670A456D12366AA4C8E5 /* SwiftyRSA.swift */; };
61C3EEC5859298BF282B0BA572B8FCAC /* NSData+SHA.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AAC360BC9A1D1773D59C8E91186044E /* NSData+SHA.h */; settings = {ATTRIBUTES = (Public, ); }; };
6C24D5A1214B2FD7004C13F3 /* swiftyrsa-public.pub in Resources */ = {isa = PBXBuildFile; fileRef = 6C24D5A0214B2FD7004C13F3 /* swiftyrsa-public.pub */; };
8E6B4DE19F56FBA2DEEE318C4EEEA1C6 /* NSData+SHA.m in Sources */ = {isa = PBXBuildFile; fileRef = D77B6899031A54A9F698D3430A599421 /* NSData+SHA.m */; };
A535C69BBF632C0A67A6DD222B7A4760 /* NSData+SHA.h in Headers */ = {isa = PBXBuildFile; fileRef = 5AAC360BC9A1D1773D59C8E91186044E /* NSData+SHA.h */; settings = {ATTRIBUTES = (Public, ); }; };
B0981AE770653C8FE25311592E2BCE5E /* SwiftyRSA.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CB44E2E8BE22C702325440A0F96410A /* SwiftyRSA.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -90,6 +91,7 @@
38E84F551B29670A456D12366AA4C8E5 /* SwiftyRSA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyRSA.swift; sourceTree = "<group>"; };
47807FD4AF81104758D4EA9B778F5186 /* multiple-keys-testcase.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "multiple-keys-testcase.sh"; sourceTree = "<group>"; };
5AAC360BC9A1D1773D59C8E91186044E /* NSData+SHA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+SHA.h"; sourceTree = "<group>"; };
6C24D5A0214B2FD7004C13F3 /* swiftyrsa-public.pub */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "swiftyrsa-public.pub"; sourceTree = "<group>"; };
6E2D7A1B9C4D58D0887FB023C0E15594 /* swiftyrsa-private-headerless.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "swiftyrsa-private-headerless.pem"; sourceTree = "<group>"; };
7DF0A3222330CDFCF12918ED08519EB9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
986B7C763D1B85C628621E89E7071B06 /* swiftyrsa-public.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "swiftyrsa-public.der"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -230,6 +232,7 @@
D240C55056247A7B270C92143EF30532 /* keys */ = {
isa = PBXGroup;
children = (
6C24D5A0214B2FD7004C13F3 /* swiftyrsa-public.pub */,
C04D33801DC54D5700D65B05 /* swiftyrsa-public-base64-newlines.txt */,
C065ED6E1DBF314500674763 /* swiftyrsa-private.der */,
C096A0111D9085C100E285C2 /* swiftyrsa-public-base64.txt */,
Expand Down Expand Up @@ -451,6 +454,7 @@
C04D33811DC54D5700D65B05 /* swiftyrsa-public-base64-newlines.txt in Resources */,
C084A72A1EC3034B003F79ED /* swiftyrsa-private-header-octetstring.pem in Resources */,
C076F5541DADEC1D006AF5DB /* swiftyrsa-public.der in Resources */,
6C24D5A1214B2FD7004C13F3 /* swiftyrsa-public.pub in Resources */,
C076F5551DADEC1D006AF5DB /* swiftyrsa-public.pem in Resources */,
C065ED6F1DBF314500674763 /* swiftyrsa-private.der in Resources */,
);
Expand Down
59 changes: 59 additions & 0 deletions SwiftyRSA/PublicKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,65 @@ public class PublicKey: Key {
return pem
}

/// Returns an OpenSSH representation of the public key.
///
/// - Returns: Data of the key, OpenSSH encoded
/// - Throws: SwiftyRSAError
public func sshString() throws -> String{
let data = try self.data()
let node = try! Asn1Parser.parse(data: data)

// Ensure the raw data is an ASN1 sequence
guard case .sequence(let nodes) = node else {
throw SwiftyRSAError.invalidAsn1Structure
}

let RSA_HEADER = "ssh-rsa"

var ssh:String = RSA_HEADER + " "
var rsaBytes:Data = Data()

// Get size of the header
var byteCount: UInt32 = UInt32(RSA_HEADER.count).bigEndian
var sizeData = Data(bytes: &byteCount, count: MemoryLayout.size(ofValue: byteCount))

// Append size of header and content of header
rsaBytes.append(sizeData)
rsaBytes += RSA_HEADER.data(using: .utf8)!

// Get the exponent
if let exp = nodes.last, case .integer(let exponent) = exp {
// Get size of exponent
byteCount = UInt32(exponent.count).bigEndian
sizeData = Data(bytes: &byteCount, count: MemoryLayout.size(ofValue: byteCount))

// Append size of exponent and content of exponent
rsaBytes.append(sizeData)
rsaBytes += exponent
}
else{
throw SwiftyRSAError.invalidAsn1Structure
}

// Get the modulus
if let mod = nodes.first, case .integer(let modulus) = mod {
// Get size of modulus
byteCount = UInt32(modulus.count).bigEndian
sizeData = Data(bytes: &byteCount, count: MemoryLayout.size(ofValue: byteCount))

// Append size of modulus and content of modulus
rsaBytes.append(sizeData)
rsaBytes += modulus
}
else{
throw SwiftyRSAError.invalidAsn1Structure
}

ssh += rsaBytes.base64EncodedString() + "\n"
return ssh

}

/// Creates a public key with a keychain key reference.
/// This initializer will throw if the provided key reference is not a public RSA key.
///
Expand Down
12 changes: 12 additions & 0 deletions SwiftyRSATests/KeyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,18 @@ class PublicKeyTests: XCTestCase {
XCTAssertNotNil(newPublicKey)
XCTAssertEqual(try? publicKey.data(), try? newPublicKey.data())
}

func test_sshString() throws {
let publicKey = try PublicKey(pemNamed: "swiftyrsa-public", in: bundle)
let sshString = try publicKey.sshString()

guard let path = bundle.path(forResource: "swiftyrsa-public", ofType: "pub") else {
return XCTFail()
}

let expectedKeyString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
XCTAssertEqual(sshString, expectedKeyString)
}
}

class PrivateKeyTests: XCTestCase {
Expand Down
1 change: 1 addition & 0 deletions SwiftyRSATests/keys/swiftyrsa-public.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDQZ0YVolLtPXXSowc6CopEXzGI/Tvri6hYT3KZ45G97DQn8ocydBQXSZfRR92MpiZHQn1zydpVBOCj7tUnSh1QoSN9aISG+tuLggYWdav6XlW2JAlduHkMbbyug9aoWIyaZjXXzyV+0e3hjwQhfTeQj9DLuGssWNX3YuYgf/e5LQ==