1

我在服务器和客户端之间使用基于证书的身份验证。我已经生成了根证书。我的客户端在安装时将生成一个新证书并使用根证书对其进行签名。我需要使用 Windows API。无法使用任何 Windows 工具,例如 makecert。

到目前为止,我已经能够在商店中安装根证书。下面的代码

X509Certificate2 ^ certificate = gcnew X509Certificate2("C:\\rootcert.pfx","test123");
X509Store ^ store = gcnew X509Store( "teststore",StoreLocation::CurrentUser );
store->Open( OpenFlags::ReadWrite );
store->Add( certificate );
store->Close();

然后打开安装的根证书获取上下文

GetRootCertKeyInfo(){
  HCERTSTORE hCertStore;
  PCCERT_CONTEXT pSignerCertContext=NULL;
  DWORD dwSize = NULL; 
  CRYPT_KEY_PROV_INFO* pKeyInfo = NULL; 
  DWORD dwKeySpec;
  if ( !( hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL,  CERT_SYSTEM_STORE_CURRENT_USER,L"teststore")))
  {
    _tprintf(_T("Error 0x%x\n"), GetLastError());
  }
  pSignerCertContext = CertFindCertificateInStore(hCertStore,MY_ENCODING_TYPE,0,CERT_FIND_ANY,NULL,NULL);

if(NULL == pSignerCertContext)
{
    _tprintf(_T("Error 0x%x\n"), GetLastError());
}

if(!(CertGetCertificateContextProperty( pSignerCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize))) 
{ 
    _tprintf(_T("Error 0x%x\n"), GetLastError());
} 

if(pKeyInfo) 
   free(pKeyInfo); 
if(!(pKeyInfo = (CRYPT_KEY_PROV_INFO*)malloc(dwSize))) 
{ 
    _tprintf(_T("Error 0x%x\n"), GetLastError());
} 


if(!(CertGetCertificateContextProperty( pSignerCertContext, CERT_KEY_PROV_INFO_PROP_ID, pKeyInfo, &dwSize))) 
{    
    _tprintf(_T("Error 0x%x\n"), GetLastError());
}
return pKeyInfo;
}

然后最后创建证书并使用 pKeyInfo 签名

    // Acquire key container
if (!CryptAcquireContext(&hCryptProv, _T("trykeycon"), NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET)) 
{
  _tprintf(_T("Error 0x%x\n"), GetLastError());

  // Try to create a new key container
  _tprintf(_T("CryptAcquireContext... "));
  if (!CryptAcquireContext(&hCryptProv, _T("trykeycon"), NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))
  {
    _tprintf(_T("Error 0x%x\n"), GetLastError());
    return 0;
  }
  else 
  {
    _tprintf(_T("Success\n"));
  }
}
else
{
  _tprintf(_T("Success\n"));
}

// Generate new key pair
_tprintf(_T("CryptGenKey... "));
if (!CryptGenKey(hCryptProv, AT_SIGNATURE, 0x08000000 /*RSA-2048-BIT_KEY*/, &hKey))
{
  _tprintf(_T("Error 0x%x\n"), GetLastError());
  return 0;
}
else
{
  _tprintf(_T("Success\n"));
}
//some code
    CERT_NAME_BLOB SubjectIssuerBlob;
memset(&SubjectIssuerBlob, 0, sizeof(SubjectIssuerBlob));
SubjectIssuerBlob.cbData = cbEncoded;
SubjectIssuerBlob.pbData = pbEncoded;

// Prepare algorithm structure for self-signed certificate
CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
memset(&SignatureAlgorithm, 0, sizeof(SignatureAlgorithm));
SignatureAlgorithm.pszObjId = szOID_RSA_SHA1RSA;

// Prepare Expiration date for self-signed certificate
SYSTEMTIME EndTime;
GetSystemTime(&EndTime);
EndTime.wYear += 5;

// Create self-signed certificate
_tprintf(_T("CertCreateSelfSignCertificate... "));

