You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Windows 10及.NET 8中,如何用RSACryptoServiceProvider公钥SignHash并私钥验签?

.NET 8中RSACryptoServiceProvider公钥签名与私钥验证问题

问题背景

此前提问过如何在.NET 8中使用RSACryptoServiceProvider执行SignHash操作,按建议加载私钥时设置X509KeyStorageFlags.Exportable后可正常签名,但对接的API要求仅用公钥执行SignHash,导致签名无法被验证。尝试用公钥执行SignHash时触发以下错误:

Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
执行请求时发生未处理的异常。
System.Security.Cryptography.CryptographicException:对象仅包含密钥对的公钥部分,必须同时提供私钥。

现需在Windows 10 + .NET 8环境下,实现用RSACryptoServiceProvider完成「公钥加密/验证、私钥签名/解密」的合规流程,相关原始代码如下:

/** RSACrypto Variables */
    RSACryptoServiceProvider? key = null;
    RSACryptoServiceProvider? privateKey = null;
    string Payload = "Hello Sending sensitive data";

    /** Load CIGServerCerts */
    X509Certificate2Collection CIGServerCerts = new X509Certificate2Collection();
    CIGServerCerts.Import(_appSettingsHelper.CIGServerCertCertificatePath, null, X509KeyStorageFlags.PersistKeySet);
    if (CIGServerCerts.Count > 0)
    {
      CIGServerCert = CIGServerCerts[0];
      Console.WriteLine("Certificates found in the specified file.");
    }


    ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(Helper.CustomCertificateValidation);
    if (CIGServerCert != null && CIGServerCert.PublicKey != null)
    {
      RSA rsaPublicKey = this.CIGServerCert.GetRSAPublicKey();
      key = new RSACryptoServiceProvider();
      key.ImportParameters(rsaPublicKey.ExportParameters(false));
      if (key != null)
      {
        Console.WriteLine("Key retrieved successfully");
      }
    }

    /** Load Certificate for Stakeholder */
    X509Certificate2Collection stakeHolderCerts = new X509Certificate2Collection();
    if (stakeHolderCerts != null)
    {
      stakeHolderCerts.Import(_appSettingsHelper.StakeholderCertificatePath, _appSettingsHelper.StakeholderPassword, X509KeyStorageFlags.PersistKeySet);
      Console.WriteLine($"@A There is stakeholder certificate");
    }

    if (stakeHolderCerts?.Count > 0)
    {
      this.StakeHolderCert = stakeHolderCerts[0];
      Console.WriteLine("certificates found in the specified file.");
    }

    RSA rsaPrivateKey = this.StakeHolderCert.GetRSAPrivateKey();
    RSAParameters rsaParams = rsaPrivateKey.ExportParameters(false);
    privateKey = new RSACryptoServiceProvider();
    privateKey.ImportParameters(rsaParams);

    /** Serialize Data */
    if (Payload.Trim().Length == 20)
    {

      Helper.CryptoOutput cryptoOutput = Payload.ToString().EncryptPayload(); // (This is a function that return Key, IV and Payload) works fine
      byte[] inArray = key.Encrypt(cryptoOutput.Key, false);
      string base64String1 = Convert.ToBase64String(key.Encrypt(cryptoOutput.IV, false));
      string base64String2 = Convert.ToBase64String(inArray);
      string payload = cryptoOutput.Payload;
      SHA1Managed shA1Managed = new SHA1Managed();
      byte[] hash = shA1Managed.ComputeHash(Convert.FromBase64String(cryptoOutput.Payload));
      string oid = CryptoConfig.MapNameToOID("SHA1");
      string base64String3 = Convert.ToBase64String(privateKey.SignHash(hash, oid));

      string responseXModal = SerializationHelper.CreateDataRequest(
            base64String1,
            base64String2,
            payload,
            base64String3,
            "123.12.13.1",
            ApiConstant.ID,
            ApiConstant.TIMESTAMP,
            ApiConstant.USER_ID
      );

      byte[] rgbSignature = Convert.FromBase64String(responseXModal.Signature);
      byte[] rgbHash = shA1Managed.ComputeHash(Convert.FromBase64String(responseXModal.Payload));
      var signatureVerification = privateKey.VerifyHash(rgbHash, 
 CryptoConfig.MapNameToOID("SHA1"), rgbSignature);
      Console.WriteLine($"KEY RESPONSE found in the specified file. {signatureVerification}");

      if (signatureVerification)
      {

        /** Decrypt Keys */
        byte[] i_IV = privateKey.Decrypt(Convert.FromBase64String(responseXModal.EncryptedCryptoIV), false);
        byte[] i_Key = privateKey.Decrypt(Convert.FromBase64String(responseXModal.EncryptedCryptoIV), false);
      }
    }

解决方案

核心逻辑纠正

RSA非对称加密的标准流程是:私钥签名、公钥验证;公钥加密、私钥解密。对接API要求「公钥签名」属于表述错误,实际正确流程应为:

  • 己方用私钥签名请求,对方用己方公钥验证
  • 对方用私钥签名响应,己方用对方公钥验证

修正后代码实现

