Skip to content

Commit 03134f2

Browse files
authored
Serialise SSH Public Key (#127)
Motivation: Needed a function function that takes a NIOSSHPublicKey and turns it into OpenSSH public key string
1 parent f8b20b2 commit 03134f2

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

Sources/NIOSSH/Keys And Signatures/NIOSSHPublicKey.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,14 @@ extension ByteBuffer {
443443
return returnValue
444444
}
445445
}
446+
447+
extension String {
448+
/// Takes a NIOSSHPublicKey and turns it into OpenSSH public key string in the format of "algorithm-id base64-encoded-key"
449+
public init(openSSHPublicKey: NIOSSHPublicKey) {
450+
var buffer = ByteBuffer()
451+
buffer.writeSSHHostKey(openSSHPublicKey)
452+
let next = Data(buffer.readableBytesView).base64EncodedString()
453+
let publicKeyString = String(openSSHPublicKey.keyPrefix) + " " + next
454+
self = publicKeyString
455+
}
456+
}

Tests/NIOSSHTests/CertifiedKeyTests.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import XCTest
2121
private enum Fixtures {
2222
// A P384 certificate authority key generated by ssh-keygen
2323
static let caPublicKey = "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBHYlMSXacXt13oBLpMXEP0OSMw5okd5c7G3hoim1MR/THUOyOS2AVQKEqLZs+td3Y6yYCrq5TGWDNGY2dfKFX99nLqJCq2kxR//CP3UherkZnn6u4eW4biLL7xODqNOzkQ== [email protected]"
24+
static let caPublicKeyExport = "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBHYlMSXacXt13oBLpMXEP0OSMw5okd5c7G3hoim1MR/THUOyOS2AVQKEqLZs+td3Y6yYCrq5TGWDNGY2dfKFX99nLqJCq2kxR//CP3UherkZnn6u4eW4biLL7xODqNOzkQ=="
2425
static let caPrivateKey = """
2526
-----BEGIN OPENSSH PRIVATE KEY-----
2627
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
@@ -36,22 +37,34 @@ private enum Fixtures {
3637

3738
static let p256UserBase = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIZS1APJofiPeoATC/VC4kKi7xRPdz934nSkFLTc0whYi3A8hEKHAOX9edgL1UWxRqRGQZq2wvvAIjAO9kCeiQA= [email protected]"
3839

40+
static let p256UserBaseExport = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIZS1APJofiPeoATC/VC4kKi7xRPdz934nSkFLTc0whYi3A8hEKHAOX9edgL1UWxRqRGQZq2wvvAIjAO9kCeiQA="
41+
3942
// A P256 user key. id "User P256 key" serial 0 for foo,bar valid from 2020-06-03T17:50:15 to 2070-04-02T17:51:15
4043
// Generated using ssh-keygen -s ca-key -I "User P256 key" -n "foo,bar" -V "-1m:+2600w" user-p256
4144
static let p256User = "[email protected] AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg3JSGtQjaK4FLif7Gx2ftKiYSCdOTFMO+W6UJvmnIu4AAAAAIbmlzdHAyNTYAAABBBIZS1APJofiPeoATC/VC4kKi7xRPdz934nSkFLTc0whYi3A8hEKHAOX9edgL1UWxRqRGQZq2wvvAIjAO9kCeiQAAAAAAAAAAAAAAAAEAAAANVXNlciBQMjU2IGtleQAAAA4AAAADZm9vAAAAA2JhcgAAAABe19THAAAAALyR+QMAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAiAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQR2JTEl2nF7dd6AS6TFxD9DkjMOaJHeXOxt4aIptTEf0x1DsjktgFUChKi2bPrXd2OsmAq6uUxlgzRmNnXyhV/fZy6iQqtpMUf/wj91IXq5GZ5+ruHluG4iy+8Tg6jTs5EAAACEAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMQD1SlsXyALkWyb5fsNEVNhFA7yZ9PV6KrhT6hcUc7WiscqnfTWmlr84dsydmElTXEEAAAAwAmb+JbJ/Bo+3ywz5fQRUoEbNFo9NsQRwLofyrVTdWPv+IMDtMyX/W/SuEjjr8XD4 [email protected]"
4245

46+
static let p256UserExport = "[email protected] AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg3JSGtQjaK4FLif7Gx2ftKiYSCdOTFMO+W6UJvmnIu4AAAAAIbmlzdHAyNTYAAABBBIZS1APJofiPeoATC/VC4kKi7xRPdz934nSkFLTc0whYi3A8hEKHAOX9edgL1UWxRqRGQZq2wvvAIjAO9kCeiQAAAAAAAAAAAAAAAAEAAAANVXNlciBQMjU2IGtleQAAAA4AAAADZm9vAAAAA2JhcgAAAABe19THAAAAALyR+QMAAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAiAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQR2JTEl2nF7dd6AS6TFxD9DkjMOaJHeXOxt4aIptTEf0x1DsjktgFUChKi2bPrXd2OsmAq6uUxlgzRmNnXyhV/fZy6iQqtpMUf/wj91IXq5GZ5+ruHluG4iy+8Tg6jTs5EAAACEAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMQD1SlsXyALkWyb5fsNEVNhFA7yZ9PV6KrhT6hcUc7WiscqnfTWmlr84dsydmElTXEEAAAAwAmb+JbJ/Bo+3ywz5fQRUoEbNFo9NsQRwLofyrVTdWPv+IMDtMyX/W/SuEjjr8XD4"
47+
4348
static let p384HostBase = "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJPOgAXHijSxoZBiyhSDOR3eUELUoc+hqh/SY1Wq4/562jThf6Q+tjVzZTMWZMAP4S6DD2qZswsRvisxXkcZDOw5bvyk0WmezYvjUP6TZII/0BDVTotCf4SxukEtcqBZqg== [email protected]"
4449

50+
static let p384HostBaseExport = "ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJPOgAXHijSxoZBiyhSDOR3eUELUoc+hqh/SY1Wq4/562jThf6Q+tjVzZTMWZMAP4S6DD2qZswsRvisxXkcZDOw5bvyk0WmezYvjUP6TZII/0BDVTotCf4SxukEtcqBZqg=="
51+
4552
// A P384 host key. id "Host P384 key" serial 543 for localhost,example.com valid from 2020-06-03T17:55:53 to 2038-01-19T03:14:07.
4653
// Generated using ssh-keygen -s ca-key -I "Host P384 key" -h -n "localhost,example.com" -V "-1m:+2600w" -z 543 -O "critical:cats=dogs" -O "extension:lens=wide" -O "extension:size=full-frame" host-p384
4754
static let p384Host = "[email protected] AAAAKGVjZHNhLXNoYTItbmlzdHAzODQtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgvD8+H64ZEuPHwYIxuym9XHVpiJEoCvCqyy8Ch7JAZEgAAAAIbmlzdHAzODQAAABhBJPOgAXHijSxoZBiyhSDOR3eUELUoc+hqh/SY1Wq4/562jThf6Q+tjVzZTMWZMAP4S6DD2qZswsRvisxXkcZDOw5bvyk0WmezYvjUP6TZII/0BDVTotCf4SxukEtcqBZqgAAAAAAAAIfAAAAAgAAAA1Ib3N0IFAzODQga2V5AAAAHAAAAAlsb2NhbGhvc3QAAAALZXhhbXBsZS5jb20AAAAAXtfWGQAAAAC8kfpVAAAAFAAAAARjYXRzAAAACAAAAARkb2dzAAAALgAAAARsZW5zAAAACAAAAAR3aWRlAAAABHNpemUAAAAOAAAACmZ1bGwtZnJhbWUAAAAAAAAAiAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQR2JTEl2nF7dd6AS6TFxD9DkjMOaJHeXOxt4aIptTEf0x1DsjktgFUChKi2bPrXd2OsmAq6uUxlgzRmNnXyhV/fZy6iQqtpMUf/wj91IXq5GZ5+ruHluG4iy+8Tg6jTs5EAAACEAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMH0U5Rb7TVXX4TP1T1keRioun8qUwsynDX9HHJ/lxgQVdpv3rK/8JVRYE3iEhs8gCwAAADEAp+ljZpPr60aE5l0Q1KrLv5/gfEbYasXBdnSbO47qnAYRg+6VuEb+GGiG9ZAXsq5G [email protected]"
4855

56+
static let p384HostExport = "[email protected] AAAAKGVjZHNhLXNoYTItbmlzdHAzODQtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgvD8+H64ZEuPHwYIxuym9XHVpiJEoCvCqyy8Ch7JAZEgAAAAIbmlzdHAzODQAAABhBJPOgAXHijSxoZBiyhSDOR3eUELUoc+hqh/SY1Wq4/562jThf6Q+tjVzZTMWZMAP4S6DD2qZswsRvisxXkcZDOw5bvyk0WmezYvjUP6TZII/0BDVTotCf4SxukEtcqBZqgAAAAAAAAIfAAAAAgAAAA1Ib3N0IFAzODQga2V5AAAAHAAAAAlsb2NhbGhvc3QAAAALZXhhbXBsZS5jb20AAAAAXtfWGQAAAAC8kfpVAAAAFAAAAARjYXRzAAAACAAAAARkb2dzAAAALgAAAARsZW5zAAAACAAAAAR3aWRlAAAABHNpemUAAAAOAAAACmZ1bGwtZnJhbWUAAAAAAAAAiAAAABNlY2RzYS1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQR2JTEl2nF7dd6AS6TFxD9DkjMOaJHeXOxt4aIptTEf0x1DsjktgFUChKi2bPrXd2OsmAq6uUxlgzRmNnXyhV/fZy6iQqtpMUf/wj91IXq5GZ5+ruHluG4iy+8Tg6jTs5EAAACEAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMH0U5Rb7TVXX4TP1T1keRioun8qUwsynDX9HHJ/lxgQVdpv3rK/8JVRYE3iEhs8gCwAAADEAp+ljZpPr60aE5l0Q1KrLv5/gfEbYasXBdnSbO47qnAYRg+6VuEb+GGiG9ZAXsq5G"
57+
4958
static let ed25519UserBase = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJfkNV4OS33ImTXvorZr72q4v5XhVEQKfvqsxOEJ/XaR [email protected]"
5059

60+
static let ed25519UserBaseExport = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJfkNV4OS33ImTXvorZr72q4v5XhVEQKfvqsxOEJ/XaR"
61+
5162
/// A ed25519 user key. id "User ed25519 key" serial 0 valid from 2020-06-03T17:58:47 to 2038-01-19T03:14:07
5263
/// Generated using ssh-keygen -s ca-key -I "User ed25519 key" -V "-1m:+2600w" -O "force-command=uname -a" user-ed25519
5364
static let ed25519User = "[email protected] AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIDxk/nOhhVDtrweRRR1trNm3T3RdPinf7bYLTPnfWAPuAAAAIJfkNV4OS33ImTXvorZr72q4v5XhVEQKfvqsxOEJ/XaRAAAAAAAAAAAAAAABAAAAEFVzZXIgZWQyNTUxOSBrZXkAAAAAAAAAAF7X1scAAAAAvJH7AwAAACEAAAANZm9yY2UtY29tbWFuZAAAAAwAAAAIdW5hbWUgLWEAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBHYlMSXacXt13oBLpMXEP0OSMw5okd5c7G3hoim1MR/THUOyOS2AVQKEqLZs+td3Y6yYCrq5TGWDNGY2dfKFX99nLqJCq2kxR//CP3UherkZnn6u4eW4biLL7xODqNOzkQAAAIMAAAATZWNkc2Etc2hhMi1uaXN0cDM4NAAAAGgAAAAwBWeqRhZqFoGRXg7WtKSbQ9rOn2WNUiaDV1XjX2aCyi/W7431Hxpxg5iGLzP5B7ZuAAAAMByxIrsZhBM9RDxS2qGV9QByw5ebAaRFLtmvJSyxgn1nwWtkPnKetYTsP1Olh4+3tQ== [email protected]"
5465

66+
static let ed25519UserExport = "[email protected] AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIDxk/nOhhVDtrweRRR1trNm3T3RdPinf7bYLTPnfWAPuAAAAIJfkNV4OS33ImTXvorZr72q4v5XhVEQKfvqsxOEJ/XaRAAAAAAAAAAAAAAABAAAAEFVzZXIgZWQyNTUxOSBrZXkAAAAAAAAAAF7X1scAAAAAvJH7AwAAACEAAAANZm9yY2UtY29tbWFuZAAAAAwAAAAIdW5hbWUgLWEAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBHYlMSXacXt13oBLpMXEP0OSMw5okd5c7G3hoim1MR/THUOyOS2AVQKEqLZs+td3Y6yYCrq5TGWDNGY2dfKFX99nLqJCq2kxR//CP3UherkZnn6u4eW4biLL7xODqNOzkQAAAIMAAAATZWNkc2Etc2hhMi1uaXN0cDM4NAAAAGgAAAAwBWeqRhZqFoGRXg7WtKSbQ9rOn2WNUiaDV1XjX2aCyi/W7431Hxpxg5iGLzP5B7ZuAAAAMByxIrsZhBM9RDxS2qGV9QByw5ebAaRFLtmvJSyxgn1nwWtkPnKetYTsP1Olh4+3tQ=="
67+
5568
/// An expired ed25519 user key. id "Expired ed25519 key" serial 0 valid from 2019-06-12T13:00:56 to 2020-05-13T13:00:56
5669
static let ed25519UserExpired = "[email protected] AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIEm5DjT8oKCz6iBsujwy9Gk7WDbCZ0/+tV+oerk+WkTBAAAAIJfkNV4OS33ImTXvorZr72q4v5XhVEQKfvqsxOEJ/XaRAAAAAAAAAAAAAAABAAAAE0V4cGlyZWQgZWQyNTUxOSBrZXkAAAAAAAAAAF0A6XgAAAAAXrvheAAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBHYlMSXacXt13oBLpMXEP0OSMw5okd5c7G3hoim1MR/THUOyOS2AVQKEqLZs+td3Y6yYCrq5TGWDNGY2dfKFX99nLqJCq2kxR//CP3UherkZnn6u4eW4biLL7xODqNOzkQAAAIQAAAATZWNkc2Etc2hhMi1uaXN0cDM4NAAAAGkAAAAwfllCm2yx6paKwnn9UPoTmOP52DH2O4zTcLb3/Jz1wDhGG7yKffZ3vt8hBJ50htz/AAAAMQCM/wSyvs2+sGCemtPKy0OiBUAFOdA8p0bpnNgMF8fmyIVufkdkHrcxD4wvfhFayL4= [email protected]"
5770

@@ -288,6 +301,49 @@ final class CertifiedKeyTests: XCTestCase {
288301
XCTAssertEqual(userKey.signatureKey, caKey)
289302
}
290303

304+
// tests to check that keyIntoComponents returns the correct string without comments
305+
func testCaKeyToComponents() throws {
306+
let caKey = try NIOSSHPublicKey(openSSHPublicKey: Fixtures.caPublicKey)
307+
let reExported = String(openSSHPublicKey: caKey)
308+
XCTAssertEqual(reExported, Fixtures.caPublicKeyExport)
309+
}
310+
311+
func testBaseKeyToComponents() throws {
312+
let baseKey = try NIOSSHPublicKey(openSSHPublicKey: Fixtures.ed25519UserBase)
313+
let reExported = String(openSSHPublicKey: baseKey)
314+
XCTAssertEqual(reExported, Fixtures.ed25519UserBaseExport)
315+
}
316+
317+
func testUserKeyToComponents() throws {
318+
let userK = try (NIOSSHPublicKey(openSSHPublicKey: Fixtures.ed25519User))
319+
let reExported = String(openSSHPublicKey: userK)
320+
XCTAssertEqual(reExported, Fixtures.ed25519UserExport)
321+
}
322+
323+
func test384HostToComponents() throws {
324+
let host384 = try (NIOSSHPublicKey(openSSHPublicKey: Fixtures.p384Host))
325+
let reExported = String(openSSHPublicKey: host384)
326+
XCTAssertEqual(reExported, Fixtures.p384HostExport)
327+
}
328+
329+
func testUser256ToComponents() throws {
330+
let user256 = try (NIOSSHPublicKey(openSSHPublicKey: Fixtures.p256User))
331+
let reExported = String(openSSHPublicKey: user256)
332+
XCTAssertEqual(reExported, Fixtures.p256UserExport)
333+
}
334+
335+
func test384HostBaseToComponents() throws {
336+
let baseKey = try NIOSSHPublicKey(openSSHPublicKey: Fixtures.p384HostBase)
337+
let reExported = String(openSSHPublicKey: baseKey)
338+
XCTAssertEqual(reExported, Fixtures.p384HostBaseExport)
339+
}
340+
341+
func test256UserBasedToComponents() throws {
342+
let userB = try (NIOSSHPublicKey(openSSHPublicKey: Fixtures.p256UserBase))
343+
let reExported = String(openSSHPublicKey: userB)
344+
XCTAssertEqual(reExported, Fixtures.p256UserBaseExport)
345+
}
346+
291347
private func testVerificationFailsOnMutation(_ mutator: (inout NIOSSHCertifiedPublicKey) throws -> Void) throws {
292348
let caKey = try NIOSSHPublicKey(openSSHPublicKey: Fixtures.caPublicKey)
293349
var userKey = try NIOSSHCertifiedPublicKey(NIOSSHPublicKey(openSSHPublicKey: Fixtures.p256User))!

0 commit comments

Comments
 (0)