使用 Java 解析和轉換 OpenSSH 和 PuTTY 私鑰格式

Mondo 科技 更新 2024-01-28

兩種常用的 SSH 金鑰格式及其轉換方式。

Secure Shell (SSH) 是一種建立在應用層和傳輸層之上的安全協議,由 IETF 的網路工作組開發。 SSH 是當今最可靠的協議,可為 telnet 會話和其他網路服務提供安全性。 SSH協議可以有效防止遠端管理過程中的資訊洩露。 除了使用者名稱和密碼的密碼認證方法以及金鑰認證方法外,SSH還可用於驗證客戶端和伺服器之間通訊的安全性。

在程式開發中,我們經常需要使用程式與伺服器或儲存裝置建立SSH連線來管理裝置(如重啟裝置、檢視硬體資訊等),目前J**a的開源SSH庫一般只支援OpenSSH格式的金鑰連線,但在日常使用中,大量客戶會使用Putty來建立SSH金鑰, 這樣程式就需要解析和轉換金鑰。本文介紹如何使用 Orion SSH2 通過金鑰 SSH 連線到伺服器,並在 J**a 程式中執行操作,並分析了兩種常用 SSH 金鑰的格式和解析原理。

在本文中,我們將使用 Orion SSH2 作為 SSH 客戶端,然後使用金鑰訪問 Linux 伺服器並執行 Linux 命令。

首先,我們需要在 Linux 伺服器上生成一對金鑰,將公鑰註冊到伺服器,然後將私鑰傳輸到本地計算機,然後使用 putty 載入私鑰並連線到 Linux 伺服器。 伺服器管理工具、儲存裝置、交換機等很多裝置也都提供了SSH連線功能,有時候我們需要根據使用者需求開發程式來管理這些裝置,這時我們就可以使用SSH客戶端庫來開發。

Orion SSH2 是乙個純 JSH-2 協議包,允許 JSH 程式通過 SSH 協議連線到伺服器,以執行遠端命令和檔案傳輸功能。 通過ORION SSH2,我們只需要傳入SSH伺服器的IP、埠和金鑰資訊即可建立SSH連線,CLI命令的具體程式碼如下:

string hostname = "192.168.1.2"; string username = "root";輸入金鑰所在的路徑 file keyfile = new file("c:\\temp\\private");輸入金鑰的加密密碼,不能設定為空字串 keyfilepass ="joespass";嘗試關閉 SSH 會話 SESSclose();關閉 SSH 連線連線close();catch (ioexception e)
以上 ** 顯示了連線到 Linux 系統並執行命令 uname -a &&&&date &&uptime &&&WHO 列印作業系統資訊、當前系統時間、系統執行時間以及當前登入使用者資訊的過程。 同樣,我們可以用這個**來連線伺服器的IMM(Integrated Management Module)來執行命令來檢視伺服器的網路配置和硬體資訊或重啟伺服器,或者我們可以連線到IBM Storwize V7000的控制台來管理V7000。

OpenSSH 在 RFC4716 中定義了公鑰和私鑰的格式,它簡單地由乙個初始建立者、乙個標頭、乙個主體和乙個最終建立者組成。 下面是乙個未加密的私鑰(在本文中,我們將使用未加密的 RSA 私鑰進行解析)。

---begin rsa private key---miicwgibaakbggabzjs7fo9heoxvzqnjhu9wtofbnx+rmgtrtra0pqg0ekori7mnskd3k8zgs3tgjveww48ydufvqndwgpdwztlhbgpydbqk1ki87arazrexaely7tq60+cpj8vgkvec3k1zh0/tkruhlp/6c8v3soqi56d1/zjqwerz8fvfqmy9agelaogaicsukvoxtc0bi0m8sycmi3fzsekkgbbq9uy5rnqwaxbdlihhhckptfx6n6vvflcpdragk1ue1azmnhajv82l6xm4s1m06jmq2uvvmpnjpecn978u9qpjke+iqdys1gs6ck3xqyfkm1zgrpckbqfl4fswh62pwuh5amxlzu71r60cqqc5ebbwygtn7bnoqgtv/gdzdwqwimbfrpbqfgziwlw**lb8kcj7trkduzg6yt8zcszdkgsjxkgkqs51f+uk/b0bakeahpgeazdmu9rpzzwx0mm6sxdmsrkgpfvelxkhqheakiaorhuasr3xn0dr4r4hpppwwvg4aqmjybi+ug7n5n1dv**bakapuhwbvw3frsa3ssfg6/n/jiftsuqd5jhi/ppat5bfzrdrxbeeb8ugonjmzsmlzrkn0jgdoi5ozjwbm15do60cqcpurmfjuq7hoclmycqvoskjwc9ujcxtjxoiab5lfjxfozwk624lf3a5w21gafwrcwq/mabjhhn3f99crxwgicucqqccdz8y99auopppd1t4z1wemc44nyl/m+ixgxieveauwv0udbbtyuqx+9gh2wimvyfl8uggtqci22n6xt/u+w8n---end rsa private key---
OpenSSH 支援 RSA 和 DSA 金鑰,本文將以 RSA 金鑰為例進行解析。 RSA金鑰的語法結構在文件RFC3447中定義,私鑰的語法結構如下:

rsaprivatekey ::= sequence
version 是 RSA 的版本號,本文中使用的金鑰是版本 0,如果金鑰使用多個素數(超過 2 個素數),則為 1。 模量是 RSA 的復合模量 n。 Publicexponent 是 RSA 的公共權力 e。 Privateexponent 是 rsa 的私有冪 d。 prime1 是 n 的質因數 p。 素數2 i 是 n 的質因數 q。 指數 1 等於 D mod (p 1)。 指數 2 等於 D mod (q 1)。 係數是 CRT 係數 q 1 mod p。 OtherprimeInfos 包含其他素數 r3 ,......挨次如。 如果 version 為 0,則應忽略;如果 version 為 1,則它應該至少包含乙個 otherprimeinfo 例項。 RSA公鑰的語法結構如下圖所示,可以看出公鑰所需的因子資訊包含在私鑰中。

rsapublickey ::= sequence
OpenSSH 的 RSA 金鑰的檔案正文是 DER 編碼的,JDK 提供了乙個 DerInputStream 來解析 DER 編碼的字串。 因此,openssh 金鑰的解析非常簡單,首先讀取金鑰,過濾掉開始和結束標誌,檔案頭(如果是加密金鑰,則需要根據檔案頭資訊確定解密方法,因為本文使用的是未加密的金鑰,去掉檔案頭),然後使用 der inputstream 解析金鑰, **如下:

string keyinfo = "";string line = null;刪除檔案開頭和結尾的注釋,同時 ((line = br..)readline())= null) } 金鑰資訊採用 base64 編碼加密,需要解密位元組 decodeKeyInfo = (new base64DeCoder())DecodeBuffer(KeyInfo);使用 derinputstream 讀取金鑰資訊,derinputstream dis = new derinputstream(decodekeyinfo);該鍵不包含 otherprimeinfos 資訊,因此只有 9 個 dervalue ders = disgetsequence(9);讀取 rsa 因子資訊 int version = ders[0]。getbiginteger().intvalue();biginteger modulus = ders[1].getbiginteger();biginteger publicexponent = ders[2].getbiginteger();biginteger privateexponent = ders[3].getbiginteger();biginteger primep = ders[4].getbiginteger();biginteger primeq = ders[5].getbiginteger();biginteger primeexponentp = ders[6].getbiginteger();biginteger primeexponentq = ders[7].getbiginteger();biginteger crtcoefficient = ders[8].getbiginteger();//generate public key and private keykeyfactory keyfactory = keyfactory.getinstance("rsa");rsapublickeyspec rsapublickeyspec =new rsapublickeyspec(modulus, publicexponent);publickey publickey = keyfactory.generatepublic(rsapublickeyspec);rsaprivatecrtkeyspec rsaprivatekeyspec =new rsaprivatecrtkeyspec(modulus,publicexponent,privateexponent,primep,primeq,primeexponentp,primeexponentq,crtcoefficient);privatekey privatekey = keyfactory.generateprivate(rsaprivatekeyspec);
SSH 金鑰的解析也可以在 Orion SSH2 的原始碼中看到,詳見 pemdecoder 類,以下是解析 RSA 私鑰的片段:

if (ps.pemtype == pem_rsa_private_key)
金鑰解析完成後,可以使用這些金鑰因子資訊與 SSH 伺服器進行驗證,具體的驗證流程請參考 OION SSH2 中的 AuthenticationManager 類。

Putty 沒有提供其金鑰語法的文件,但 Putty 是乙個開源專案,我們可以從其網站 Putty 的原始碼中了解它是如何構建金鑰的。

從 putty 原始碼中我們可以看到 putty 的金鑰結構如下:

