// net 层 RSA 加密器测试(OpenSSL 3.x EVP API)。 // 用临时生成的 RSA-2048 密钥对自测,不依赖实站公钥: // 1) 用 EVP_RSA_gen(2048) 生成密钥对; // 2) 导出公钥 PEM,喂给 RsaEncryptor 加密 + base64; // 3) 用同一密钥对的私钥解密密文,断言能还原原文。 // PKCS#1 v1.5 填充与 JSEncrypt 默认一致。 #include #include #include #include #include #include #include #include #include #include "crypto/RsaEncryptor.hpp" namespace { // RAII 包装:保证测试里 OpenSSL 句柄不泄漏。 struct PkeyDeleter { void operator()(EVP_PKEY* p) const noexcept { if (p) EVP_PKEY_free(p); } }; struct PkeyCtxDeleter { void operator()(EVP_PKEY_CTX* c) const noexcept { if (c) EVP_PKEY_CTX_free(c); } }; struct BioDeleter { void operator()(BIO* b) const noexcept { if (b) BIO_free(b); } }; using PkeyPtr = std::unique_ptr; using PkeyCtxPtr = std::unique_ptr; using BioPtr = std::unique_ptr; // base64 解码(处理尾部 padding),用于长度断言。 std::vector base64Decode(const std::string& b64) { std::vector out(b64.size()); // 解码后必不超过输入长度 int decoded = EVP_DecodeBlock(out.data(), reinterpret_cast(b64.data()), static_cast(b64.size())); if (decoded < 0) return {}; // EVP_DecodeBlock 按 4 字节对齐解码,会把 '=' 当作 0;需按 padding 数扣除尾部字节。 size_t pad = 0; if (!b64.empty() && b64[b64.size() - 1] == '=') ++pad; if (b64.size() >= 2 && b64[b64.size() - 2] == '=') ++pad; out.resize(static_cast(decoded) - pad); return out; } // 用私钥(kp)以 PKCS#1 v1.5 解密 cipher。 std::string decryptWithPrivateKey(EVP_PKEY* kp, const std::vector& cipher) { PkeyCtxPtr ctx(EVP_PKEY_CTX_new(kp, nullptr)); EXPECT_NE(ctx, nullptr); EXPECT_GT(EVP_PKEY_decrypt_init(ctx.get()), 0); EXPECT_GT(EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING), 0); size_t outlen = 0; EXPECT_GT(EVP_PKEY_decrypt(ctx.get(), nullptr, &outlen, cipher.data(), cipher.size()), 0); std::vector plain(outlen); EXPECT_GT(EVP_PKEY_decrypt(ctx.get(), plain.data(), &outlen, cipher.data(), cipher.size()), 0); return std::string(reinterpret_cast(plain.data()), outlen); } std::string exportPublicKeyPem(EVP_PKEY* kp) { BioPtr bio(BIO_new(BIO_s_mem())); EXPECT_NE(bio, nullptr); EXPECT_GT(PEM_write_bio_PUBKEY(bio.get(), kp), 0); char* data = nullptr; long len = BIO_get_mem_data(bio.get(), &data); return std::string(data, static_cast(len)); } } // namespace TEST(RsaEncryptorTest, EncryptsToBase64DecryptableByPrivateKey) { // Arrange: 临时生成 RSA-2048 密钥对,导出公钥 PEM。 PkeyPtr kp(EVP_RSA_gen(2048)); ASSERT_NE(kp, nullptr); const std::string pubPem = exportPublicKeyPem(kp.get()); ASSERT_FALSE(pubPem.empty()); const std::string plaintext = "hello-geopro"; // Act: 加密 + base64。 geopro::net::RsaEncryptor enc(pubPem); const std::string b64 = enc.encryptBase64(plaintext); // Assert: base64 解码后是 256 字节密文(RSA-2048)。 const std::vector cipher = base64Decode(b64); EXPECT_EQ(cipher.size(), 256u); // Assert: 私钥能解出原文(即便长度断言放宽,这条是核心正确性保证)。 const std::string recovered = decryptWithPrivateKey(kp.get(), cipher); EXPECT_EQ(recovered, plaintext); } TEST(RsaEncryptorTest, ThrowsOnInvalidPublicKey) { EXPECT_THROW(geopro::net::RsaEncryptor("not-a-valid-pem"), std::runtime_error); } TEST(RsaEncryptorTest, MoveConstructionPreservesEncryption) { PkeyPtr kp(EVP_RSA_gen(2048)); ASSERT_NE(kp, nullptr); const std::string pubPem = exportPublicKeyPem(kp.get()); geopro::net::RsaEncryptor original(pubPem); geopro::net::RsaEncryptor moved(std::move(original)); const std::string plaintext = "move-check"; const std::string b64 = moved.encryptBase64(plaintext); const std::vector cipher = base64Decode(b64); EXPECT_EQ(cipher.size(), 256u); EXPECT_EQ(decryptWithPrivateKey(kp.get(), cipher), plaintext); }