C#生成putty格式的ppk文件(支持passphrase)

来源:https://www.cnblogs.com/bossma/archive/2022/06/30/16428919.html
-Advertisement-
Play Games

進入移動互聯網時代以來,Windows桌面開發已經很久不碰了。之前就是從做Windows開發入行的。 當年,還是C++ VC6, MFC的時代。那時候開發要查的是MSDN :-)。記憶體要自己管理, 排查記憶體泄漏(忘了釋放分配的記憶體)也是基本日常。光陰似箭,歲月如梭~! 幾年之前,北漂時需要寫一個wi ...


背景

2022國家級護網行動即將開啟,根據阿裡雲給出的安全建議,需要將登陸Linux的方式改為密鑰對方式。我這裡使用的遠程工具是自己開發的,能夠同時管理Windows和Linux,但是以前不支持密鑰對的登陸方式,所以需要改造一下。

護網行動是什麼?護網行動從2016年開始,是一場由公安部組織的網路安全攻防演練,目的是針對全國範圍的真實網路目標為對象的實戰攻防活動,旨在發現、暴露和解決安全問題,檢驗我國各大企事業單位、部屬機關的網路安全防護水平和應急處置能力。護網行動每年舉辦一次,為期2-3周。

我使用的遠程工具 RDManager:https://blog.bossma.cn/tools/new-version-of-rdmanager-replace-poderosa-with-putty/,這個工具訪問Linux使用了putty,putty的密鑰對登陸方式使用的是自有格式的ppk文件,但是阿裡雲上下載的是pem格式的密鑰文件,所以需要將pem格式轉換為ppk格式。

思路

putty本身提供了一個工具,可以將其他格式的密鑰文件轉換為自有的ppk文件,這個工具的名字是puttygen。在linux上可以通過命令進行轉換,在Windows上則必須使用GUI工具手動操作,這多有不便。我期望的是能通過編程的方式進行這個轉換,這樣只需要在RDManger中上傳pem文件,就可以自動轉換為putty的ppk格式的文件,不需要再去使用puttygen。