putty-user-key-file-2:encryption:comment: public-lines:private-lines:private-mac:
第一行的金鑰演算法標記了金鑰的演算法,“ssh-rsa”表示使用了RSA演算法。 下行的加密表示金鑰的加密方式,目前僅支援“AES256-CBC”和“None”。 公鑰和私鑰後面的數字分別表示公鑰和私鑰的行數。 專用 MAC 是此金鑰資訊的 HMAC-SHA1 值。

實際的 ppk 金鑰如下:

putty-user-key-file-2: ssh-rsaencryption: nonecomment: imported-openssh-keypublic-lines: 4aaaab3nzac1yc2eaaaabjqaaaibgg847o36pr3qmb2ap4x7vvkzhwzv/ktbrubawnd6hthpdq4uzj0pa9yvgyln7ro1rmfupghvh1ajq8bqxcgbzyqyd8nw6pnsovo2qwgaxmqbjcu00otpgj4/lxpfratytwr9p05evb5t/+**fd0jqioendf2y6lnq2fbvrajmvq==private-lines: 8aaaagcheljfaf7xnaytjvemhjotxwuhcpbgq**vgoutufgf2wyyiyyqij7x1+p+lb35xdw6wictbntqmzjxwcvfni+stoettnoo5qtll1zqty6xgjfe/fpuky5hvoka2etyeunct8ugbsjncxqzwpg6hzebbfh+tqvrh+wpss87u9uetaaaaqqc5ebbwygtn7bnoqgtv/gdzdwqwimbfrpbqfgziwlw**lb8kcj7trkduzg6yt8zcszdkgsjxkgkqs51f+uk/b0baaaaqqce8z4bkmxt2ullnbfqwzqxcoaysqa9+8svcodad4a**o5gg5pkvfe3r2vhhigmk/bzubhqqangej66ds3k3uo9aaaaqqccdz8y99auopppd1t4z1wemc44nyl/m+ixgxieveauwv0udbbtyuqx+9gh2wimvyfl8uggtqci22n6xt/u+w8nprivate-mac: 4c039beb12ae005acb763225572cc4eeec767542
從 Putty 的原始碼可以看出,ppk 中的公鑰包含以下資訊:

string "ssh-rsa"mpint exponentmpint modulus
私鑰包含以下資訊:

mpint private_exponentmpint p (the larger of the two primes)mpint q (the smaller prime)mpint iqmp (the inverse of q modulo p)data padding (to reach a multiple of the cipher block size)
此因素資訊可以在上面解釋的 OpenSSH 私鑰格式中找到。

公鑰和私鑰資訊都遵循 RFC 4251 中定義的資料格式,這僅意味著每個元素都儲存為位元組流,前 4 個位元組是整數,數值是元素的位元組長度。 由於私鑰只儲存 IQPm(即 OpenSSH 稱之為係數),我們還需要計算 primep(即上面的 prime1)和 primeq(即上面的 prime2)。 具體分析如下:

public static void testppk() catch (exception e) 讀取前 4 個位元組獲取元素長度,然後讀取該元素的位元組資訊並將其轉換為 biginteger public static biginteger readint(datainputstream dis) 丟擲 ioexception 將鍵資訊解析為鍵值對 private static map parsekv(string file) 丟擲 ioexception }else s = s + line; kv.put(k, s); finally return kv; }
ORION SSH2 不支援 PPK 格式的 SSH 金鑰,但是通過以上方法,我們可以自己從 PPK 中讀取 RSA 的每個元素,這樣在 ORION SSH2 的 AuthenticationManager 中,我們可以將 PPK 格式金鑰的判斷和處理新增到 ORION SSH2 的 AuthenticationManager 中,這樣 ORION SSH2 就可以使用 PPK 連線到 SSH 伺服器了。

大多數 j**a ssh 客戶端庫只提供對 openssh 金鑰的支援,但在實踐中,大多數使用者使用 putty 來生成和管理 ssh 金鑰,使用上述方法可以讓我們編寫直接支援 ppk 金鑰的程式,而無需使用者在使用程式前轉換金鑰格式。

同樣,為了方便使用者,我們可以在程式中提供金鑰格式轉換功能。

要轉換這兩類金鑰,首先需要將它們解析成金鑰演算法對應的因子,解析方法在前兩節中已經介紹過了,所以我就不重複了,下面只說明如何根據RSA的因子生成OpenSSH格式或者PPK格式的金鑰。

OpenSSH 金鑰使用 DER 編碼來記錄資訊,而 J**A 提供了 deroutputstream 來寫入 der 資料,唯一需要注意的是,在構建了金鑰的位元組資訊後,還需要寫入 der 的序列標籤資訊,以表明資訊是有序的。 轉換後的**如下:

