【问题标题】:C# USB eToken signature and validation issueC# USB eToken 签名和验证问题
【发布时间】:2017-08-25 00:26:43
【问题描述】:

我有一个 x509 证书,其中包含存储在安全网 USB 令牌上的公钥和私钥。

我有一些数据要签名。我需要使用证书的公钥来验证签名。

使用我自己的自签名证书进行签名的最终代码:

RSACryptoServiceProvider rsa1 = (RSACryptoServiceProvider)useCertificate.PrivateKey;
byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));

以及使用证书公钥验证的代码:

RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);

使用自签名证书可以正常工作。我得到的签名是 256
字节。

使用此代码获取签名然后验证的令牌,我只得到 128 字节的签名并且验证失败:

CspParameters csp = new CspParameters(1, "SafeNet RSA CSP");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
csp.KeyNumber = (int)KeyNumber.Signature;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(csp);

验证码同上。

我注意到我要使用的证书是令牌中的默认值。为什么我只得到一个 128 字节的签名而不是 256?我怀疑这就是它无法验证的原因。

我的 csp 中是否需要一些其他参数和设置?

谢谢

* 基于 cmets 的更新 *

很明显,当我指定 csp.keyNumber = (int)KeyNumber.Signature 时,我使用的是 1024 位 - 但这是令牌实际返回任何内容的唯一方式。即使令牌密钥大小是 2048 位并且密钥规范是 AT_KEYEXCHANGE。当我使用我认为实际上正确的交换密钥号时,当我尝试计算签名时,系统会提示我登录,但随后出现异常“参数无效”。因此,据我所知,我需要两件事之一:

1 - 如何使用公钥来验证使用 1024 位的签名(没有令牌 - 我们需要在没有令牌的机器上验证)。

2 - 如何设置不正确的内容,以便我们可以通过异常 - 我认为这是更好的主意。

有没有人对我可以对这个异常做些什么或可能导致它的原因有什么建议?

完整的异常详情如下:

HResult = -2147024809 Message = 参数不正确。 堆栈跟踪

在 System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 小时) 在 System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey,Int32 keyNumber,Int32 calgKey,Int32 calgHash,Byte[] 哈希,Int32 cbHash,ObjectHandleOnStack retSignature) 在 System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash) 在 System.Security.Cryptography.RSACryptoServiceProvider.SignHash(字节 [] rgbHash,Int32 calgHash) 在 System.Security.Cryptography.RSACryptoServiceProvider.SignHash(字节 [] rgbHash,字符串 str) 在 TE.Program.Main(String[] args) 在 z:\Work\compusolve\enctest\TE\TE\Program.cs:line 77

【问题讨论】:

  • 除非数据丢失,否则 128 字节签名用于 1024 位密钥而不是 2048 位密钥;即错误的密钥对。
  • 这对我有帮助。我知道我的验证密钥是 2048 位密钥。因此,我将深入研究如何让令牌使用正确的密钥对。我会在这里报告。
  • 我将签名的代码更改为:RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(2048, csp); 但它并没有改变结果 - 我仍然只得到 128 个字节。这应该需要使用 2048 位密钥。
  • 无论我在创建 RsaCryptoServiceProvider 时指定什么,内部密钥大小都是 1024。如果我将 csp.KeyNumber 更改为 (Int)KeyNumber.Exchange 而不是 Signature,那么我会得到我想要的 2048 密钥大小,当我要求设备计算签名时,我得到一个异常“参数无效”。设备上的 KeySize 为 2048 位。
  • 您确定可以使用密钥生成签名吗?因为 AT_KEYEXCHANGE 不是签名生成。

标签: c# cryptography rsacryptoserviceprovider


【解决方案1】:

我必须质疑您的断言,即它实际上是默认的密钥容器密钥(您可能在代码第一次运行时创建了该密钥,因为您没有声明 UseExistingKey 标志)。

假设证书在您的证书存储中,运行 certutil -user -silent store my 并找到证书条目并检查 Key Container 值:

================ Certificate 11 ================
Serial Number: 0123456789abcdeffedcba9876543210
Issuer: CN=Intermediate Certificate Authority
 NotBefore: 10/21/2016 7:26 AM
 NotAfter: 10/21/2017 7:26 AM
