C# USB eToken 签名和验证问题
C# USB eToken signature and validation issue
我有一个带有 public 的 x509 证书和存储在 safenet usb 令牌上的私钥。
我有一些数据要签名。我需要使用证书的public密钥来验证签名。
使用我自己的自签名证书进行签名的最终代码:
RSACryptoServiceProvider rsa1 = (RSACryptoServiceProvider)useCertificate.PrivateKey;
byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
以及使用证书的 public 密钥验证的代码:
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);
使用自签名证书可以正常工作。我取回的签名是256
字节。
token用这个code获取签名然后验证,我只得到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 中是否需要一些其他参数和设置?
谢谢
* 根据评论更新 *
很明显,当我指定 csp.keyNumber = (int)KeyNumber.Signature 时,我使用的是 1024 位 - 但这是令牌实际上 returns 任何东西的唯一方式。即使令牌密钥大小为 2048 位且密钥规范为 AT_KEYEXCHANGE。当我使用我认为实际上正确的交换密钥号时,当我尝试计算签名时,系统提示我登录,但随后出现异常 "The parameter is invalid"。所以据我所知,我需要两件事之一:
1 - 如何使用 public 密钥来验证使用 1024 位的签名(没有令牌 - 我们需要在没有令牌的机器上进行验证)。
或
2 - 如何设置不正确的内容以便我们可以通过异常 - 我认为这是更好的主意。
有没有人对我可以如何处理此异常或可能导致它的原因有任何建议?
下面是完整的异常详细信息:
HResult = -2147024809
消息 = 参数不正确。
堆栈跟踪
在 System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 小时)
在 System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature)
在 System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash)
在 System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash)
在 System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, String str)
在 TE.Program.Main(String[] args) 在 z:\Work\compusolve\enctest\TE\TE\Program.cs:line 77
我不得不质疑你关于它实际上是默认密钥容器密钥的断言(你可能已经在你的代码 运行 第一次创建了那个密钥,因为你没有断言 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
如果您 copy/paste 任何值并将其用作密钥容器名称,您的签名可能会开始具有正确的大小。
(如果您的证书在机器存储而不是用户存储中,请省略 -user
选项)
这个问题的答案有两个。如果您使用的是这些设备之一,我发现在 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);
}
}
}
}
我有一个带有 public 的 x509 证书和存储在 safenet usb 令牌上的私钥。
我有一些数据要签名。我需要使用证书的public密钥来验证签名。
使用我自己的自签名证书进行签名的最终代码:
RSACryptoServiceProvider rsa1 = (RSACryptoServiceProvider)useCertificate.PrivateKey;
byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
以及使用证书的 public 密钥验证的代码:
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);
使用自签名证书可以正常工作。我取回的签名是256
字节。
token用这个code获取签名然后验证,我只得到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 中是否需要一些其他参数和设置?
谢谢
* 根据评论更新 *
很明显,当我指定 csp.keyNumber = (int)KeyNumber.Signature 时,我使用的是 1024 位 - 但这是令牌实际上 returns 任何东西的唯一方式。即使令牌密钥大小为 2048 位且密钥规范为 AT_KEYEXCHANGE。当我使用我认为实际上正确的交换密钥号时,当我尝试计算签名时,系统提示我登录,但随后出现异常 "The parameter is invalid"。所以据我所知,我需要两件事之一:
1 - 如何使用 public 密钥来验证使用 1024 位的签名(没有令牌 - 我们需要在没有令牌的机器上进行验证)。
或
2 - 如何设置不正确的内容以便我们可以通过异常 - 我认为这是更好的主意。
有没有人对我可以如何处理此异常或可能导致它的原因有任何建议?
下面是完整的异常详细信息:
HResult = -2147024809 消息 = 参数不正确。 堆栈跟踪
在 System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 小时) 在 System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash, Int32 cbHash, ObjectHandleOnStack retSignature) 在 System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] hash) 在 System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, Int32 calgHash) 在 System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, String str) 在 TE.Program.Main(String[] args) 在 z:\Work\compusolve\enctest\TE\TE\Program.cs:line 77
我不得不质疑你关于它实际上是默认密钥容器密钥的断言(你可能已经在你的代码 运行 第一次创建了那个密钥,因为你没有断言 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
如果您 copy/paste 任何值并将其用作密钥容器名称,您的签名可能会开始具有正确的大小。
(如果您的证书在机器存储而不是用户存储中,请省略 -user
选项)
这个问题的答案有两个。如果您使用的是这些设备之一,我发现在 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);
}
}
}
}