deroutputstream deros = new deroutputstream();deros.putinteger(version);deros.putinteger(modulus);deros.putinteger(publicexponent);deros.putinteger(privateexponent);deros.putinteger(primep);deros.putinteger(primeq);deros.putinteger(primeexponentp);deros.putinteger(primeexponentq);deros.putinteger(crtcoefficient);deros.flush();byte rsainfo = deros.tobytearray();deros.close();deros = new deroutputstream();在寫入資訊時,需要編寫序列標記write(dervalue.tag_sequence, rsainfo);deros.flush();寫入需要使用 base64 編碼進行加密:string keyinfo = (new base64encoder())encode(deros.tobytearray())
在PPK金鑰格式解析中對PPK的資訊格式進行了分析,在得到RSA的計算因子後寫入資訊時,計算每個因子的位元組長度並寫入長度資訊,然後寫入因子的位元組資訊,以公鑰資訊為例

bytearrayoutputstream bos = new bytearrayoutputstream();寫出元素bos的長度資訊write(convertinttobytearray(keyalgo.length()) 寫入位元組資訊 bos。元素的write(keyalgo.getbytes())bos.write(convertinttobytearray(publicexponent.tobytearray().length));bos.write(publicexponent.tobytearray())bos.write(convertinttobytearray(modulus.tobytearray().length));bos.write(modulus.tobytearray())bos.flush();string keyinfo = (new base64encoder())encode(bos.tobytearray())bos.close();public static byte convertinttobytearray(int value)
SSH 是一種使用非常廣泛的協議,許多伺服器管理工具(如 IMM、CMM)、儲存裝置(如 IBM Storwize V7000)和交換機都提供了 SSH 伺服器,供管理員使用 SSH 客戶端(如 putty)登入,並使用命令列檢視裝置資訊和遠端控制裝置,因此許多虛擬機器管理程式也使用 SSH 客戶端來管理這些裝置。

本文以開源的 J**A SSH 客戶端(Orion SSH2)為例,講解 SSH 金鑰的結構以及 SSH 客戶端對金鑰的解析過程,希望能幫助大家了解 SSH 金鑰,並使用 SSH 客戶端進行開發。

相關問題答案

    使用JAVA租房系統原始碼,網上租房更方便

    使用j a租房系統的原始碼 網上租房更方便。隨著網際網絡的普及和技術的進步,網上租房已成為一種趨勢。採用J a開發的租賃系統原始碼,為使用者提供更便捷 更高效的租賃體驗。本文將利用J A的優勢,實現網租的便利性。 J A租賃系統原始碼優勢。跨平台 J a 是一種跨平台語言,它開發了乙個可以在多個作業...

    如何使用按摩板清潔痤瘡?

    按摩板是一種可以有效清潔面部和預防粉刺的工具,下面介紹如何使用按摩板清潔痘痘 清潔面部。在使用按摩板之前,首先需要用溫水或清潔劑清潔臉部,以去除表面的汙垢和油脂。然後,用唇輕輕擦乾臉部。塗抹潔面乳或潤唇膏。將適量的潔面乳或潤唇膏均勻塗抹在臉上,這樣在使用按摩板時更容易滑動,也有助於提高清潔效果。使用...

    使用空化射流銷毀彈藥的優點

    利用空化射流排空裝藥彈產生的廢水經澄清後可迴圈進入空化射流射流去除工藝,經多層填料過濾器和活性炭過濾器過濾,清除後的廢炸藥經技術處理後可用於民用爆破,過濾器中的廢活性炭可由焚燒爐處理,提高了廢炸藥的再利用率,避免了環境汙染。國外常採用空化射流空心火箭發動機推進劑和空心不易熔化 組分 和索金 蠟製品 ...

    Java 並行程式設計 fork Join 框架解釋

    image.PNG分而治之演算法是一種解決問題的策略,它將乙個複雜的問題分解成幾個較小的 相似的子問題,遞迴地求解這些子問題,然後將這些子問題的解合併,得到原來問題的解。分而治之演算法的步驟通常由三個主要部分組成 分 將原始問題分解為一系列子問題。這些子問題應該更小,更容易解決原始問題的版本。征服 ...

    參考 java 中 void 的用法

    在 J A 程式語言中,void 是乙個特殊關鍵字,用於指示方法沒有返回值。此關鍵字在定義方法時使用,指示該方法不向呼叫方返回任何值。在方法主體中,可以呼叫其他方法並執行各種操作,但最終它們不會返回任何結果。下面是使用 void 關鍵字的示例 j a複製 在此示例中,我們定義了乙個名為sayhell...