Subject: CN=bartonjs
Non-root Certificate
Template:
Cert Hash(sha1): 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
  Key Container = le-Smartcard-987abcdf-6332-43-16531
  Provider = Microsoft Base Smart Card Crypto Provider

如果您复制/粘贴任何值并将其用作密钥容器名称,您的签名可能会开始具有正确的大小。

(如果您的证书在机器存储而不是用户存储中,请省略-user 选项)

【讨论】:

  • 私钥的密钥容器在令牌上而不是本地存储上。我确实确保它也是默认证书 - USB 令牌上有两个。
【解决方案2】:

这个问题的答案有两个。如果您使用其中一种设备,我在注册表中发现了 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider

有 3 个不同的提供商。每个都具有相同的类型甚至图像设置 - 使用的 dll。但是选择一个不同的,在我的例子中是 Datakey RSP CSP,它提供了基于 2048 位密钥的 256 字节签名。您还必须确保您使用的证书是令牌中的默认证书。就我而言,有两个不同的证书。我正在使用一个进行验证,但使用另一个进行签名。

测试客户端的完整源代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

namespace TE
{
class Program
{
    static void Main(string[] args)
    {
        try
        {

            // these variables should be changed to math your installation

            // find CSP's in this windows registry key:  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
            string TokenCSPName = "Datakey RSA CSP";
            string TokenCertificateName = "ACME Inc";
            string NonTokenCertificateName = "SelfSigned";
            string certLocation = "Token"; // change to something else to use self signed "Token" for token
            // the certificate on the token should be installed into the local users certificate store
            // tokens will not store or export the private key, only the public key


            // find the certificate we want to use - there's no recovery if the certificate is not found

            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.OpenExistingOnly);
            X509Certificate2Collection certificates = store.Certificates;
            X509Certificate2 certificate = new X509Certificate2();
            X509Certificate2 useCertificate = new X509Certificate2();
            if (certLocation == "Token")
            {
                for (int i = 0; i < certificates.Count; i++)
                {
                    certificate = certificates[i];
                    string subj = certificate.Subject;
                    List<X509KeyUsageExtension> extensions = certificate.Extensions.OfType<X509KeyUsageExtension>().ToList();
                    if (certificate.GetNameInfo(X509NameType.SimpleName, false).ToString() == TokenCertificateName)
                    {
                        for (int j = 0; j < extensions.Count; j++)
                        {
                            if ((extensions[j].KeyUsages & X509KeyUsageFlags.DigitalSignature) == X509KeyUsageFlags.DigitalSignature)
                            {
                                useCertificate = certificate;
                                j = extensions.Count + 1;
                            }

                        }

                    }
                }
            } else
            {
                for (int i = 0; i < certificates.Count; i++)
                {
                    certificate = certificates[i];
                    string subj = certificate.Subject;
                    List<X509KeyUsageExtension> extensions = certificate.Extensions.OfType<X509KeyUsageExtension>().ToList();
                    if (certificate.GetNameInfo(X509NameType.SimpleName, false).ToString() == NonTokenCertificateName)
                        useCertificate = certificate;
                }

            }
            CspParameters csp = new CspParameters(1, TokenCSPName);

            csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
            csp.KeyNumber = (int)KeyNumber.Exchange;
            RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(csp);
            string SignatureString = "Data that is to be signed";
            byte[] plainTextBytes = Encoding.ASCII.GetBytes(SignatureString);
            bool Verified = false;
            using (SHA1CryptoServiceProvider shaM = new SHA1CryptoServiceProvider())
            {
                // hash the data to be signed - you can use signData and avoid the hashing if you like

                byte[] hash = shaM.ComputeHash(plainTextBytes);
                // sign the hash
                byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
                // check your signature size here - if not 256 bytes then you may not be using the proper
                // crypto provider

                // Verify the signature with the hash

                RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
                Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);
                if (Verified)
                {
                    Console.WriteLine("Signature Verified");
                }
                else
                {
                    Console.WriteLine("Signature Failed Verification");
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
  }
}

【讨论】:

  • 感谢您的反馈,您可以在 2 天后接受自己的回答。
  • @user2709214 我收到“未定义密钥集”错误
猜你喜欢
  • 2017-09-07
  • 2022-08-03
  • 2016-06-27
  • 1970-01-01
  • 2022-11-09
  • 2012-01-16
  • 1970-01-01
  • 1970-01-01
  • 2019-10-02
相关资源
最近更新 更多