/** RSACrypto Variables */
RSACryptoServiceProvider? serverPublicKey = null; // 对方服务器公钥:加密请求密钥、验证响应签名
RSACryptoServiceProvider? stakeholderPrivateKey = null; // 己方私钥:签名请求、解密响应密钥
string Payload = "Hello Sending sensitive data";

/** Load CIGServerCerts (对方服务器公钥证书) */
X509Certificate2Collection CIGServerCerts = new X509Certificate2Collection();
CIGServerCerts.Import(_appSettingsHelper.CIGServerCertCertificatePath, null, X509KeyStorageFlags.PersistKeySet);
if (CIGServerCerts.Count > 0)
{
    CIGServerCert = CIGServerCerts[0];
    Console.WriteLine("服务器证书加载成功");
}

ServicePointManager.ServerCertificateValidationCallback += Helper.CustomCertificateValidation;
if (CIGServerCert != null)
{
    using RSA rsaPublicKey = CIGServerCert.GetRSAPublicKey();
    serverPublicKey = new RSACryptoServiceProvider();
    serverPublicKey.ImportParameters(rsaPublicKey.ExportParameters(false));
    Console.WriteLine("服务器公钥提取成功");
}

/** Load Stakeholder Certificate (己方私钥证书) */
X509Certificate2Collection stakeHolderCerts = new X509Certificate2Collection();
stakeHolderCerts.Import(_appSettingsHelper.StakeholderCertificatePath, _appSettingsHelper.StakeholderPassword, 
    X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet); // 必须添加Exportable确保私钥可导出
if (stakeHolderCerts.Count > 0)
{
    StakeHolderCert = stakeHolderCerts[0];
    Console.WriteLine("己方证书加载成功");
}

using RSA rsaPrivateKey = StakeHolderCert.GetRSAPrivateKey();
RSAParameters rsaParams = rsaPrivateKey.ExportParameters(true); // 传入true导出完整私钥参数
stakeholderPrivateKey = new RSACryptoServiceProvider();
stakeholderPrivateKey.ImportParameters(rsaParams);

/** 数据处理与请求构建 */
if (Payload.Trim().Length == 20)
{
    Helper.CryptoOutput cryptoOutput = Payload.EncryptPayload(); // 自定义对称加密逻辑
    
    // 用对方公钥加密对称密钥和IV
    byte[] encryptedKey = serverPublicKey.Encrypt(cryptoOutput.Key, false);
    byte[] encryptedIV = serverPublicKey.Encrypt(cryptoOutput.IV, false);
    string base64EncryptedIV = Convert.ToBase64String(encryptedIV);
    string base64EncryptedKey = Convert.ToBase64String(encryptedKey);
    string encryptedPayload = cryptoOutput.Payload;

    // 用己方私钥对Payload哈希签名
    using SHA1Managed sha1 = new SHA1Managed();
    byte[] payloadHash = sha1.ComputeHash(Convert.FromBase64String(encryptedPayload));
    string sha1Oid = CryptoConfig.MapNameToOID("SHA1");
    byte[] signature = stakeholderPrivateKey.SignHash(payloadHash, sha1Oid);
    string base64Signature = Convert.ToBase64String(signature);

    // 构建请求
    var requestModal = SerializationHelper.CreateDataRequest(
        base64EncryptedIV,
        base64EncryptedKey,
        encryptedPayload,
        base64Signature,
        "123.12.13.1",
        ApiConstant.ID,
        ApiConstant.TIMESTAMP,
        ApiConstant.USER_ID
    );

    // 发送请求并获取响应(此处省略请求发送逻辑)
    // var responseXModal = SendRequestToApi(requestModal);

    // 验证响应签名:用对方公钥验证
    if (responseXModal != null)
    {
        byte[] responseSignature = Convert.FromBase64String(responseXModal.Signature);
        byte[] responsePayloadHash = sha1.ComputeHash(Convert.FromBase64String(responseXModal.Payload));
        bool isSignatureValid = serverPublicKey.VerifyHash(responsePayloadHash, sha1Oid, responseSignature);
        Console.WriteLine($"响应签名验证结果:{isSignatureValid}");

        if (isSignatureValid)
        {
            // 用己方私钥解密响应中的对称密钥和IV
            byte[] decryptedIV = stakeholderPrivateKey.Decrypt(Convert.FromBase64String(responseXModal.EncryptedCryptoIV), false);
            byte[] decryptedKey = stakeholderPrivateKey.Decrypt(Convert.FromBase64String(responseXModal.EncryptedCryptoKey), false);
        }
    }
}

关键修正点

  1. 密钥用途明确区分:对方公钥负责加密请求密钥、验证响应签名;己方私钥负责签名请求、解密响应密钥,完全符合RSA标准逻辑
  2. 私钥导出参数修正:导出己方私钥时传入true,才能获取完整的私钥参数
  3. 验证逻辑修正:用对方公钥验证响应签名,原代码误用己方私钥验证,逻辑完全错误
  4. 证书加载标记补充:己方私钥证书加载时必须添加X509KeyStorageFlags.Exportable,否则无法导出私钥参数

内容的提问来源于stack exchange,提问作者Andongwisye Mwamengo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 06:25:03