.NET 平台 SM2 国密算法 License 证书生成深度解析

License 授权机制构思

.NET 中实现 License 授权机制,可以通过以下步骤进行:

1.确定授权方式

  • 基于时间的授权:限制软件的使用时间。
  • 基于功能的授权:根据用户购买的版本,开放不同的功能。
  • 基于并发数的授权:限制同时使用软件的用户或设备数量。

2.生成许可证文件

  • 包含信息:用户信息、授权类型、有效期、签名等。
  • 使用加密算法:采用非对称加密算法(如RSA)对许可证文件进行签名,防止改。

3.验证许可证

  • 解析许可证文件:读取并解析许可证中的信息。
  • 验证签名:使用公钥验证许可证的签名,确保其合法性。
  • 检查授权条件:根据许可证中的信息,检查授权是否有效。

4.集成到应用程序

  • 启动时验证:在应用程序启动时验证许可证,如果无效则提示用户或限制功能。
  • 提供管理界面:允许用户输入许可证密钥或查看授权信息。

注意事项

  • 安全性:确保许可证文件的安全性,防止被非法获取或算改。
  • 用户体验:在授权验证失败时,提供友好的提示信息,避免影响用户体验。
  • 灵活性:根据实际需求,设计灵活的授权机制,方便后续扩展和维护。

通过以上步骤,可以在 .NET 应用程序中实现一个基本的 License 授权机制,确保软件的合法使用。

继续完善授权机制

为了完善 .NET 中的 License 授权机制,使其包含时间效期、应用功能和并发数限制等,您可以按照以下步骤进行:

说明:实际业务场景请具体问题考虑,这里只是抛砖引玉。

1.许可证文件结构

设计一个包含以下信息的许可证文件格式:

  • 时间效期:许可证的有效开始日期和结束日期。
  • 应用功能:列出可用的功能模块或权限级别。
  • 并发数限制:允许同时使用的最大用户数或设备数。

2.生成许可证文件

在生成许可证文件时,将这些信息嵌入到文件中,并使用加密算法进行签名。

3.验证许可证

在验证许可证时,除了检查签名和时间效期外,还需要检查功能和并发数限制:

  • 功能验证:根据许可证中的功能列表,检查用户是否有权限使用特定功能。
  • 并发数验证:维护一个计数器或记录,跟踪当前使用的用户或设备数量,确保不超过许可证允许的并发数。

4.集成到应用程序

  • 启动时验证:在应用程序启动时,验证许可证的时间效期和并发数限制。
  • 功能访问控制:在用户尝试访问特定功能时,验证其是否在许可证允许的功能列表中。
  • 并发数管理:在用户登录或启动新实例时,检查并发数限制,并在用户注销或实例关闭时更新计数器。

注意事项

  • 同步和并发管理:确保在多用户或多线程环境中,并发数的计数和管理是同步和准确的。
  • 动态更新:考虑实现许可证的动态更新机制,允许在不影响用户使用的情况下更新许可证信。
  • 息日志记录:记录许可证验证和使用的日志,便于追踪和审计。

通过以上步骤,您可以实现一个包含时间效期、应用功能和并发数限制的完整 License 授权机制。

授权证书文件格式介绍

授权证书生成文件的后缀通常取决于其编码格式和用途。
以下是一些常见的授权证书文件后缀:

  • .pemPrivacy Enhanced Mail 的缩写,采用 Base64 编码的ASCIl 文本格式,可包含证书.私钥或两者皆有。
  • .derDistinguished Encoding Rules 的缩写,二进制编码格式,常用于存储 X.509 证书。
  • .crtCertificate 的缩写,常用于存储公钥证书,格式可以是PEMDER
  • .cer:与 .crt 类似,用于存储证书,编码格式通常为 DER
  • .pfxPersonal Information Exchange 的缩写,包含证书和私钥的二进制格式,常用于 Windows 平台,可通过密码保护。
  • .p12:与 .pfx 相同,是 PKCS #12 标准的另一种扩展名。
  • .jksJava KeyStore 的缩写,用于存储证书和私钥,常用于Java 平台。
  • .licLicense 的缩写,特定软件或系统的授权证书文件,格式和内容自定义。

这些后缀代表了不同的证书格式和编码方式,在实际应用中,应根据具体需求和场景选择合适的文件格式。

BouncyCastle.Cryptography 包介绍

