.NET 內建 RSACryptoServiceProvider 僅支援「公鑰加密、私鑰解密」(數位信封) 與「私鑰簽名、公鑰驗簽」。若需「私鑰加密、公鑰解密」(如授權檔產生、客戶端驗證授權),需自行實作 RSA 原始運算。
p, q → n = p*q, φ(n) = (p-1)(q-1) → 選 e (通常 65537) 與 φ(n) 互質 → 計算 d ≡ e⁻¹ (mod φ(n))c ≡ mᵉ (mod n)m ≡ cᵈ (mod n)
public struct BigInteger
{
private readonly uint[] _data; // little-endian 32-bit limbs
private readonly int _sign; // 1, 0, -1
// 建構子、隱含轉換、ToString(16/10)、位運算、加減乘除、ModPow、ModInverse、GCD...
// 完整實作約 300-500 行,此處略過,重點在使用層面
}
※ 生產環境請直接使用 .NET Core 2.1+ 內建 System.Numerics.BigInteger,效能經過原生優化並通過 FIPS 驗證。
using System;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
public class CustomRSA
{
public BigInteger N { get; private set; } // 模數
public BigInteger E { get; private set; } // 公鑰指數
public BigInteger D { get; private set; } // 私鑰指數
// --- 靜態工廠 ---
public static CustomRSA Generate(int keySize = 2048)
{
using var rsa = RSA.Create(keySize);
var param = rsa.ExportParameters(true);
return new CustomRSA
{
N = new BigInteger(param.Modulus.Reverse().ToArray()), // BigInteger 是 little-endian
E = new BigInteger(param.Exponent.Reverse().ToArray()),
D = new BigInteger(param.D.Reverse().ToArray())
};
}
public static CustomRSA FromParameters(BigInteger n, BigInteger e, BigInteger d) => new() { N = n, E = e, D = d };
// --- 核心運算 ---
private byte[] ProcessBlock(byte[] block, BigInteger exp)
{
var m = new BigInteger(block.Concat(new byte[] { 0 }).ToArray()); // 確保正數
var c = BigInteger.ModPow(m, exp, N);
var bytes = c.ToByteArray(true, true); // little-endian, unsigned
return bytes;
}
// --- 公鑰加密 / 私鑰解密 (標準用法) ---
public byte[] EncryptPublic(string plainText, Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
byte[] data = encoding.GetBytes(plainText);
return ProcessData(data, E, 245); // 2048-bit key: max 245 bytes per block (PKCS#1 v1.5 padding 需預留 11 bytes)
}
public string DecryptPrivate(byte[] cipherText, Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
byte[] data = ProcessData(cipherText, D, 256);
return encoding.GetString(data);
}
// --- 私鑰加密 / 公鑰解密 (簽名/授權檔場景) ---
public byte[] EncryptPrivate(string plainText, Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
byte[] data = encoding.GetBytes(plainText);
return ProcessData(data, D, 245);
}
public string DecryptPublic(byte[] cipherText, Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
byte[] data = ProcessData(cipherText, E, 256);
return encoding.GetString(data);
}
// --- 分塊處理 (無 Padding 教學版,生產必須加 OAEP/PKCS#1 v1.5) ---
private byte[] ProcessData(byte[] data, BigInteger exp, int blockSize)
{
using var ms = new MemoryStream();
for (int i = 0; i < data.Length; i += blockSize)
{
int len = Math.Min(blockSize, data.Length - i);
byte[] block = new byte[len];
Buffer.BlockCopy(data, i, block, 0, len);
byte[] processed = ProcessBlock(block, exp);
// 固定輸出長度 (256 bytes for 2048-bit)
if (processed.Length < 256) Array.Resize(ref processed, 256);
ms.Write(processed, 0, 256);
}
return ms.ToArray();
}
}
// 產生金鑰對
var rsa = CustomRSA.Generate(2048);
Console.WriteLine($"N: {rsa.N.ToString("X")}");
Console.WriteLine($"E: {rsa.E.ToString("X")}");
Console.WriteLine($"D: {rsa.D.ToString("X")}");
// 標準加解密
string secret = "機密資料";
byte[] enc = rsa.EncryptPublic(secret);
string dec = rsa.DecryptPrivate(enc);
Console.WriteLine($"解密: {dec}"); // 機密資料
// 私鑰加密、公鑰解密 (授權檔)
string license = "USER=John;EXP=2025-12-31";
byte[] licEnc = rsa.EncryptPrivate(license);
string licDec = rsa.DecryptPublic(licEnc);
Console.WriteLine($"授權驗證: {licDec}");
| 風險 | 說明 | 對策 |
|---|---|---|
| 無 Padding | 教學碼直接對明文模冪,極不安全 (可被選定明文攻擊、同態性攻擊) | 生產必須使用 OAEP Padding (加密) / PSS Padding (簽名) |
| 時序攻擊 | ModPow 執行時間隨私鑰位元而異 | 使用常數時間實作或 .NET 內建 RSA 類別 |
| 金鑰長度 | 1024-bit 已不安全 | 最小 2048-bit,建議 3072/4096-bit |
| 私鑰儲存 | 明文儲存 D 參數極危險 | 使用 RSA.ExportEncryptedPkcs8PrivateKey、HSM、Azure Key Vault、DPAPI |
理解 RSA 數學原理與 BigInteger.ModPow 核心運算,有助於除錯互通問題、實作跨平台授權驗證、教學示範。**生產系統嚴禁使用無 Padding 的教學級實作**,請直接呼叫 .NET 內建 RSA / ECDsa 或成熟函式庫 (BouncyCastle, libsodium.net)。