added a keymanager and keymanagerview, can ontly generate 4096 rsa rn

uses seckey
This commit is contained in:
neon443
2025-06-20 10:31:22 +01:00
parent 5747fe5395
commit 86dd316e33
9 changed files with 301 additions and 143 deletions

View File

@@ -29,6 +29,9 @@
A95FAA552DF4B62900DE2F5A /* LibSSH.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A95FAA502DF4B62100DE2F5A /* LibSSH.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A95FAA552DF4B62900DE2F5A /* LibSSH.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A95FAA502DF4B62100DE2F5A /* LibSSH.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A95FAA562DF4B62A00DE2F5A /* openssl.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A95FAA512DF4B62100DE2F5A /* openssl.xcframework */; }; A95FAA562DF4B62A00DE2F5A /* openssl.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A95FAA512DF4B62100DE2F5A /* openssl.xcframework */; };
A95FAA572DF4B62A00DE2F5A /* openssl.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A95FAA512DF4B62100DE2F5A /* openssl.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A95FAA572DF4B62A00DE2F5A /* openssl.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A95FAA512DF4B62100DE2F5A /* openssl.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A98554552E05535F009051BD /* KeyManagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98554542E05535F009051BD /* KeyManagerView.swift */; };
A98554592E0553AA009051BD /* KeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98554582E0553AA009051BD /* KeyManager.swift */; };
A985545D2E055D4D009051BD /* ConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A985545C2E055D4D009051BD /* ConnectionView.swift */; };
A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */; }; A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@@ -87,6 +90,9 @@
A95FAA5A2DF4B79900DE2F5A /* ci_post_clone.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_post_clone.sh; sourceTree = "<group>"; }; A95FAA5A2DF4B79900DE2F5A /* ci_post_clone.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_post_clone.sh; sourceTree = "<group>"; };
A95FAA5B2DF4B7A000DE2F5A /* ci_pre_xcodebuild.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_pre_xcodebuild.sh; sourceTree = "<group>"; }; A95FAA5B2DF4B7A000DE2F5A /* ci_pre_xcodebuild.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_pre_xcodebuild.sh; sourceTree = "<group>"; };
A95FAA5C2DF4B7A300DE2F5A /* ci_prost_xcodebuild.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_prost_xcodebuild.sh; sourceTree = "<group>"; }; A95FAA5C2DF4B7A300DE2F5A /* ci_prost_xcodebuild.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_prost_xcodebuild.sh; sourceTree = "<group>"; };
A98554542E05535F009051BD /* KeyManagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyManagerView.swift; sourceTree = "<group>"; };
A98554582E0553AA009051BD /* KeyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyManager.swift; sourceTree = "<group>"; };
A985545C2E055D4D009051BD /* ConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionView.swift; sourceTree = "<group>"; };
A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHHandler.swift; sourceTree = "<group>"; }; A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSHHandler.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -153,8 +159,10 @@
children = ( children = (
A93143C22DF61F5700FCD5DB /* ShhShell.entitlements */, A93143C22DF61F5700FCD5DB /* ShhShell.entitlements */,
A92538C62DEE0742007E0A18 /* ShhShellApp.swift */, A92538C62DEE0742007E0A18 /* ShhShellApp.swift */,
A92538D32DEE0749007E0A18 /* Views */, A98554572E055398009051BD /* Keys */,
A98554562E055394009051BD /* Host */,
A93143C12DF61E8500FCD5DB /* SSH */, A93143C12DF61E8500FCD5DB /* SSH */,
A92538D32DEE0749007E0A18 /* Views */,
); );
path = ShhShell; path = ShhShell;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -179,9 +187,10 @@
A92538D32DEE0749007E0A18 /* Views */ = { A92538D32DEE0749007E0A18 /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A98554532E05534F009051BD /* Keys */,
A92538C52DEE0742007E0A18 /* ContentView.swift */, A92538C52DEE0742007E0A18 /* ContentView.swift */,
A91AE3B12DF73E0900FF3537 /* TerminalView.swift */, A985545C2E055D4D009051BD /* ConnectionView.swift */,
A91AE3BC2DF7402100FF3537 /* TextViewController.swift */, A98554522E055347009051BD /* Terminal */,
A93143C52DF61FE300FCD5DB /* ViewModifiers.swift */, A93143C52DF61FE300FCD5DB /* ViewModifiers.swift */,
); );
path = Views; path = Views;
@@ -198,7 +207,6 @@
A93143C12DF61E8500FCD5DB /* SSH */ = { A93143C12DF61E8500FCD5DB /* SSH */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A93143BF2DF61B3200FCD5DB /* Host.swift */,
A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */, A9C897EE2DF1A9A400EF9A5F /* SSHHandler.swift */,
); );
path = SSH; path = SSH;
@@ -214,6 +222,39 @@
path = ci_scripts; path = ci_scripts;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
A98554522E055347009051BD /* Terminal */ = {
isa = PBXGroup;
children = (
A91AE3B12DF73E0900FF3537 /* TerminalView.swift */,
A91AE3BC2DF7402100FF3537 /* TextViewController.swift */,
);
path = Terminal;
sourceTree = "<group>";
};
A98554532E05534F009051BD /* Keys */ = {
isa = PBXGroup;
children = (
A98554542E05535F009051BD /* KeyManagerView.swift */,
);
path = Keys;
sourceTree = "<group>";
};
A98554562E055394009051BD /* Host */ = {
isa = PBXGroup;
children = (
A93143BF2DF61B3200FCD5DB /* Host.swift */,
);
path = Host;
sourceTree = "<group>";
};
A98554572E055398009051BD /* Keys */ = {
isa = PBXGroup;
children = (
A98554582E0553AA009051BD /* KeyManager.swift */,
);
path = Keys;
sourceTree = "<group>";
};
A9C8976F2DF1980900EF9A5F /* Frameworks */ = { A9C8976F2DF1980900EF9A5F /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -376,9 +417,12 @@
A92538C82DEE0742007E0A18 /* ContentView.swift in Sources */, A92538C82DEE0742007E0A18 /* ContentView.swift in Sources */,
A93143C02DF61B3200FCD5DB /* Host.swift in Sources */, A93143C02DF61B3200FCD5DB /* Host.swift in Sources */,
A92538C92DEE0742007E0A18 /* ShhShellApp.swift in Sources */, A92538C92DEE0742007E0A18 /* ShhShellApp.swift in Sources */,
A985545D2E055D4D009051BD /* ConnectionView.swift in Sources */,
A91AE3B22DF73E0900FF3537 /* TerminalView.swift in Sources */, A91AE3B22DF73E0900FF3537 /* TerminalView.swift in Sources */,
A98554592E0553AA009051BD /* KeyManager.swift in Sources */,
A91AE3BD2DF7402100FF3537 /* TextViewController.swift in Sources */, A91AE3BD2DF7402100FF3537 /* TextViewController.swift in Sources */,
A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */, A9C897EF2DF1A9A400EF9A5F /* SSHHandler.swift in Sources */,
A98554552E05535F009051BD /* KeyManagerView.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -0,0 +1,38 @@
//
// KeyManager.swift
// ShhShell
//
// Created by neon443 on 20/06/2025.
//
import Foundation
struct Key: Identifiable, Hashable {
var id = UUID()
var privateKey: SecKey
var publicKey: SecKey {
SecKeyCopyPublicKey(privateKey)!
}
}
class KeyManager: ObservableObject {
func generateRSA() throws {
let type = kSecAttrKeyTypeRSA
let tag = "com.neon443.ShhSell.keys.\(Date().timeIntervalSince1970)".data(using: .utf8)!
let attributes: [String: Any] =
[kSecAttrKeyType as String: type,
kSecAttrKeySizeInBits as String: 4096,
kSecPrivateKeyAttrs as String:
[kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag]
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
print(privateKey)
print(SecKeyCopyPublicKey(privateKey))
}
}

View File

@@ -10,10 +10,14 @@ import SwiftUI
@main @main
struct ShhShellApp: App { struct ShhShellApp: App {
@StateObject var sshHandler: SSHHandler = SSHHandler(host: blankHost()) @StateObject var sshHandler: SSHHandler = SSHHandler(host: blankHost())
@StateObject var keyManager: KeyManager = KeyManager()
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
ContentView(handler: sshHandler) ContentView(
handler: sshHandler,
keyManager: keyManager
)
} }
} }
} }

View File

@@ -0,0 +1,162 @@
//
// ConnectionView.swift
// ShhShell
//
// Created by neon443 on 20/06/2025.
//
import SwiftUI
struct ConnectionView: View {
@StateObject var handler: SSHHandler
@StateObject var keyManager: KeyManager
@State var passphrase: String = ""
@State var pubkeyStr: String = ""
@State var privkeyStr: String = ""
@State var pubkey: Data?
@State var privkey: Data?
@State var privPickerPresented: Bool = false
@State var pubPickerPresented: Bool = false
var body: some View {
NavigationStack {
List {
HStack {
TextField("", text: $pubkeyStr, prompt: Text("Public Key"))
.onSubmit {
pubkey = Data(pubkeyStr.utf8)
}
Button() {
pubPickerPresented.toggle()
} label: {
Image(systemName: "folder")
}
.buttonStyle(.plain)
.fileImporter(isPresented: $pubPickerPresented, allowedContentTypes: [.item, .content, .data]) { (Result) in
do {
let fileURL = try Result.get()
pubkey = try! Data(contentsOf: fileURL)
print(fileURL)
} catch {
print(error.localizedDescription)
}
}
}
HStack {
TextField("", text: $privkeyStr, prompt: Text("Private Key"))
.onSubmit {
privkey = Data(privkeyStr.utf8)
}
Button() {
privPickerPresented.toggle()
} label: {
Image(systemName: "folder")
}
.buttonStyle(.plain)
.fileImporter(isPresented: $privPickerPresented, allowedContentTypes: [.item, .content, .data]) { (Result) in
do {
let fileURL = try Result.get()
privkey = try! Data(contentsOf: fileURL)
print(fileURL)
} catch {
print(error.localizedDescription)
}
}
}
TextField("", text: $passphrase)
HStack {
Text(handler.connected ? "connected" : "not connected")
.modifier(foregroundColorStyle(handler.connected ? .green : .red))
Text(handler.authorized ? "authorized" : "unauthorized")
.modifier(foregroundColorStyle(handler.authorized ? .green : .red))
}
// if let testSucceded = testSucceded {
// Image(systemName: testSucceded ? "checkmark.circle" : "xmark.circle")
// .modifier(foregroundColorStyle(testSucceded ? .green : .red))
// }
if handler.host.key != nil {
Text("Hostkey: \(handler.host.key!.base64EncodedString())")
}
TextField("address", text: $handler.host.address)
.textFieldStyle(.roundedBorder)
TextField(
"port",
text: Binding(
get: { String(handler.host.port) },
set: { handler.host.port = Int($0) ?? 22} )
)
.keyboardType(.numberPad)
.textFieldStyle(.roundedBorder)
TextField("username", text: $handler.host.username)
.textFieldStyle(.roundedBorder)
SecureField("password", text: $handler.host.password)
.textFieldStyle(.roundedBorder)
if handler.connected {
Button() {
handler.disconnect()
} label: {
Label("Disconnect", systemImage: "xmark.app.fill")
}
} else {
Button() {
handler.connect()
if pubkey != nil && privkey != nil {
handler.authWithPubkey(pub: pubkey!, priv: privkey!, pass: passphrase)
} else {
let _ = handler.authWithPw()
}
handler.openShell()
} label: {
Label("Connect", systemImage: "powerplug.portrait")
}
.disabled(
pubkey == nil && privkey == nil ||
handler.host.username.isEmpty && handler.host.password.isEmpty
)
}
NavigationLink() {
TerminalView(handler: handler)
} label: {
Label("Open Terminal", systemImage: "apple.terminal")
}
.disabled(!(handler.connected && handler.authorized))
Button() {
withAnimation { handler.testExec() }
} label: {
if handler.testSuceeded {
Image(systemName: handler.testSuceeded ? "checkmark.circle" : "xmark.circle")
.modifier(foregroundColorStyle(handler.testSuceeded ? .green : .red))
} else {
Label("Test Connection", systemImage: "checkmark")
}
}
.disabled(!(handler.connected && handler.authorized))
}
.transition(.opacity)
}
}
}
#Preview {
ConnectionView(
handler: SSHHandler(host: debugHost()),
keyManager: KeyManager()
)
}

View File

@@ -9,151 +9,28 @@ import SwiftUI
struct ContentView: View { struct ContentView: View {
@ObservedObject var handler: SSHHandler @ObservedObject var handler: SSHHandler
@ObservedObject var keyManager: KeyManager
@State var passphrase: String = ""
@State var pubkeyStr: String = ""
@State var privkeyStr: String = ""
@State var pubkey: Data?
@State var privkey: Data?
@State var privPickerPresented: Bool = false
@State var pubPickerPresented: Bool = false
var body: some View { var body: some View {
NavigationStack { TabView {
List { ConnectionView(
HStack { handler: handler,
TextField("", text: $pubkeyStr, prompt: Text("Public Key")) keyManager: keyManager
.onSubmit { )
pubkey = Data(pubkeyStr.utf8) .tabItem {
} Label("Connection", systemImage: "powerplug.portrait")
Button() {
pubPickerPresented.toggle()
} label: {
Image(systemName: "folder")
}
.buttonStyle(.plain)
.fileImporter(isPresented: $pubPickerPresented, allowedContentTypes: [.item, .content, .data]) { (Result) in
do {
let fileURL = try Result.get()
pubkey = try! Data(contentsOf: fileURL)
print(fileURL)
} catch {
print(error.localizedDescription)
}
}
}
HStack {
TextField("", text: $privkeyStr, prompt: Text("Private Key"))
.onSubmit {
privkey = Data(privkeyStr.utf8)
}
Button() {
privPickerPresented.toggle()
} label: {
Image(systemName: "folder")
}
.buttonStyle(.plain)
.fileImporter(isPresented: $privPickerPresented, allowedContentTypes: [.item, .content, .data]) { (Result) in
do {
let fileURL = try Result.get()
privkey = try! Data(contentsOf: fileURL)
print(fileURL)
} catch {
print(error.localizedDescription)
}
}
}
TextField("", text: $passphrase)
HStack {
Text(handler.connected ? "connected" : "not connected")
.modifier(foregroundColorStyle(handler.connected ? .green : .red))
Text(handler.authorized ? "authorized" : "unauthorized")
.modifier(foregroundColorStyle(handler.authorized ? .green : .red))
}
// if let testSucceded = testSucceded {
// Image(systemName: testSucceded ? "checkmark.circle" : "xmark.circle")
// .modifier(foregroundColorStyle(testSucceded ? .green : .red))
// }
if handler.host.key != nil {
Text("Hostkey: \(handler.host.key!.base64EncodedString())")
}
TextField("address", text: $handler.host.address)
.textFieldStyle(.roundedBorder)
TextField(
"port",
text: Binding(
get: { String(handler.host.port) },
set: { handler.host.port = Int($0) ?? 22} )
)
.keyboardType(.numberPad)
.textFieldStyle(.roundedBorder)
TextField("username", text: $handler.host.username)
.textFieldStyle(.roundedBorder)
SecureField("password", text: $handler.host.password)
.textFieldStyle(.roundedBorder)
if handler.connected {
Button() {
handler.disconnect()
} label: {
Label("Disconnect", systemImage: "xmark.app.fill")
}
} else {
Button() {
handler.connect()
if pubkey != nil && privkey != nil {
handler.authWithPubkey(pub: pubkey!, priv: privkey!, pass: passphrase)
} else {
let _ = handler.authWithPw()
}
handler.openShell()
} label: {
Label("Connect", systemImage: "powerplug.portrait")
}
.disabled(
pubkey == nil && privkey == nil ||
handler.host.username.isEmpty && handler.host.password.isEmpty
)
}
NavigationLink() {
TerminalView(handler: handler)
} label: {
Label("Open Terminal", systemImage: "apple.terminal")
}
.disabled(!(handler.connected && handler.authorized))
Button() {
withAnimation { handler.testExec() }
} label: {
if handler.testSuceeded {
Image(systemName: handler.testSuceeded ? "checkmark.circle" : "xmark.circle")
.modifier(foregroundColorStyle(handler.testSuceeded ? .green : .red))
} else {
Label("Test Connection", systemImage: "checkmark")
}
}
.disabled(!(handler.connected && handler.authorized))
} }
.transition(.opacity) KeyManagerView(keyManager: keyManager)
.tabItem {
Label("Keys", systemImage: "key.2.on.ring")
}
} }
} }
} }
#Preview { #Preview {
ContentView( ContentView(
handler: SSHHandler(host: debugHost()) handler: SSHHandler(host: debugHost()),
keyManager: KeyManager()
) )
} }

View File

@@ -0,0 +1,33 @@
//
// KeyManagerView.swift
// ShhShell
//
// Created by neon443 on 20/06/2025.
//
import SwiftUI
struct KeyManagerView: View {
@ObservedObject var keyManager: KeyManager
var body: some View {
Button("ed25519") {
do {
try keyManager.generateEd25519()
} catch {
print(error.localizedDescription)
}
}
// Button("rsa") {
// do {
// try keyManager.generateRSA()
// } catch {
// print(error.localizedDescription)
// }
// }
}
}
#Preview {
KeyManagerView(keyManager: KeyManager())
}