RSA 算法的 C語言實現通常比較復雜,但已經有許多密碼算法庫實現了 RSA 算法,例如OpenSSL、Libgcrypt? 和 Botan ?等。我們可以在這些庫的基礎上進行配置或移植,從而快速實現密碼算法。但這些庫主要面向大量設備進行優化,如通用計算機和服務器。本文需要在嵌入式設備上實現密碼算法,因此選擇了實現開銷更小的密碼算法庫 BearSSL。BearSSL包含RSA的兩種加密方案,分別是PKCS#1 v1.5方案和RSAES-OAEP方案,PKCS#1 v1.5方案是早期方案,存在安全漏洞,RSAES-OAEP使用OAEP填充技術,安全性高。BearSSL還包含兩種RSA簽名方案,分別是RSASSA-PKCS1-v1_5方案和RSASSA-PSS方案,RSASSA-PKCS1-v1_5是早期方案,有安全漏洞,RSASSA-PSS是目前推薦方案,使用PSS填充技術,引入隨機鹽值,安全性高。另外,BearSSL還包含還包括一些分組密碼加密和雜湊算法相關內容。本文僅進行測試演示,從BearSSL中提取出了容易實現的RSASSA-PKCS1-v1_5方案,雖然該方案存在漏洞,但因此實現簡便,資源占用更小,但可以用于安全要求不太高的小型設備,或作為教學使用。我們刪減了除RSASSA-PKCS1-v1_5方案外的大量冗余內容,使工程大幅簡化,下載地址為:https://download.csdn.net/download/weixin_43261410/91277142,讀者可以免費下載。
一、BearSSL 密碼算法庫
BearSSL是一個輕量級、高性能的SSL/TLS加密庫,專為嵌入式系統和資源受限環境設計。與其他通用加密庫(如OpenSSL)不同,BearSSL的核心目標是極致的精簡與高效,同時不犧牲安全性。它采用模塊化設計,允許開發者僅編譯所需的加密算法,從而大幅減少代碼體積(可小至50KB以下)和內存占用(棧內存通常低于3KB)。這種設計使其非常適合運行在微控制器(如ARM Cortex-M)、物聯網設備(IoT)或實時操作系統中。此外,BearSSL嚴格遵循恒定時間編程原則,所有算法均避免數據依賴的分支和內存訪問,有效抵御側信道攻擊(如時序攻擊)。這種對安全性和資源效率的平衡,使其成為嵌入式安全通信的首選庫之一。
BearSSL在技術實現上有多項創新,尤其是其分層加密算法支持和大整數運算優化。例如,它提供多種大整數實現(i15、i31、i62),分別針對不同硬件平臺優化:i15適用于無硬件乘法指令的CPU(如Cortex-M0),i31利用32位CPU的64位乘法加速運算,而i62則針對64位架構(如x86-64)進一步優化模冪運算。此外,BearSSL采用純C語言編寫,無需匯編代碼,確保了跨平臺兼容性。其API設計也極具特色,例如通過“控制位”(ctl參數)實現恒定時間的條件操作,避免分支預測漏洞。這些設計使其在保持高度可移植性的同時,仍能實現接近硬件的性能。
BearSSL在安全性上毫不妥協,全面支持現代TLS協議(如TLS 1.2和1.3),并實現了前向保密(PFS)和抗降級攻擊的機制。其密碼套件默認禁用弱算法(如RC4、SHA-1),且支持證書鏈驗證和OCSP裝訂(OCSP Stapling)。與其他庫不同,BearSSL的證書解析器極度精簡,僅處理必要的X.509字段,既減少了代碼體積,又降低了潛在漏洞風險。此外,它通過了多項密碼學標準的驗證(如FIPS 140-2的算法測試),并提供了針對側信道攻擊的防護措施(如盲簽名、恒定時間模冪運算)。這些特性使其在工業控制、醫療設備等對安全性要求嚴苛的場景中備受青睞。
二、僅提取i31位的PKCS1簽名與驗簽方法
i31實現是BearSSL中平衡性能和代碼大小的優化選擇,使用31位無符號整數作為基本運算單元。這種設計充分利用了32位CPU的寄存器寬度,同時避免了符號位的潛在問題。在示例工程中,我們通過精心挑選的源文件實現了最小化的RSA PKCS#1功能,僅包含i31相關運算和必要的輔助函數。
簽名過程遵循PKCS#1 v1.5規范,首先使用br_rsa_pkcs1_sig_pad對哈希值進行編碼,添加ASN.1算法標識符和填充字節。示例中使用BR_HASH_OID_SHA256常量指定SHA-256算法,這比硬編碼OID更安全可靠。填充后的消息隨后通過br_rsa_i31_private函數進行實際簽名運算,該函數采用CRT優化顯著提升性能。
驗簽過程則相反,先用br_rsa_i31_public解密簽名,再用br_rsa_pkcs1_sig_unpad驗證填充格式并提取哈希值。值得注意的是,示例中嚴格檢查了所有函數的返回值,這是安全編程的關鍵實踐。驗簽最后一步將提取的哈希值與原始哈希比較,確保內容的完整性和真實性。
工程中的params.h文件包含了完整的RSA-1024密鑰對,采用中國剩余定理格式存儲。私鑰包含p、q、dp、dq和iq等CRT參數,這些參數預先計算并存儲,可顯著提升簽名速度。公鑰部分則包含標準的模數n和公開指數e(通常為65537)。這種密鑰表示方式與BearSSL的br_rsa_private_key和br_rsa_public_key結構體完美匹配,便于直接使用。
三、整體工程架構分析
工程采用清晰的模塊化結構,通過CMake構建系統管理。頂層CMakeLists.txt明確定義了所有源文件和頭文件的依賴關系,確保構建過程的可重復性。項目結構分為include和src兩個主要目錄,前者存放BearSSL頭文件和應用特定的params.h,后者包含精選的i31實現源文件。
核心功能集中在main.c中,該文件實現了完整的簽名驗簽工作流。簽名函數sign_data初始化私鑰結構并調用br_rsa_i31_pkcs1_sign;驗簽函數verify_signature類似地配置公鑰后調用br_rsa_i31_pkcs1_vrfy。這種封裝使主邏輯清晰可讀,同時便于復用。密鑰材料與業務邏輯分離的設計增強了安全性,方便后續密鑰輪換。
構建系統僅包含實現RSA PKCS#1簽名驗簽所需的最小源文件集,如i31_*.c基礎運算和rsa_i31_*.c專門實現。這種精確的組件選擇體現了BearSSL的模塊化優勢,最終生成的可執行文件體積顯著小于包含完整庫的情況。settings.c提供必要的運行時配置,而ccopy.c等輔助函數則確保安全的內存操作。
測試方面,工程使用固定的測試向量進行基礎驗證。main函數中硬編碼的SHA-256哈希值作為輸入,通過打印簽名前后數據提供了簡單的視覺驗證手段。雖然這不能替代完整的測試套件,但對于演示和基礎驗證已經足夠。實際項目中可擴展為自動化單元測試,覆蓋更多邊界情況和錯誤路徑。
四、測試函數實現
測試實現采用了經典的測試模式 - 先執行簽名,再驗證簽名,最后比較結果。main函數中首先打印原始哈希值建立基線,然后調用sign_data生成簽名。簽名失敗會立即終止程序并提示錯誤,成功則輸出128字節的簽名數據,每32字節換行以便閱讀。
驗簽階段將簽名作為輸入,調用verify_signature函數。該函數不僅檢查簽名本身的合法性,還通過memcmp確保解出的哈希與原始哈希完全一致。這種雙重驗證保證了整個流程的正確性。測試輸出包含明確的成功/失敗指示和詳細的十六進制數據,極大便利了調試過程。
測試使用的固定哈希值對應字符串"BearSSL RSA Test"的SHA-256摘要,這提供了確定性的測試基準。實際應用中,哈希值通常來自對實際消息的摘要計算。示例中簽名緩沖區(signature)大小固定為128字節(1024位),與密鑰長度匹配;哈希輸出緩沖區(verify_hash_out)則為32字節,符合SHA-256的輸出要求。
錯誤處理方面,代碼檢查了所有關鍵操作的返回值,包括簽名和驗簽函數的輸出。這種防御性編程風格對于安全關鍵代碼至關重要。雖然示例中沒有實現復雜的錯誤恢復機制,但簡單的立即返回已能防止錯誤傳播。擴展測試可添加無效簽名、錯誤密鑰等負面測試案例,進一步驗證代碼的健壯性。
最后,我們將參數保存在了params.h文件中,這是原工程沒有的。密鑰參數生成和SHA256的哈希計算可采用工程scripts目錄下的兩個腳本實現,注意需要在python中通過命令pip install cryptography安裝庫后運行。
#include <stdio.h>
#include <string.h>
#include "bearssl_rsa.h"
#include "params.h"unsigned char hash_value[] = {0x54, 0xba, 0x1f, 0xdc, 0xe5, 0xa8, 0x9e, 0x0d,0x3e, 0xee, 0x6e, 0x4c, 0x58, 0x74, 0x97, 0x83,0x3b, 0xc3, 0x8c, 0x35, 0x86, 0xff, 0x02, 0x05,0x7d, 0xd6, 0x45, 0x1f, 0xd2, 0xd6, 0xb6, 0x40
};int sign_data(unsigned char *signature) {br_rsa_private_key sk;sk.n_bitlen = 1024;sk.p = RSA_P;sk.plen = sizeof(RSA_P);sk.q = RSA_Q;sk.qlen = sizeof(RSA_P);sk.dp = RSA_DP;sk.dplen = sizeof(RSA_DP);sk.dq = RSA_DQ;sk.dqlen = sizeof(RSA_DQ);sk.iq = RSA_IQ;sk.iqlen = sizeof(RSA_IQ);uint32_t sign_result = br_rsa_i31_pkcs1_sign(BR_HASH_OID_SHA256,hash_value,sizeof(hash_value),&sk,signature);return sign_result != 0;
}int verify_signature(const unsigned char *signature, size_t sig_len, unsigned char *hash_out) {br_rsa_public_key pk;pk.n = RSA_N;pk.nlen = sizeof(RSA_N);pk.e = RSA_E;pk.elen = sizeof(RSA_E);uint32_t verify_result = br_rsa_i31_pkcs1_vrfy(signature,sig_len,BR_HASH_OID_SHA256,sizeof(hash_value),&pk,hash_out);if (!verify_result) {return 0;}return memcmp(hash_out, hash_value, sizeof(hash_value)) == 0;
}int main() {unsigned char signature[128] = {0};unsigned char verify_hash_out[32] = {0};printf("The original hash is: \n");for (int i = 0; i < 32; i++) {printf("%02x", hash_value[i]);}printf("\n");if (!sign_data(signature)) {printf("Failed sign!\n");return -1;}printf("Success sign, the signature is: \n");for (int i = 0; i < 128; i++) {printf("%02x", signature[i]);if((i+1)%32 ==0)printf("\n");}if (!verify_signature(signature, 128, verify_hash_out)) {printf("Failed verify!\n");return -1;}printf("Success verify, the hash out is: \n");for (int i = 0; i < 32; i++) {printf("%02x", verify_hash_out[i]);}printf("\n");return 0;
}
五、總結
本文詳細介紹了基于BearSSL輕量級加密庫實現RSA-PKCS#1數字簽名與驗簽的全過程。BearSSL以其模塊化設計和嵌入式友好特性,成為資源受限環境下安全通信的理想選擇。文章重點剖析了i31位優化實現的RSA算法,該實現通過31位整數運算單元在32位CPU上達到性能與代碼大小的最佳平衡。工程采用精簡化架構,僅集成必要的i31運算模塊,通過CMake構建系統實現高效管理。測試方案采用確定性測試向量,完整演示了從私鑰簽名到公鑰驗簽的閉環流程,包含嚴格的返回值檢查和內存比較驗證。整個實現充分展現BearSSL的安全設計哲學:恒定時間算法防御旁路攻擊、顯式內存管理避免動態分配、最小化API降低誤用風險。該方案特別適合物聯網設備等嵌入式場景,為開發者提供了兼具安全性和效率的輕量級密碼學實踐范例,其模塊化思想也可擴展至其他加密算法實現。