有一种算法可以为程序的离线激活生成许可证密钥。这意味着许可证信息直接编码在密钥中。要生成许可证密钥,需要使用应用程序的名称和版本,以及许可证所有者的姓名。几乎所有的代码都是从 Internet 上的各种来源收集的,并且在某种程度上适应了我的任务。我该怎么做和做什么:
许可证密钥生成
- 我使用程序的名称和版本创建了一个“盐”;
- 使用许可证所有者的名称作为解密许可证密钥的密码;
- 我使用先前创建的“salt”和密码创建 Rijndale - 许可证的所有者;
- 我将许可证信息加密为编码字节数组;
- 我将使用BASE36将生成的字节数组转换为人类可读的形式;
许可证解密
- 在程序方面,使用反射,我得到程序的名称和版本,以及许可证所有者;
- 根据收到的信息,我形成一个“盐”和一个密码;
- 我从BASE36格式的许可证密钥中取回一组编码字节;
- 使用之前创建的“salt”和密码创建 Rijndale ;
- 我将编码字节数组解码回许可证信息;
问题是我只能用这种方法加密长度在 16 到 31 个字符之间的字符串。如何针对不同长度的字符串修改现有代码?
PS:您可以在此处使用代码“玩耍” 。
BASE36 类
using System;
using System.Text;
namespace ConsoleApp1
{
public static class BASE36
{
private const string _charList = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static readonly char[] _charArray = _charList.ToCharArray();
public static long Decode(string input)
{
long _result = 0;
double _pow = 0;
for (int _i = input.Length - 1; _i >= 0; _i--)
{
char _c = input[_i];
int pos = _charList.IndexOf(_c);
if (pos > -1)
{
_result += pos * (long)Math.Pow(_charList.Length, _pow);
}
else
{
return -1;
}
_pow++;
}
return _result;
}
public static string Encode(ulong input)
{
var _sb = new StringBuilder();
do
{
_sb.Append(_charArray[input % (ulong)_charList.Length]);
input /= (ulong)_charList.Length;
} while (input != 0);
return Reverse(_sb.ToString());
}
private static string Reverse(string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
}
}
节目班
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
string saltString = GetSaltString("Blechexport", "15");
byte[] salt = Encoding.UTF8.GetBytes(saltString);
string pwd = "J.Doe";
string licInfo = $"[0,{new DateTime(2019, 11, 30).Ticks},10]";
var bytes = Encoding.UTF8.GetBytes(licInfo);
var seconds = new DateTime(2019, 12, 31).Ticks / 10000000;
string[] testDatas = new string[]{
$"[0,{seconds},1]",
$"[1,{seconds},10]",
$"[2,{seconds},10,10]",
$"[3,{seconds},10,10,10]",
$"[3,{seconds},10,10,10,10]",
$"[3,{seconds},10,10,10,10,10]"
// throws System.Security.Cryptography.CryptographicException
// ,$"[3,{seconds},10,10,10,10,10,10]"
};
foreach (string testData in testDatas)
{
TestLic(pwd, salt, testData);
}
Console.ReadKey();
}
static void TestLic(string pwd, byte[] salt, string phrase)
{
var bytes = Encoding.UTF8.GetBytes(phrase);
Console.WriteLine("------------------------------------------");
Console.WriteLine($"phrase: {phrase} ({phrase.Length} symbols)");
string licKey;
using (var rijndael = InitSymmetric(Rijndael.Create(), pwd, salt, 256))
{
byte[] encryptedBytes = Transform(bytes, rijndael.CreateEncryptor);
licKey = GenerateUID(encryptedBytes);
Console.WriteLine($"encrypted bytes: {BitConverter.ToString(encryptedBytes)}");
Console.WriteLine($"license key: {licKey}");
Console.WriteLine($"key length: {licKey.Length}");
}
using (var rijndael = InitSymmetric(Rijndael.Create(), pwd, salt, 256))
{
byte[] uidBytes = GetUIDInBytes(licKey);
byte[] decryptedBytes = Transform(uidBytes, rijndael.CreateDecryptor);
Console.WriteLine($"decrypted bytes: {BitConverter.ToString(decryptedBytes)}");
string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
Console.WriteLine("decryptedText: {0}", decryptedText);
}
Console.WriteLine("------------------------------------------");
}
static string GetSaltString(string programmName, string programmVersion)
{
return $"{programmName}.{programmVersion}";
}
static string GenerateUID(byte[] bytes)
{
//Convert checksum into 4 ulong parts and use BASE36 to encode both
string _part1Id = BASE36.Encode(BitConverter.ToUInt64(bytes, 0));
string _part2Id = BASE36.Encode(BitConverter.ToUInt64(bytes, 8));
string _part3Id = BASE36.Encode(BitConverter.ToUInt64(bytes, 16));
string _part4Id = BASE36.Encode(BitConverter.ToUInt64(bytes, 24));
//Concat these 4 part into one string
return string.Format("{0}-{1}-{2}-{3}", _part1Id, _part2Id, _part3Id, _part4Id);
}
static byte[] GetUIDInBytes(string UID)
{
//Split 4 part Id into 4 ulong
string[] _ids = UID.Split('-');
//Combine 4 part Id into one byte array
byte[] _value = new byte[32];
Buffer.BlockCopy(BitConverter.GetBytes(BASE36.Decode(_ids[0])), 0, _value, 0, 8);
Buffer.BlockCopy(BitConverter.GetBytes(BASE36.Decode(_ids[1])), 0, _value, 8, 8);
Buffer.BlockCopy(BitConverter.GetBytes(BASE36.Decode(_ids[2])), 0, _value, 16, 8);
Buffer.BlockCopy(BitConverter.GetBytes(BASE36.Decode(_ids[3])), 0, _value, 24, 8);
return _value;
}
static byte[] EncryptStringToBytes_Aes(string plainText, byte[] Key, byte[] IV)
{
// Check arguments.
if (string.IsNullOrEmpty(plainText))
{
throw new ArgumentNullException(nameof(plainText));
}
if (Key == null || Key.Length == 0)
{
throw new ArgumentNullException(nameof(Key));
}
if (IV == null || IV.Length == 0)
{
throw new ArgumentNullException(nameof(IV));
}
byte[] encrypted;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create an encryptor to perform the stream transform.
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for encryption.
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
// Return the encrypted bytes from the memory stream.
return encrypted;
}
static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
{
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
{
throw new ArgumentNullException(nameof(cipherText));
}
if (Key == null || Key.Length <= 0)
{
throw new ArgumentNullException(nameof(Key));
}
if (IV == null || IV.Length <= 0)
{
throw new ArgumentNullException(nameof(IV));
}
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an AesManaged object
// with the specified key and IV.
using (AesManaged aesAlg = new AesManaged())
{
aesAlg.Key = Key;
aesAlg.IV = IV;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
return plaintext;
}
static SymmetricAlgorithm InitSymmetric(SymmetricAlgorithm algorithm, string password, byte[] salt, int keyBitLength)
{
const int Iterations = 234;
using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
{
if (!algorithm.ValidKeySize(keyBitLength))
{
throw new InvalidOperationException("Invalid size key");
}
algorithm.Key = rfc2898DeriveBytes.GetBytes(keyBitLength / 8);
algorithm.IV = rfc2898DeriveBytes.GetBytes(algorithm.BlockSize / 8);
return algorithm;
}
}
static byte[] Transform(byte[] bytes, Func<ICryptoTransform> selectCryptoTransform)
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, selectCryptoTransform(), CryptoStreamMode.Write))
{
cryptoStream.Write(bytes, 0, bytes.Length);
}
return memoryStream.ToArray();
}
}
}
}
感谢tym32167和Zergatul的评论,该问题已得到解决,并且已修改以下方法以处理任意长度的字符串:
我很乐意接受任何建设性的意见和建议!
PS:和以前一样,您可以在这里“玩”更正和工作的代码。