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

本文涉及的产品
可观测监控 Prometheus 版,每月50GB免费额度
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
云原生网关 MSE Higress,422元/月
简介: 授权证书文件的后缀通常取决于其编码格式和具体用途。本文档通过一个示例程序展示了如何在 .NET 平台上使用国密 SM2 算法生成和验证许可证(License)文件。该示例不仅详细演示了 SM2 国密算法的实际应用场景,还提供了关于如何高效处理大规模许可证文件生成任务的技术参考。通过对不同并发策略的性能测试,开发者可以更好地理解如何优化许可证生成流程,以满足高并发和大数据量的需求。希望这段描述更清晰地传达了程序的功能和技术亮点。

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 方式来提高性能和稳定性。

目录
相关文章
|
2月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
2月前
|
监控 Cloud Native 测试技术
.NET技术深度解析:现代企业级开发指南
每日激励:“不要一直责怪过去的自己,他曾经站在雾里也很迷茫”。我是蒋星熠Jaxonic,一名在代码宇宙中探索的极客旅人。从.NET Framework到.NET 8,我深耕跨平台、高性能、云原生开发,践行领域驱动设计与微服务架构,用代码书写技术诗篇。分享架构演进、性能优化与AI融合前沿,助力开发者在二进制星河中逐光前行。关注我,共探技术无限可能!
.NET技术深度解析:现代企业级开发指南
|
3月前
|
机器学习/深度学习 人工智能 搜索推荐
从零构建短视频推荐系统:双塔算法架构解析与代码实现
短视频推荐看似“读心”,实则依赖双塔推荐系统:用户塔与物品塔分别将行为与内容编码为向量,通过相似度匹配实现精准推送。本文解析其架构原理、技术实现与工程挑战,揭秘抖音等平台如何用AI抓住你的注意力。
638 7
从零构建短视频推荐系统:双塔算法架构解析与代码实现
|
3月前
|
机器学习/深度学习 存储 算法
动态规划算法深度解析:0-1背包问题
0-1背包问题是经典的组合优化问题,目标是在给定物品重量和价值及背包容量限制下,选取物品使得总价值最大化且每个物品仅能被选一次。该问题通常采用动态规划方法解决,通过构建二维状态表dp[i][j]记录前i个物品在容量j时的最大价值,利用状态转移方程避免重复计算子问题,从而高效求解最优解。
481 1
|
3月前
|
算法 搜索推荐 Java
贪心算法:部分背包问题深度解析
该Java代码基于贪心算法求解分数背包问题,通过按单位价值降序排序,优先装入高价值物品,并支持部分装入。核心包括冒泡排序优化、分阶段装入策略及精度控制,体现贪心选择性质,适用于可分割资源的最优化场景。
277 1
贪心算法:部分背包问题深度解析
|
3月前
|
机器学习/深度学习 边缘计算 人工智能
粒子群算法模型深度解析与实战应用
蒋星熠Jaxonic是一位深耕智能优化算法领域多年的技术探索者,专注于粒子群优化(PSO)算法的研究与应用。他深入剖析了PSO的数学模型、核心公式及实现方法,并通过大量实践验证了其在神经网络优化、工程设计等复杂问题上的卓越性能。本文全面展示了PSO的理论基础、改进策略与前沿发展方向,为读者提供了一份详尽的技术指南。
粒子群算法模型深度解析与实战应用
|
3月前
|
机器学习/深度学习 资源调度 算法
遗传算法模型深度解析与实战应用
摘要 遗传算法(GA)作为一种受生物进化启发的优化算法,在复杂问题求解中展现出独特优势。本文系统介绍了GA的核心理论、实现细节和应用经验。算法通过模拟自然选择机制,利用选择、交叉、变异三大操作在解空间中进行全局搜索。与梯度下降等传统方法相比,GA不依赖目标函数的连续性或可微性,特别适合处理离散优化、多目标优化等复杂问题。文中详细阐述了染色体编码、适应度函数设计、遗传操作实现等关键技术,并提供了Python代码实现示例。实践表明,GA的成功应用关键在于平衡探索与开发,通过精心调参维持种群多样性同时确保收敛效率
机器学习/深度学习 算法 自动驾驶
509 0
|
3月前
|
机器学习/深度学习 人工智能 资源调度
大语言模型的核心算法——简要解析
大语言模型的核心算法基于Transformer架构,以自注意力机制为核心,通过Q、K、V矩阵动态捕捉序列内部关系。多头注意力增强模型表达能力,位置编码(如RoPE)解决顺序信息问题。Flash Attention优化计算效率,GQA平衡性能与资源消耗。训练上,DPO替代RLHF提升效率,MoE架构实现参数扩展,Constitutional AI实现自监督对齐。整体技术推动模型在长序列、低资源下的性能突破。
408 8
|
3月前
|
算法 API 数据安全/隐私保护
深度解析京东图片搜索API:从图像识别到商品匹配的算法实践
京东图片搜索API基于图像识别技术,支持通过上传图片或图片URL搜索相似商品,提供智能匹配、结果筛选、分页查询等功能。适用于比价、竞品分析、推荐系统等场景。支持Python等开发语言,提供详细请求示例与文档。

推荐镜像

更多
  • DNS