Skip to content
This repository was archived by the owner on Jan 25, 2026. It is now read-only.

Commit f2eadd2

Browse files
authored
fix: ChaCha20 在旧平台上不支持 (#153)
1 parent 6e7abdb commit f2eadd2

File tree

5 files changed

+83
-113
lines changed

5 files changed

+83
-113
lines changed

Utils/Encryption/AesCbc.cs

Lines changed: 1 addition & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -49,112 +49,6 @@ public byte[] Encrypt(ReadOnlySpan<byte> data, ReadOnlySpan<byte> key)
4949
throw new NotSupportedException("You should no longer use AES-CBC as your encryption method");
5050
}
5151

52-
/// <summary>
53-
/// 使用特定的 AES 算法加密数据
54-
/// </summary>
55-
/// <param name = "input" > 需要加密的数据 </ param >
56-
/// < param name="key">密钥</param>
57-
/// <returns>Base64 编码的加密数据</returns>
58-
/// <exception cref = "ArgumentNullException" > 如果 key 为 null 或者空</exception>
59-
//private static string AesEncrypt(string input, string key)
60-
//{
61-
// if (string.IsNullOrEmpty(input))
62-
// return string.Empty;
63-
// if (string.IsNullOrEmpty(key))
64-
// throw new ArgumentNullException(nameof(key));
65-
66-
// using var aes = Aes.Create();
67-
// aes.KeySize = 256;
68-
// aes.BlockSize = 128;
69-
// aes.Mode = CipherMode.CBC;
70-
// aes.Padding = PaddingMode.PKCS7;
71-
// var salt = new byte[32];
72-
//#if NET6_0_OR_GREATER
73-
// using (var rng = RandomNumberGenerator.Create())
74-
//#else
75-
// using (var rng = new RNGCryptoServiceProvider())
76-
//#endif
77-
// {
78-
// rng.GetBytes(salt);
79-
// }
80-
81-
//#pragma warning disable SYSLIB0041
82-
// using (var deriveBytes = new Rfc2898DeriveBytes(key, salt, 1000))
83-
// {
84-
// aes.Key = deriveBytes.GetBytes(aes.KeySize / 8);
85-
// aes.GenerateIV();
86-
// }
87-
//#pragma warning restore SYSLIB0041
88-
89-
// using (var ms = new MemoryStream())
90-
// {
91-
// ms.Write(salt, 0, salt.Length);
92-
// ms.Write(aes.IV, 0, aes.IV.Length);
93-
94-
// using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
95-
// {
96-
// var data = Encoding.UTF8.GetBytes(input);
97-
// cs.Write(data, 0, data.Length);
98-
// }
99-
100-
// return Convert.ToBase64String(ms.ToArray());
101-
// }
102-
// }
103-
104-
/// <summary>
105-
/// 使用特定的 AES 算法解密数据
106-
/// </summary>
107-
/// <param name="input">Base64 编码的加密数据</param>
108-
/// <param name="key">密钥</param>
109-
/// <returns>返回解密文本</returns>
110-
/// <exception cref="ArgumentNullException">如果 Key 为 null 或空</exception>
111-
/// <exception cref="ArgumentException">如果 input 数据错误</exception>
112-
// private static string AesDecrypt(string input, string key)
113-
// {
114-
// if (string.IsNullOrEmpty(input))
115-
// return string.Empty;
116-
// if (string.IsNullOrEmpty(key))
117-
// throw new ArgumentNullException(nameof(key));
118-
119-
120-
// using var aes = Aes.Create();
121-
// aes.KeySize = 256;
122-
// aes.BlockSize = 128;
123-
// aes.Mode = CipherMode.CBC;
124-
// aes.Padding = PaddingMode.PKCS7;
125-
126-
// var encryptedData = Convert.FromBase64String(input);
127-
128-
// var salt = new byte[32];
129-
// Array.Copy(encryptedData, 0, salt, 0, salt.Length);
130-
131-
// var iv = new byte[aes.BlockSize / 8];
132-
// Array.Copy(encryptedData, salt.Length, iv, 0, iv.Length);
133-
// aes.IV = iv;
134-
135-
// if (encryptedData.Length < salt.Length + iv.Length)
136-
// {
137-
// throw new ArgumentException("加密数据格式无效或已损坏");
138-
// }
139-
140-
//#pragma warning disable SYSLIB0041
141-
// using (var deriveBytes = new Rfc2898DeriveBytes(key, salt, 1000))
142-
// {
143-
// aes.Key = deriveBytes.GetBytes(aes.KeySize / 8);
144-
// }
145-
//#pragma warning restore SYSLIB0041
146-
147-
// var cipherTextLength = encryptedData.Length - salt.Length - iv.Length;
148-
// using (var ms = new MemoryStream(encryptedData, salt.Length + iv.Length, cipherTextLength))
149-
// {
150-
// using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
151-
// {
152-
// using (var sr = new StreamReader(cs, Encoding.UTF8))
153-
// {
154-
// return sr.ReadToEnd();
155-
// }
156-
// }
157-
// }
158-
// }
52+
public bool IsSupported { get => false; }
15953
}
16054
}