CRYPT_KEY_PROV_INFO* aKeyInfo;
aKeyInfo = GetRootCertKeyInfo();
pCertContext = CertCreateSelfSignCertificate(NULL, &SubjectIssuerBlob, 0, aKeyInfo, &SignatureAlgorithm, 0, &EndTime, 0);

使用上面的代码,我可以创建证书,但它看起来没有由根证书签名。我无法弄清楚我所做的是对还是错..任何帮助都将不胜感激..

谢谢阿西夫

4

2 回答 2

1

我认为您在这里不需要自签名证书。您想创建一个由您的“根”证书签名的新“客户端”证书,对吗?我认为您需要使用 CryptSignAndEncodeCertificate 来执行此操作。

于 2010-03-07T21:52:30.230 回答
1

首先,几年前,我编写了一些产品,其中还包含您要求的完整代码:创建新证书并针对其他根证书对其进行签名。此代码仅使用Microsoft CryptoAPI。但在我给你详细的答案或发布一些我想知道的代码之前,你的问题是否是实际的:你的问题是一个半月前。

一般来说,做你想做的事,不应该使用CertCreateSelfSignCertificate()函数,而是创建一个关于其他 CryptoAPI 的证书。CertCreateSelfSignCertificate()函数只能用于创建和签署自签名证书。它不能用您创建的其他证书签署证书。此外CertCreateSelfSignCertificate(),功能(或至少在几年前)对创建的私钥的长度有严格的限制。因此,必须CertCreateSelfSignCertificate()手动完成功能相同的事情。API链中的最后一个APICryptSignAndEncodeCertificate()就像在另一个答案Luke中写的一样。

此外,我有一个很大的怀疑,即为您的目的创建证书链不是正确的方法。如果您只需要为身份验证执行此操作,则无需在客户端创建证书。在我看来,处理 singed 消息就足够了。一种使用证书而不是密钥对的情况是,您需要密钥撤销或密钥限制等功能仅用于某些目的(例如仅用于代码签名而不用于数据加密)。

如果你真的需要实现基于证书的身份验证,那么至少你必须创建客户端计算机的证书,将其发送到服务器计算机并在服务器计算机上发出客户端证书,因为只有服务器计算机才能持有根证书的私钥.

您也可以实现您对证书注册 API的要求吗?它不会完全符合您的要求,但它可以解决您的主要问题。

回答其他问题(评论):要么我不理解您计划实现为“2 路身份验证”的构造,要么这个概念是错误的。

  1. 我不明白您计划如何以及何时将根客户端证书从所有客户端传输到服务器。
  2. 您想使用客户端根 CA 签署客户端证书,因此您必须在客户端上保存客户端根 CA 证书的私钥(!!!???)。
  3. 如果服务器没有关于客户端的任何经过验证的信息,例如其根 CA 证书,那么您就不能真正使用客户端证书进行客户端授权。任何拥有证书的客户端都将被“授权”。
  4. 使用没有客户端证书的标准 SSL,您可以确保始终与同一个客户端进行通信。因此,如果您不解决客户端根 CA 到服务器的传输问题,您将无法像标准(没有客户端证书)SSL 中那样实现更高的安全性。
  5. 您所写的内容似乎您尝试不使用服务器和客户端之间的标准通信协议。取而代之的是,您尝试引入一个新的自己的协议。这是非常危险的!如果你不是密码学专家,你会意识到一个看起来很安全的协议,实际上并不安全。“小”问题,如持有私钥和从客户端到服务器或返回的公钥安全传输是非常重要的密码学主题。那里有不同的加密协议要解决。所以不要试图在这个领域实施一些新的东西!
  6. 再想一想为什么你真的想要使用客户端证书。你真的用这种方式实现客户端认证吗?例如,在标准 SSL 中,可以确保始终与同一个客户端进行通信,并为此仅使用随机数和为会话生成的对称密钥。您想为每个客户端或每个会话生成一个证书吗?

我可以写更多我的问题。如果你实现了他自己的基于证书的通信协议,在我理解你的概念之前,我有很大的问题要给出我的建议。

于 2010-04-25T15:31:15.760 回答