首先查了下有沒有現成的輪子,經過多次尋找,在Github上找到了一個項目:pem2ppk (https://github.com/akira345/pem2ppk),這個項目看名字就知道很貼合我的需求,它的主要功能就是讀取pem文件,然後輸出為ppk文件。我最終的解決方案主體也是從此而來。不過這個程式有兩個問題:

  • 1、不是所有的pem文件都能轉換成功,網上也是有人說成功了,有人說不行。
  • 2、不支持對密鑰進行加密,別人拿走了這個ppk文件就可以直接使用。puttygen是有這個功能的。

除此之外,很難再找到比較貼合需求的資料了。怎麼辦?其實這個Github項目的很大一部分代碼來源於另一篇文章:https://antonymale.co.uk/generating-putty-key-files.html,作者提到可以去看putty的源碼。

受此啟發,我也可以去看putty的源碼,然後將相關處理翻譯為C#的實現,這樣應該是可以解決問題的。

實現

putty的源碼官網上就可以下載到,不過我看的是一個幾年前的版本:https://github.com/KasperDeng/putty,這個版本和新版本的主要邏輯都是一樣的,搞懂C語言的若幹函數和數據類型就很容易理解,而且舊版本更原始,沒有那麼多的抽象,反而更容易理解。

輸出ppk內容不正確的問題

這個問題主要是由於填充(padding)使用不當造成的,pem2ppk項目在輸出密鑰的各個屬性時都使用了前置填充,而putty並不是固定的都加了填充。

看putty的代碼實現:https://github.com/KasperDeng/putty/blob/037a4ccb6e731fafc4cc77c0d16f80552fd69dce/putty-src/sshrsa.c#L654

    dlen = (bignum_bitcount(rsa->private_exponent) + 8) / 8;
    plen = (bignum_bitcount(rsa->p) + 8) / 8;
    qlen = (bignum_bitcount(rsa->q) + 8) / 8;
    ulen = (bignum_bitcount(rsa->iqmp) + 8) / 8;
    bloblen = 16 + dlen + plen + qlen + ulen;

這段代碼是計算密鑰的各個屬性的值的位元組數,然後用於初始化一個大的位元組數組,將這些數據寫進去。bignum_bitcount是計算值的比特位數,除以8就是得到位元組數,為什麼還要加8呢?這是因為C語言中除法的結果是向下取整的,比如數學計算結果是1.5,那麼C語言中得到的就是1,為了不讓任何一個比特丟失,所以這裡加了一個8,預留好充足的空間。

再來看pem2ppk中的實現:https://github.com/akira345/pem2ppk/blob/d2baee08064953280984607d1e4ae1183127e5ad/PEM2PPK/PuttyKeyFileGenerator.cs#L24

private const int prefixSize = 4;
private const int paddedPrefixSize = prefixSize + 1;
byte[] publicBuffer = new byte[3 + keyType.Length + paddedPrefixSize + keyParameters.Exponent.Length +
                                           paddedPrefixSize + keyParameters.Modulus.Length + 1];

這裡keyParameters.Exponent和keyParameters.Modulus是公鑰的兩個屬性,可以看到前邊加了一個固定的長度paddedPrefixSize,這個paddedPrefixSize=prefixSize + 1,這裡邊的1就對應putty中的+8邏輯。

不過固定+1是有問題的,可以想一下C#和C語言在處理這些屬性值時的差別。

在putty中如果數據比特數不能被8整除,那麼+8之後再整除就可以得到正確的位元組數,否則就會少1個位元組;如果數據能被8整除,那麼+8就會多1個空的位元組,這個多的位元組就是padding了。所以能被8整除的時候才會有這個padding。

在C#中開始處理的時候就已經都是位元組了,所以C#中不需要處理位數不能被8整除的問題,但是需要在能被8整除的時候增加一個空位元組,C#中如何判斷數據的位數能被8整除呢?可以認為數據首byte的最高位是1的時候,比特數就能被8整數,此時最小二進位數是10000000,比它小的數就可以被捨棄掉至少1位。10000000也就是128,因此凡是大於等於這個數的都是能被8整數的,也就是需要padding的。

所以可以這樣判斷是否需要增加padding:https://gist.github.com/bosima/ee6630d30b533c7d7b2743a849e9b9d0#file-puttykeyfilegenerator-cs-L190

	private static bool CheckIsNeddPadding(byte[] bytes)
	{
		return bytes[0] >= 128;
	}

	private static int GetPrefixSize(byte[] bytes)
	{
		return CheckIsNeddPadding(bytes) ? paddedPrefixSize : prefixSize;
	}

實現ppk加密

pem2ppk項目中沒有對key進行加密的實現,網上也沒有找到C#的源代碼可以實現這個功能。但是這個功能很關鍵,在RDManager中所有的密碼都是加密處理的,這樣伺服器賬號落盤的時候安全性才能有比較好的保障,但是阿裡雲導出的pem是沒有加密的,雖然puttygen也可以給pem加密,但是還不是不能將加密以編程的方式集成到RDManager中。

解決這個問題的方式還是搬運putty的實現方式,將C語言的實現轉換為C#的實現。其中有兩個關鍵的處理:一是要在計算Private-MAC的值時給私鑰增加padding,二是使用AES256進行加密處理。至於putty為什麼要這樣處理,我沒有研究,只是照搬過來。

主要看下AES256加密的處理,有些參數很關鍵:

byte[] passKey = new byte[40];
...
byte[] iv = new byte[16];
byte[] aesKey = new byte[32];
Buffer.BlockCopy(passKey, 0, aesKey, 0, 32);
using (RijndaelManaged rijalg = new RijndaelManaged())
{
	rijalg.BlockSize = 128;
	rijalg.KeySize = 256;
	rijalg.Padding = PaddingMode.None;
	rijalg.Mode = CipherMode.CBC;
	rijalg.Key = aesKey;
	rijalg.IV = iv;

	ICryptoTransform encryptor = rijalg.CreateEncryptor(rijalg.Key, rijalg.IV);
	return encryptor.TransformFinalBlock(bytes, 0, bytes.Length);
}
  • iv是長度為16的位元組數組,裡邊都是預設值0。
  • aeskey是一個長度為32的位元組數組,不過計算的時候準備的是長度為40的位元組數組,需要截一下。
  • Padding需要設置為PaddingMode.None,預設的不是這個。

其它就沒什麼好說的了。

為了不那麼單調,來一張RDManger的使用界面:


以上就是本文的主要內容了。

完整代碼在Github,歡迎訪問:https://gist.github.com/bosima/ee6630d30b533c7d7b2743a849e9b9d0


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Java基礎之 註釋、標識符、關鍵字 註釋、標識符、關鍵字 數據類型 類型轉換 變數、常量 運算符 包機制、JavaDoc 註釋 在代碼量比較多,項目結構複雜起來,我們就需要用到註釋。 註釋不會被執行,是給寫代碼的人看的 書寫註釋是一個非常好的習慣 在Java中的註釋有三種: 單行註釋 多行註釋 文 ...
  • 現在有個需求是接收base64編碼的圖片,然後上傳到文件伺服器上,由於文件服務的代碼是固定的代碼不能修改,所以只能適配介面。文件服務代碼如下: @FeignClient(value = "base-files", configuration = SpringMultipartEncoder.clas ...
  • Hi,大家好,我是Mic。 一個工作了5年的粉絲私信我,最近面試碰到很多Redis相關的問題。 其中一個面試官問他Redis裡面的持久化機制,沒有回答得很好。 希望我幫他系統回答一下。 關於Redis裡面的RDB和AOF兩種持久化機制的原理和優缺點這個問題。 下麵看看普通人和高手的回答。 普通人: ...
  • 通常在讀寫文件之前,需要判斷文件或目錄是否存在,不然某些處理方法可能會使程式出錯。所以最好在做任何操作之前,先判斷文件是否存在。 這裡將介紹三種判斷文件或文件夾是否存在的方法,分別使用os模塊、Try語句、pathlib模塊。 1.使用os模塊 os模塊中的os.path.exists()方法用於檢 ...
  • JAVA編碼中存在一些容易被人忽視的陷阱,稍不留神可能就會跌落其中,給項目的穩定運行埋下隱患。此外,這些陷阱也是面試的時候面試官比較喜歡問的問題。 本文對這些陷阱進行了統一的整理,讓你知道應該如何避免落入陷阱中,下麵就一起來瞭解下吧。 迴圈中操作目標list 遍歷List然後對list中符合條件的元 ...
  • 筆者使用的開發環境是IntelliJ IDEA Community Edition 2021.3.3 scala版本是2.11.8,與Spark2.0嚴格對應。 (1)在maven環境中導入scala框架 導入Scala框架 導入成功後在main/java 路徑中可以創建新的scala類 至此,sc ...
  • 前言 聚類問題是無監督學習的問題,演算法思想就是物以類聚,人以群分,聚類演算法感知樣本間的相似度,進行類別歸納,對新輸入進行輸出預測,輸出變數取有限個離散值。本次我們使用兩種方法對鳶尾花數據進行聚類。 無監督就是沒有標簽的進行分類 K-means 聚類演算法 K-means聚類演算法(k-均值或k-平均)聚 ...
  • 來源:cnblogs.com/youzhibing/p/15354706.html 疑慮背景 疑慮描述 最近,在進行開發的過程中,發現之前的一個寫法,類似如下: 以我的理解,@Configuration 加 @Bean 會創建一個 userName 不為 null 的 UserManager 對象, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...