changhong_newclient/wb_unity_pro/Assets/Scripts/RSAHelper.cs

236 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Text;
using System.Security.Cryptography;
public static class RSAHelper
{
private static string publicKey =
"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmZLmb4paD13E2uOueFStT1g8OnA4JQYkN4PZl/il7Nb05EbZrRbQz/5VbiZxc+S4asfFrrU0d++gjiluiAbBBhgtQSi+eNZuJ2wrBxjvN3W9d14XMmZKDg21Knu7tDUwOlEfCPVMZ/nJ+9YU6mnEvme29UgpI4RbEIIWPuDW3DavbaOvZ/cYP+0m2pxU3mETwDbVpbM3LimJ4JOBIs7XhV2UqEUfRVCYiuZUkydOCs1TgTJRJC5qgYpx4wUZ0ja7t9jP3klg/vjjyA1SDnC98bQLR5QzVJE1LvI2Rr3AHfZS0hckg0WAR87D1R8sGjmQ0TUi4qanpQYhEL4dBH8a6TX/xssobM7M+XgFsGiT4fWG18JhJM98rhVEk2AUaXgnFNf3OyLP+NuYrqjYamuug00s2azCl/rQCkvnLWO4W+lPn27ZinnBRg1+r2fNws7dPPa/8tWScRcTtfaidYsQP2lnYJvFgc299HdQL7iFgL336NihUxll09Hhm7vf8+N0ZP5TLE8SRoSfHNgHup6k9YG5e+V9bW4VckQFgPPBlHSS0mDEOvD9d54+F1cBg/sG8XMduW5WhfroYsMbRzB9a4rIfctn6yqpM+ot2EW8CKgiOVM5lJQhBwX5rMKnMTaApt6EIigY6jRVFNCOtQPDZ9prLBsp3asQxf7Vg9kfrVUCAwEAAQ==";
private static string privateKey =
"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";
// 公钥加密
public static string Encrypt(string plainText)
{
using (RSACryptoServiceProvider rsa = RSAHelper.CreateFromBase64PublicKey(publicKey))
{
byte[] data = Encoding.UTF8.GetBytes(plainText);
byte[] enc = rsa.Encrypt(data, false);
Debugger.Log(Convert.ToBase64String(rsa.Encrypt(Encoding.UTF8.GetBytes("{\"password\":\"111111\",\"deviceCode\":\"281a228fac1c29d25c1a3e572b15ad47d5f66811\",\"id\":777777}"),false)));
return Convert.ToBase64String(enc);
}
}
/// <summary>
/// 将 Base64 公钥DER 格式)转换成 RSACryptoServiceProvider 可用的 XML 公钥
/// </summary>
/// <param name="base64PublicKey">Base64 编码的 DER 公钥</param>
/// <returns>RSACryptoServiceProvider 实例</returns>
private static RSACryptoServiceProvider CreateFromBase64PublicKey(string base64PublicKey)
{
byte[] keyBytes = Convert.FromBase64String(base64PublicKey);
// 解析 DER 格式的公钥,获取 Modulus 和 Exponent
int offset = 0;
if (keyBytes[offset++] != 0x30) throw new Exception("Invalid public key format");
ReadLengthPublic(keyBytes, ref offset); // 跳过序列长度
// 跳过算法标识部分
if (keyBytes[offset++] != 0x30) throw new Exception("Invalid public key format");
ReadLengthPublic(keyBytes, ref offset);
offset += 13; // rsaEncryption OID + NULL
if (keyBytes[offset++] != 0x03) throw new Exception("Invalid public key format");
int bitStringLength = ReadLengthPublic(keyBytes, ref offset);
offset++; // unused bits
if (keyBytes[offset++] != 0x30) throw new Exception("Invalid public key format");
ReadLengthPublic(keyBytes, ref offset);
// 读取 Modulus
if (keyBytes[offset++] != 0x02) throw new Exception("Invalid public key format");
int modulusLength = ReadLengthPublic(keyBytes, ref offset);
byte[] modulus = new byte[modulusLength];
Array.Copy(keyBytes, offset, modulus, 0, modulusLength);
offset += modulusLength;
// 读取 Exponent
if (keyBytes[offset++] != 0x02) throw new Exception("Invalid public key format");
int exponentLength = ReadLengthPublic(keyBytes, ref offset);
byte[] exponent = new byte[exponentLength];
Array.Copy(keyBytes, offset, exponent, 0, exponentLength);
// 构造 XML
string xml = $"<RSAKeyValue><Modulus>{Convert.ToBase64String(modulus)}</Modulus><Exponent>{Convert.ToBase64String(exponent)}</Exponent></RSAKeyValue>";
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml);
return rsa;
}
private static int ReadLengthPublic(byte[] data, ref int offset)
{
int length = data[offset++];
if ((length & 0x80) == 0x80)
{
int bytesCount = length & 0x7F;
length = 0;
for (int i = 0; i < bytesCount; i++)
{
length = (length << 8) | data[offset++];
}
}
return length;
}
/// <summary>
/// 从 Base64 私钥PKCS#1 或 PKCS#8得到 RSACryptoServiceProvider适用于 Unity2019.4
/// </summary>
public static RSACryptoServiceProvider LoadPrivateKeyFromBase64(string base64)
{
if (string.IsNullOrEmpty(base64)) throw new ArgumentNullException(nameof(base64));
byte[] data = Convert.FromBase64String(base64);
// 先尝试解析为 PKCS#8PrivateKeyInfo -> OCTET STRING 包 PKCS#1
try
{
byte[] pkcs1 = TryExtractPkcs1FromPkcs8(data);
return LoadPkcs1(pkcs1);
}
catch (Exception)
{
// 如果不是 PKCS#8再尝试直接解析 PKCS#1
return LoadPkcs1(data);
}
}
private static byte[] TryExtractPkcs1FromPkcs8(byte[] data)
{
int offset = 0;
// Expect: SEQUENCE
if (data[offset++] != 0x30) throw new Exception("Not a valid ASN.1 SEQUENCE (PKCS#8)");
ReadLength(data, ref offset); // skip top-level length
// Optional version INTEGER (usually present)
if (data[offset] == 0x02)
{
offset++; // tag
int verLen = ReadLength(data, ref offset);
offset += verLen;
}
// Next should be algorithmIdentifier (SEQUENCE) — skip it properly
if (data[offset] != 0x30) throw new Exception("Invalid PKCS#8, missing alg sequence");
offset++; // tag
int algLen = ReadLength(data, ref offset);
offset += algLen;
// Next should be OCTET STRING (privateKey)
if (data[offset++] != 0x04) throw new Exception("Invalid PKCS#8, missing privateKey OCTET STRING");
int pkcs1Len = ReadLength(data, ref offset);
if (offset + pkcs1Len > data.Length) throw new Exception("Invalid lengths in PKCS#8");
byte[] inner = new byte[pkcs1Len];
Buffer.BlockCopy(data, offset, inner, 0, pkcs1Len);
return inner;
}
private static RSACryptoServiceProvider LoadPkcs1(byte[] data)
{
int offset = 0;
if (data[offset++] != 0x30) throw new Exception("Invalid PKCS#1: no SEQUENCE");
ReadLength(data, ref offset);
// version INTEGER (skip)
if (data[offset] == 0x02)
{
offset++; // tag
int vlen = ReadLength(data, ref offset);
offset += vlen;
}
RSAParameters p = new RSAParameters();
p.Modulus = ReadInteger(data, ref offset, "Modulus");
p.Exponent = ReadInteger(data, ref offset, "Public Exponent");
p.D = ReadInteger(data, ref offset, "Private Exponent");
p.P = ReadInteger(data, ref offset, "P");
p.Q = ReadInteger(data, ref offset, "Q");
p.DP = ReadInteger(data, ref offset, "DP");
p.DQ = ReadInteger(data, ref offset, "DQ");
p.InverseQ = ReadInteger(data, ref offset, "InverseQ");
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(p);
return rsa;
}
private static byte[] ReadInteger(byte[] data, ref int offset, string nameForError = null)
{
if (data[offset] != 0x02)
{
// 报错时输出附近几个字节,便于调试
string hexSnippet = GetHexSnippet(data, offset, 12);
throw new Exception($"Expected INTEGER at offset {offset} ({nameForError ?? ""}), but found 0x{data[offset]:X2}. Nearby: {hexSnippet}");
}
offset++; // skip INTEGER tag
int len = ReadLength(data, ref offset);
// 如果有前导 0x00, 去掉(去除符号位)
if (len > 0 && data[offset] == 0x00)
{
offset++;
len--;
}
if (len < 0 || offset + len > data.Length) throw new Exception("Invalid integer length");
byte[] value = new byte[len];
Buffer.BlockCopy(data, offset, value, 0, len);
offset += len;
return value;
}
private static int ReadLength(byte[] data, ref int offset)
{
if (offset >= data.Length) throw new Exception("Invalid offset while reading length");
int length = data[offset++];
if ((length & 0x80) == 0) // short form
{
return length;
}
int bytesCount = length & 0x7F;
if (bytesCount <= 0 || bytesCount > 4) throw new Exception("Invalid length bytes");
if (offset + bytesCount > data.Length) throw new Exception("Invalid length (overflow)");
int val = 0;
for (int i = 0; i < bytesCount; i++)
{
val = (val << 8) | data[offset++];
}
return val;
}
private static string GetHexSnippet(byte[] data, int offset, int maxLen)
{
int start = Math.Max(0, offset - 6);
int end = Math.Min(data.Length, offset + maxLen);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = start; i < end; i++)
{
sb.AppendFormat("{0:X2} ", data[i]);
}
return sb.ToString().Trim();
}
// 私钥解密
public static string Decrypt(string cipherText)
{
using (RSACryptoServiceProvider rsa = RSAHelper.LoadPrivateKeyFromBase64(privateKey))
{
byte[] data = Convert.FromBase64String(cipherText);
byte[] dec = rsa.Decrypt(data, false);
return Encoding.UTF8.GetString(dec);
}
}
}