一个开源的密码学库,提供广泛的加密算法和协议实现,支持 .NET 平台。
BouncyCastle.Cryptography

核心功能

1.算法支持

  • 对称加密(如 AES、DES
  • 非对称加密(如 RSA、SM2
  • 数字签名(如 ECDSA、SM3WithSM2
  • 哈希算法(如 SHA-256、SM3
  • 国密算法(如 SM2、SM3、SM4

2.证书与密钥管理

  • X.509 证书解析/生成
  • 密钥对(AsymmetricCipherKeyPair)生成与存储
  • PKCS#1/PKCS#8 密钥格式支持

3.协议支持

  • TLS/SSL 相关功能
  • CMS(加密消息语法)

4.适用场景

  • 需要兼容国密算法(如 SM2/SM3/SM4)的项目
  • 自定义证书签名/验证流程(如您代码中的 License 签名)
  • 低层级密码学操作(如手动处理密钥与加密流程)

在项目中安装使用:

# 通过 NuGet 安装
Install-Package BouncyCastle.Cryptography

注意事项

  • 版本兼容性:确保选择与目标 .NET 版本兼容的包版本(当前最新为 2.2.1+)。
  • 性能:相比 .NET 内置加密库(如 System.Security.Cryptography),某些场景可能性能较低。
  • 文档:官方文档较少,建议参考 源码示例

为啥选择 SM2 国密算法?

在选择签名算法时,此处本人采用 SM2 国密算法,原因如下:

安全性

  • 基于椭圆曲线密码学:SM2 算法的安全性基于椭圆曲线离散对数问题,具有较高的安全强度。256 位的 SM2 密码强度已经比2048 位的 RSA 密码强度要高。
  • 国家密码管理局认可:SM2 算法是中国国家密码管理局批准的公钥密码算法,经过严格的审查和测试,符合国家安全标准。

效率

  • 签名速度快:相较于 RSA 算法,SM2 算法在签名和密钥生成速度上更快,特别是在处理大量数据时,效率优势明显。
  • 密钥长度短: SM2 算法使用较短的密钥长度(256位),减少了存储和传输的开销。

适用性

  • 广泛应用:SM2 算法适用于数字签名、密钥交换和数据加密等多种场景,满足多种密码应用的安全需求。
  • 支持国密标准:SM2 算法与 SM3 哈希算法结合使用,符合中国国密标准,适用于需要遵循国内安全标准的系统和应用。

兼容性

  • 开源支持:许多开源密码学库(如 BouncyCastle )已经支持 SM2 算法,便于集成和开发。
  • 硬件支持:国内许多加密芯片和硬件设备支持 SM2 算法,提供硬件级的加速和安全保障。

综上所述,SM2 算法在安全性、效率和适用性方面都表现出色,是签名国密算法的理想选择。

基于 SM2 国密算法如何使用 .net 实现 License 证书?

为了方便演示,本人此处使用新建 .net9 控制台应用程序,并添加依赖包:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
  </ItemGroup>

</Project>

项目(LicenseDemo)结构如下所示:

LicenseDemo

为了使用 SM2 算法完善签名并生成 .lic 授权证书,您可以按照以下步骤进行:

1. 生成 SM2 密钥对

首先,您需要生成 SM2 算法的公钥和私钥。可以使用 C# 中的BouncyCastle.Cryptography 库来实现。

C# 中,ECKeyGenerationParameters 类用于生成椭圆曲线密钥对时指定参数。选择椭圆曲线参数集,这里使用的是 "sm2p256v1",然后创建一个安全的随机数生成器 SecureRandom(),接着实例化 ECKeyGenerationParameters

创建 ECKeyPairGenerator 实例,初始化密钥生成器,然后调用 GenerateKeyPair() 方法,即可生成 SM2 算法的密钥对。

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Asn1.GM;
using System.Text;

namespace LicenseDemo.Licenses;

/// <summary>
/// 生成SM2密钥对
/// </summary>
class Sm2KeyPairGenerator
{
    #region 单例模式
    // 使用 Lazy<T> 实现懒加载
    private static readonly Lazy<Sm2KeyPairGenerator> _instance = new(() => new Sm2KeyPairGenerator());

    // 私有构造函数,防止外部实例化
    private Sm2KeyPairGenerator() { }

    // 公共静态属性,用于访问单例实例
    public static Sm2KeyPairGenerator Instance => _instance.Value;
    #endregion

    #region 密钥对生成算法
    // 使用【sm2p256v1】曲线,获取SM2曲线的X9ECParameters对象
    private static readonly X9ECParameters x9ECParameters = GMNamedCurves.GetByName("sm2p256v1");

    // 使用X9ECParameters对象创建ECDomainParameters对象
    private static readonly ECDomainParameters domainParams = new(
        x9ECParameters.Curve,
        x9ECParameters.G,
        x9ECParameters.N,
        x9ECParameters.H,
        x9ECParameters.GetSeed()
    );

    private static readonly ECKeyPairGenerator generator = new();

    /// <summary>
    /// 生成SM2密钥对(公钥,私钥)
    /// </summary>
    /// <returns></returns>
    public AsymmetricCipherKeyPair GenerateKeyPair()
    {
        generator.Init(new ECKeyGenerationParameters(domainParams, new SecureRandom()));
        return generator.GenerateKeyPair();
    }
    #endregion

    /// <summary>
    /// 测试,输出SM2密钥对信息
    /// </summary>
    public void KeyPairInfo()
    {
        StringBuilder strBuilder = new(128);

        strBuilder.AppendLine("Hello, SM2KeyPairGenerator!");
        // 输出曲线名称
        strBuilder.AppendLine("Curve Name: sm2p256v1");

        // 输出曲线参数
        strBuilder.AppendLine($"Curve: {x9ECParameters.Curve}");
        strBuilder.AppendLine($"G:{x9ECParameters.G}");
        strBuilder.AppendLine($"N:{x9ECParameters.N}");
        strBuilder.AppendLine($"H:{x9ECParameters.H}");

        byte[]? seed = x9ECParameters.GetSeed();
        if (seed != null)
        {
            strBuilder.AppendLine($"Seed:{BitConverter.ToString(seed)}");
        }

        // 输出ECDomainParameters对象
        strBuilder.AppendLine($"ECDomainParameters: {domainParams}");
        Console.WriteLine(strBuilder.ToString());
    }
}

2. 生成 SM2 签名/验签 & 加密/解密

  • 签名 & 验签

在生成密钥对后,您可以使用私钥对数据进行签名,

// 使用SM2私钥对数据进行签名
public byte[] SignData(byte[] data, AsymmetricKeyParameter privateKey)
{
    var signer = SignerUtilities.GetSigner("SM3WITHSM2");
    signer.Init(true, privateKey);
    signer.BlockUpdate(data, 0, data.Length);
    return signer.GenerateSignature();
}

验证签名时,使用对应的公钥:

// 使用SM2公钥对数据进行验签
public bool VerifyData(byte[] data, byte[] signature, AsymmetricKeyParameter publicKey)
{
    var verifier = SignerUtilities.GetSigner("SM3WITHSM2");
    verifier.Init(false, publicKey);
    verifier.BlockUpdate(data, 0, data.Length);
    return verifier.VerifySignature(signature);
}
  • 加密 & 解密

除了上面的签名和验签之外,我们还可以利用公钥加密,私钥解密。

// 使用SM2公钥加密
public string Encrypt(string plainText, AsymmetricKeyParameter publicKey)
{
    SM2Engine engine = new();
    engine.Init(true, new ParametersWithRandom(publicKey, new SecureRandom()));
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
    byte[] encryptedBytes = engine.ProcessBlock(plainTextBytes, 0, plainTextBytes.Length);
    return Convert.ToBase64String(encryptedBytes);
}

// 使用SM2私钥解密
public string Decrypt(string encryptedText, AsymmetricKeyParameter privateKey)
{
    SM2Engine engine = new();
    engine.Init(false, privateKey);
    byte[] encryptedBytes = Convert.FromBase64String(encryptedText);
    byte[] decryptedBytes = engine.ProcessBlock(encryptedBytes, 0, encryptedBytes.Length);
    return Encoding.UTF8.GetString(decryptedBytes);
}

3. 定义 License 数据结构

License 数据结构是针对应用授权的定义,根据自己实际业务情况,可以自定义扩展定义。

/// <summary>
/// License 数据
/// </summary>
class LicenseData
{
    public string Id => Guid.NewGuid().ToString();
    public string AppName { get; set; } = null!;
    public string AppVersion { get; set; } = null!;
    public string AuthCode { get; set; } = null!;
    public string UserName { get; set; } = null!;
    public string CompanyName { get; set; } = null!;
    public DateTimeOffset StartDate { get; set; }
    public DateTimeOffset ExpirationDate { get; set; }
    public List<string> AllowedFeatures { get; set; } = [];
}

4. 定义 License 上下文对象

上下文对象用于承载证书数据的形态,但为了安全性保障,该上下文对象会被再次加密,然后将密文写入 .lic 文件中。

/// <summary>
/// License 上下文
/// </summary>
internal class LicenseContent
{
    /// <summary>
    /// 数据
    /// </summary>
    public string Data { get; set; } = null!;

    /// <summary>
    /// 签名
    /// </summary>
    public string Signature { get; set; } = null!;
}

上面基本信息铺垫后,接下来我们开始实现 License 证书的生成与校验。

5. 实现 License 授权助手

  • 在生成 .lic 授权证书时,您可以将签名数据和其他相关信息(如用户信息、授权类型、时间限制等)一起包含在证书中;
  • 在生成 .lic 授权证书时,您需要提取数据和签名,然后使用公钥进行验证;
using Org.BouncyCastle.Crypto;
using System.Text.Json;
using System.Text;

namespace LicenseDemo.Licenses;

/// <summary>
/// license 授权助手
/// </summary>
/// <param name="publicKey">公钥</param>
/// <param name="privateKey">私钥</param>
class LicenseAssistant(AsymmetricCipherKeyPair keyPair)
{
    private readonly Sm2Assistant _sm2Assistant = new();

    private AsymmetricKeyParameter PublicKey => keyPair.Public;
    private AsymmetricKeyParameter PrivateKey => keyPair.Private;

    /// <summary>
    /// 生成License文件(.lic授权证书)
    /// </summary>
    /// <param name="licenseData"></param>
    /// <param name="outputFile"></param>
    /// <returns></returns>
    public async Task GenerateLicenseFileAsync(LicenseData licenseData, string outputFile)
    {
        // 序列化数据
        string jsonString = await JsonSerializeAsync(licenseData);

        // 对数据进行签名
        byte[] dataToSign = Encoding.UTF8.GetBytes(jsonString);
        byte[] signature = _sm2Assistant.SignData(dataToSign, PrivateKey);

        // 组合数据和签名
        LicenseContent licenseContent = new()
        {
            Data = jsonString,
            Signature = Convert.ToBase64String(signature)
        };

        // 异步序列化 JSON 数据
        string license = await JsonSerializeAsync(licenseContent);

        // 公钥加密
        string licenseCiphertext = _sm2Assistant.Encrypt(license, PublicKey);

        // 写入文件
        await File.WriteAllTextAsync(outputFile, licenseCiphertext);
    }

    /// <summary>
    /// 验证License文件(.lic授权证书)
    /// </summary>
    /// <param name="licenseFilePath"></param>
    /// <returns></returns>
    public async Task<bool> VerifyLicenseFileAsync(string licenseFilePath)
    {
        // 读取文件内容
        string licenseCiphertext = await File.ReadAllTextAsync(licenseFilePath);

        // 私钥解密
        string licenseContent = _sm2Assistant.Decrypt(licenseCiphertext, PrivateKey);

        // 异步反序列化 JSON 数据
        var licenseData = await JsonDeserializeAsync<LicenseContent>(licenseContent);

        // 解析数据和签名
        string data = licenseData.Data;
        byte[] signature = Convert.FromBase64String(licenseData.Signature);

        // 验证签名
        byte[] verify = Encoding.UTF8.GetBytes(data);
        return _sm2Assistant.VerifyData(verify, signature, PublicKey);
    }

    /// <summary>
    /// 序列化数据
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    /// <param name="data"></param>
    /// <returns></returns>
    private async Task<string> JsonSerializeAsync<TData>(TData data)
    {
        using var stream = new MemoryStream();
        await JsonSerializer.SerializeAsync(stream, data);
        stream.Position = 0;
        using var reader = new StreamReader(stream);
        return await reader.ReadToEndAsync();
    }

    /// <summary>
    /// 反序列化数据
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    /// <param name="data"></param>
    /// <returns></returns>
    private async Task<TData> JsonDeserializeAsync<TData>(string data)
    {
        if (string.IsNullOrWhiteSpace(data))
            return default!;

        // 解析数据和签名
        byte[] jsonBytes = Encoding.UTF8.GetBytes(data);
        using var stream = new MemoryStream(jsonBytes);

        // 将 JSON 字符串写入内存流
        //await stream.WriteAsync(jsonBytes, 0, jsonBytes.Length);
        //stream.Position = 0;

        // 异步反序列化 JSON 数据
        return await JsonSerializer.DeserializeAsync<TData>(stream);
    }
}

应用程序中如何使用?

Program.cs 文件中添加如下代码:

// See https://aka.ms/new-console-template for more information
using LicenseDemo.Licenses;
using System.Diagnostics;
using System.Text;

{
    // 1.生成SM2密钥对
    var sm2KeyPairGenerator = Sm2KeyPairGenerator.Instance;
    sm2KeyPairGenerator.KeyPairInfo();
    var keyPair = sm2KeyPairGenerator.GenerateKeyPair();
    Console.WriteLine($"public => {keyPair.Public}");
    Console.WriteLine($"private => {keyPair.Private}");

    // 2.使用SM2签名数据 & 验证签名
    Sm2Assistant sm2Assistant = new();
    var data = Encoding.UTF8.GetBytes("hello");
    var signature = sm2Assistant.SignData(data, keyPair.Private);
    Console.WriteLine($"sign => {signature}");
    var verify = sm2Assistant.VerifyData(data, signature, keyPair.Public);
    Console.WriteLine($"verify => {verify}");

    // 3.创建许可证数据
    var licenseData = new LicenseData
    {
        AppName = "MyApp",
        AppVersion = "1.0.0",
        AuthCode = "1234567890",
        UserName = "John Doe",
        CompanyName = "ACME Corp",
        StartDate = DateTime.UtcNow,
        ExpirationDate = DateTime.UtcNow.AddDays(30),
        AllowedFeatures = ["PremiumFeature1", "PremiumFeature2"]
    };

    // 4. 实例化 License 助手
    LicenseAssistant licenseAssistant = new(keyPair);

    // 4.1 设置 .lic 证书存放路径
    string targetDirectory = "lics";
    Directory.CreateDirectory(targetDirectory);
    Console.WriteLine($"Project Folder Path: {targetDirectory}");
    string outputFilePath = Path.Combine(targetDirectory, "license.lic");

    // 4.2 生成许可证文件
    await licenseAssistant.GenerateLicenseFileAsync(licenseData, outputFilePath);

    // 4.3 验证许可证文件
    bool isValid = await licenseAssistant.VerifyLicenseFileAsync(outputFilePath);
    Console.WriteLine($"License is valid: {isValid}");  
}

注意事项

  • 密钥管理:确保私钥的安全存储,避免泄露。
  • 安全性:考虑使用其他安全措施,如加密数据、添加时间戳等,增强证书的安全性。
  • 可读性:虽然加密和签名增加了安全性,但也要确保生成的可读性。

通过上述步骤,您可以使用 SM2 算法完善签名并生成 .lic 授权证书,确保数据和签名的安全性和完整性。

生成 License 证书性能测试

为了更好的利用计算机资源,此处使用异步多线程方式生成多个 .lic 文件。

说明:性能测试基准,生成 10000 个许可证文件,记录生成耗时。

方法一:使用 Parallel.For

这里使用了 async 关键字,但 Parallel.For 是为同步代码设计的。虽然它支持 async,但可能会导致一些潜在的问题,比如任务调度不当或资源浪费。

int licenseCount = 10000; // 指定 10000 个许可证文件
{
    Stopwatch stopwatch = Stopwatch.StartNew();
    await Task.Run(() =>
    {
        Parallel.For(0, licenseCount, async i =>
        {
            LicenseData licenseData = new()
            {
                AppName = "MyApp",
                AppVersion = "1.0.0",
                AuthCode = "123456",
                UserName = "John Doe",
                CompanyName = "Acme Inc.",
                StartDate = DateTimeOffset.Now,
                ExpirationDate = DateTimeOffset.Now.AddDays(365),
                AllowedFeatures = ["Feature1", "Feature2"]
            };
            string licenseFile = Path.Combine(targetDirectory, $"license1-{i}.lic");
            await licenseAssistant.GenerateLicenseFileAsync(licenseData, licenseFile);
        });
    });
    stopwatch.Stop();
    Console.WriteLine($"方法 1:使用 Parallel.For,Generated {licenseCount} license files in {stopwatch.ElapsedMilliseconds} ms");
}    

方法 2:使用 Task.WhenAll

这种方式更符合现代化 .net 异步编程的最佳实践,能够更好地利用 I/O 并发的优势,避免阻塞主线程。

{
    Stopwatch stopwatch2 = Stopwatch.StartNew();
    List<Task> tasks = [];
    for (int i = 0; i < licenseCount; i++)
    {
        int index = i; // 捕获循环变量
        tasks.Add(Task.Run(async () =>
        {
            LicenseData licenseData = new()
            {
                AppName = "MyApp",
                AppVersion = "1.0.0",
                AuthCode = "123456",
                UserName = "John Doe",
                CompanyName = "Acme Inc.",
                StartDate = DateTimeOffset.Now,
                ExpirationDate = DateTimeOffset.Now.AddDays(365),
                AllowedFeatures = ["Feature1", "Feature2"]
            };
            string licenseFile = Path.Combine(targetDirectory, $"license2-{index}.lic");
            await licenseAssistant.GenerateLicenseFileAsync(licenseData, licenseFile);
        }));
    }
    await Task.WhenAll(tasks);
    stopwatch2.Stop();
    Console.WriteLine($"方法 2:使用 Task.WhenAll,Generated {licenseCount} license files in {stopwatch2.ElapsedMilliseconds} ms");
}

本人测试电脑配置比较差,信息如下:

cpu

  • 执行命令:
dotnet run
  • 输出信息:
Hello, SM2KeyPairGenerator!
Curve Name: sm2p256v1
Curve: Org.BouncyCastle.Math.EC.FpCurve
G:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffefffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffffffffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffffG:(32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7,bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0,1,fffffffeffff
ffffffffffffffffffffffffffff00000000fffffffffffffffc)
N:115792089210356248756420345214020892766061623724957744567843809356293439045923
H:1
ECDomainParameters: Org.BouncyCastle.Crypto.Parameters.ECDomainParameters

public => Org.BouncyCastle.Crypto.Parameters.ECPublicKeyParameters
private => Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters
sign => System.Byte[]
verify => True
Project Folder Path: lics
License is valid: True

方法 1:使用 Parallel.For,Generated 10000 license files in 81969 ms
方法 2:使用 Task.WhenAll,Generated 10000 license files in 73862 ms

说明,依据电脑配置情况,生成文件耗时会有差异,此处大概参考。
电脑配置规格: cpu:intel i7-12650h,内存:16g ddr4,硬盘:1 t ssd
生成 10000.lic 文件,大概耗时 18-19 秒左右。
感兴趣的下伙伴请自行测试哟。

  • License 证书文件如下:

在这里插入图片描述

关于 Parallel.For & Task.WhenAll 的对比

1.工作原理

  • Parallel.For
- 使用 TPL (Task Parallel Library) 来并行执行循环体。
- 内部管理线程池,自动分配任务到多个线程。
- 更适合 CPU 密集型任务。
  • Task.WhenAll
- 创建多个异步任务,并等待所有任务完成。
- 每个任务可以独立运行,适用于 I/O 密集型任务(如文件操作、网络请求)。
- 提供更好的控制和灵活性,可以组合不同的异步操作。

2.适用场景

  • Parallel.For
- 适用于需要并行处理大量数据的场景。
- 例如:计算密集型任务,如矩阵运算、图像处理等。
  • Task.WhenAll
- 适用于需要并发执行多个异步操作的场景。
- 例如:并发读写文件、发起多个 HTTP 请求等。

3.性能对比

  • Parallel.For
对于 CPU 密集型任务,性能较好。
对于 I/O 密集型任务,可能因为线程切换频繁而降低性能。
  • Task.WhenAll
对于 I/O 密集型任务,性能更好,因为它不会阻塞线程,而是让线程去执行其他任务。
在你的例子中,生成许可证文件是 I/O 密集型任务,因此 Task.WhenAll 更合适。

4.结论

如果你需要处理 I/O 密集型任务(如文件操作、网络请求),建议使用 Task.WhenAll。
如果你需要处理 CPU 密集型任务(如大量计算),可以考虑使用 Parallel.For。

在上述的代码中,生成 .lic 许可证文件属于 I/O 密集型任务,因此推荐使用 Task.WhenAll 方式来提高性能和稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChaITSimpleLove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值