如何使用CryptoPP加载OpenSSH公私钥并转换为对应RSA密钥对象?
如何用CryptoPP加载OpenSSH格式的RSA密钥
你当前的代码无法直接加载OpenSSH密钥,因为CryptoPP的BERDecodePrivateKey仅支持标准PKCS#1/PKCS#8格式的密钥,而OpenSSH采用自定义封装格式,必须先解析提取出标准密钥数据才能加载。
私钥处理步骤
OpenSSH私钥(以-----BEGIN OPENSSH PRIVATE KEY-----开头)的结构是Base64编码的二进制数据,内部包含魔数、加密参数、公钥、私钥等字段。需按以下步骤提取PKCS#1格式的私钥数据:
- 去掉首尾的标记行与换行符,保留纯Base64字符串
- 对Base64字符串解码得到二进制数据
- 解析二进制数据:跳过开头的"openssh-key-v1"魔数,依次读取加密算法、加密参数、公钥数据,最终提取出私钥字段(PKCS#1格式的DER编码)
- 用
BERDecodePrivateKey加载提取出的私钥数据
示例代码(针对无密码保护的私钥):
#include <cryptopp/files.h> #include <cryptopp/rsa.h> #include <cryptopp/base64.h> #include <cryptopp/filters.h> #include <cryptopp/endian.h> #include <string> #include <fstream> using namespace CryptoPP; // 从OpenSSH私钥文件提取PKCS#1私钥数据 std::string ExtractOpenSSHPrivateKeyData(const std::string& filePath) { std::ifstream file(filePath); std::string line, base64Data; bool inKey = false; // 过滤并读取Base64内容 while (std::getline(file, line)) { if (line == "-----BEGIN OPENSSH PRIVATE KEY-----") { inKey = true; continue; } if (line == "-----END OPENSSH PRIVATE KEY-----") { inKey = false; break; } if (inKey) { base64Data += line; } } // Base64解码 std::string binaryData; StringSource(base64Data, true, new Base64Decoder(new StringSink(binaryData))); // 解析OpenSSH私钥结构 size_t pos = 0; // 跳过魔数 "openssh-key-v1" pos += 15; // 读取加密算法字段(先4字节大端长度,再内容) word32 algLen = BigEndianToNative32(*reinterpret_cast<const word32*>(binaryData.data() + pos)); pos += 4 + algLen; // 跳过加密密钥长度、加密IV长度字段 pos += 8; // 跳过公钥数据字段 word32 pubKeyLen = BigEndianToNative32(*reinterpret_cast<const word32*>(binaryData.data() + pos)); pos += 4 + pubKeyLen; // 读取私钥数据 word32 privKeyLen = BigEndianToNative32(*reinterpret_cast<const word32*>(binaryData.data() + pos)); pos += 4; std::string privKeyData(binaryData.data() + pos, privKeyLen); return privKeyData; } int main(int argc, char* argv[]) { try { // 提取PKCS#1私钥数据 std::string privKeyData = ExtractOpenSSHPrivateKeyData("/root/.ssh/id_rsa"); // 加载到CryptoPP私钥对象 RSA::PrivateKey privateKey; StringSource(privKeyData, true, new BERDecoder(privateKey, &RSA::PrivateKey::BERDecodePrivateKey, false, privKeyData.size())); // 导出标准格式公钥(可选) RSA::PublicKey publicKey(privateKey); FileSink pubSink("/root/.ssh/id_rsa.cryptopp.pub", true); publicKey.DEREncode(pubSink); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return 1; } return 0; }
公钥处理步骤
OpenSSH公钥(以ssh-rsa开头)的结构是:算法标识 + Base64编码的公钥数据。解码后的数据包含算法标识、指数(e)、模数(n),需提取这两个参数手动构建CryptoPP公钥:
示例代码:
#include <cryptopp/rsa.h> #include <cryptopp/base64.h> #include <cryptopp/filters.h> #include <cryptopp/endian.h> #include <string> #include <fstream> #include <sstream> using namespace CryptoPP; // 从OpenSSH公钥文件加载CryptoPP公钥 RSA::PublicKey LoadOpenSSHPublicKey(const std::string& filePath) { std::ifstream file(filePath); std::string line; std::getline(file, line); // 拆分算法标识与Base64数据 std::istringstream iss(line); std::string alg, base64Data; iss >> alg >> base64Data; // Base64解码 std::string binaryData; StringSource(base64Data, true, new Base64Decoder(new StringSink(binaryData))); size_t pos = 0; // 跳过算法标识字段 word32 algLen = BigEndianToNative32(*reinterpret_cast<const word32*>(binaryData.data() + pos)); pos += 4 + algLen; // 读取指数 word32 eLen = BigEndianToNative32(*reinterpret_cast<const word32*>(binaryData.data() + pos)); pos += 4; Integer e(reinterpret_cast<const byte*>(binaryData.data() + pos), eLen); pos += eLen; // 读取模数 word32 nLen = BigEndianToNative32(*reinterpret_cast<const word32*>(binaryData.data() + pos)); pos += 4; Integer n(reinterpret_cast<const byte*>(binaryData.data() + pos), nLen); // 构建公钥 RSA::PublicKey publicKey; publicKey.Initialize(n, e); return publicKey; } int main(int argc, char* argv[]) { try { RSA::PublicKey publicKey = LoadOpenSSHPublicKey("/root/.ssh/id_rsa.pub"); // 此处可添加加密/验证逻辑 } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return 1; } return 0; }
注意事项
- 若私钥带密码保护,需先根据加密算法(如AES)和参数用密码解密私钥字段
- 上述代码仅适配RSA密钥,其他密钥类型(如ED25519)需调整解析逻辑
内容的提问来源于stack exchange,提问作者Richard
相关产品推荐
相关产品推荐