Utils/Encryption/AesGcm.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Security.Cryptography;
3+
4+
namespace PCL.Core.Utils.Encryption
5+
{
6+
public class AesGcmProvider : IEncryptionProvider
7+
{
8+
public static AesGcmProvider Instance { get; } = new();
9+
10+
private const int NonceSize = 12; // 96 bits
11+
private const int TagSize = 16; // 128 bits
12+
13+
public byte[] Encrypt(ReadOnlySpan<byte> data, ReadOnlySpan<byte> key)
14+
{
15+
byte[] nonce = new byte[NonceSize];
16+
RandomNumberGenerator.Fill(nonce);
17+
18+
byte[] tag = new byte[TagSize];
19+
byte[] ciphertext = new byte[data.Length];
20+
21+
using (var aesGcm = new AesGcm(key, TagSize))
22+
{
23+
aesGcm.Encrypt(nonce, data, ciphertext, tag);
24+
}
25+
26+
// [Nonce(12)] [Tag(16)] [Ciphertext(n)]
27+
byte[] result = new byte[NonceSize + TagSize + ciphertext.Length];
28+
Buffer.BlockCopy(nonce, 0, result, 0, NonceSize);
29+
Buffer.BlockCopy(tag, 0, result, NonceSize, TagSize);
30+
Buffer.BlockCopy(ciphertext, 0, result, NonceSize + TagSize, ciphertext.Length);
31+
32+
return result;
33+
}
34+
35+
public byte[] Decrypt(ReadOnlySpan<byte> data, ReadOnlySpan<byte> key)
36+
{
37+
if (data.Length < NonceSize + TagSize)
38+
throw new ArgumentException("加密数据长度不足。");
39+
40+
ReadOnlySpan<byte> nonce = data.Slice(0, NonceSize);
41+
ReadOnlySpan<byte> tag = data.Slice(NonceSize, TagSize);
42+
ReadOnlySpan<byte> ciphertext = data.Slice(NonceSize + TagSize);
43+
44+
byte[] plaintext = new byte[ciphertext.Length];
45+
46+
using (var aesGcm = new AesGcm(key, TagSize))
47+
{
48+
aesGcm.Decrypt(nonce, ciphertext, tag, plaintext);
49+
}
50+
51+
return plaintext;
52+
}
53+
54+
public bool IsSupported { get => AesGcm.IsSupported; }
55+
}
56+
}

Utils/Encryption/ChaCha20.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,6 @@ private static void _DeriveKey(ReadOnlySpan<byte> ikm, ReadOnlySpan<byte> salt,
8080
salt,
8181
_Info.AsSpan());
8282
}
83+
84+
public bool IsSupported { get => ChaCha20Poly1305.IsSupported; }
8385
}

Utils/Encryption/IEncryptionProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ public interface IEncryptionProvider
66
{
77
public byte[] Encrypt(ReadOnlySpan<byte> data, ReadOnlySpan<byte> key);
88
public byte[] Decrypt(ReadOnlySpan<byte> data, ReadOnlySpan<byte> key);
9+
10+
public static bool IsSupported { get; }
911
}

Utils/Secret/EncryptHelper.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Security.Cryptography;
66
using System.Text;
77
using PCL.Core.IO;
8+
using PCL.Core.Logging;
89
using PCL.Core.Utils.Encryption;
910

1011
namespace PCL.Core.Utils.Secret;
@@ -14,13 +15,26 @@ public static class EncryptHelper
1415
public static string SecretEncrypt(string data)
1516
{
1617
var rawData = Encoding.UTF8.GetBytes(data);
17-
var encryptedData = ChaCha20.Instance.Encrypt(rawData, EncryptionKey);
18-
var storeData = new EncryptionData()
18+
byte[] encryptedData;
19+
uint version;
20+
if (ChaCha20.Instance.IsSupported)
1921
{
20-
Version = 1,
21-
Data = encryptedData
22-
};
23-
return Convert.ToBase64String(EncryptionData.ToBytes(storeData));
22+
encryptedData = ChaCha20.Instance.Encrypt(rawData, EncryptionKey);
23+
version = 1;
24+
}
25+
else if (AesGcmProvider.Instance.IsSupported)
26+
{
27+
encryptedData = AesGcmProvider.Instance.Encrypt(rawData, EncryptionKey);
28+
version = 2;
29+
}
30+
else
31+
{
32+
LogWrapper.Warn("Encryption", "No available encryption method");
33+
encryptedData = rawData;
34+
version = 0;
35+
}
36+
37+
return Convert.ToBase64String(EncryptionData.ToBytes(new EncryptionData { Version = version, Data = encryptedData })); ;
2438
}
2539

2640
public static string SecretDecrypt(string data)
@@ -33,7 +47,9 @@ public static string SecretDecrypt(string data)
3347
var encryptionData = EncryptionData.FromBytes(rawData);
3448
var decryptedData = encryptionData.Version switch
3549
{
50+
0 => rawData,
3651
1 => ChaCha20.Instance.Decrypt(encryptionData.Data, EncryptionKey),
52+
2 => AesGcmProvider.Instance.Decrypt(encryptionData.Data, EncryptionKey),
3753
_ => throw new NotSupportedException("Unsupported encryption version")
3854
};
3955
return Encoding.UTF8.GetString(decryptedData);

0 commit comments

Comments
 (0)