diff --git a/src/backend/database/routes/credentials.ts b/src/backend/database/routes/credentials.ts index f628da66..bc4fee3e 100644 --- a/src/backend/database/routes/credentials.ts +++ b/src/backend/database/routes/credentials.ts @@ -11,7 +11,7 @@ import ssh2Pkg from "ssh2"; const { utils: ssh2Utils } = ssh2Pkg; // Direct SSH key generation with ssh2 - the right way -function generateSSHKeyPair(keyType: string, keySize?: number): { success: boolean; privateKey?: string; publicKey?: string; error?: string } { +function generateSSHKeyPair(keyType: string, keySize?: number, passphrase?: string): { success: boolean; privateKey?: string; publicKey?: string; error?: string } { console.log('Generating SSH key pair with ssh2:', keyType); try { @@ -29,6 +29,12 @@ function generateSSHKeyPair(keyType: string, keySize?: number): { success: boole options.bits = 256; // ECDSA P-256 uses 256 bits } + // Add passphrase protection if provided + if (passphrase && passphrase.trim()) { + options.passphrase = passphrase; + options.cipher = 'aes128-cbc'; // Default cipher for encrypted private keys + } + // Use ssh2's native key generation const keyPair = ssh2Utils.generateKeyPairSync(ssh2Type as any, options); @@ -882,8 +888,8 @@ router.post("/generate-key-pair", authenticateJWT, async (req: Request, res: Res console.log("Has passphrase:", !!passphrase); try { - // Generate keys with crypto, convert public key to SSH format - const result = generateSSHKeyPair(keyType, keySize); + // Generate SSH keys directly with ssh2 + const result = generateSSHKeyPair(keyType, keySize, passphrase); if (result.success && result.privateKey && result.publicKey) { const response = { diff --git a/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx b/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx index 40d22471..52652ae0 100644 --- a/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx +++ b/src/ui/Desktop/Apps/Credentials/CredentialEditor.tsx @@ -376,6 +376,7 @@ export function CredentialEditor({ }; const [tagInput, setTagInput] = useState(""); + const [keyGenerationPassphrase, setKeyGenerationPassphrase] = useState(""); const [folderDropdownOpen, setFolderDropdownOpen] = useState(false); const folderInputRef = useRef(null); @@ -675,6 +676,23 @@ export function CredentialEditor({ {t("credentials.generateKeyPair")} + + {/* Key Generation Passphrase Input */} +
+ + {t("credentials.keyPassword")} ({t("credentials.optional")}) + + setKeyGenerationPassphrase(e.target.value)} + className="max-w-xs" + /> +
+ {t("credentials.keyPassphraseOptional")} +
+
+