1

サーバーとクライアントの間で証明書ベースの認証を使用しています。ルート証明書を生成しました。クライアントはインストール時に新しい証明書を生成し、ルート証明書を使用して署名します。Windows API を使用する必要があります。makecert などの Windows ツールは使用できません。

これまで、ルート証明書をストアにインストールできました。以下のコード

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

まず最初に、数年前に、あなたが求めた完全なコードを含む製品を書きました: 新しい証明書を作成し、他のルート証明書に関して署名します。このコードはMicrosoft CryptoAPIのみを使用します。しかし、詳細な回答をしたりコードを投稿したりする前に、あなたの問題が実際のものであるかどうかを知りたいと思います.あなたの質問は1ヶ月半前のものです.

一般に、必要なことを行うには、CertCreateSelfSignCertificate()関数を使用するのではなく、他の CryptoAPI に関して証明書を作成する必要があります。CertCreateSelfSignCertificate()関数は、自己署名証明書の作成と署名にのみ使用できます。作成した他の証明書で証明書に署名することはできません。さらにCertCreateSelfSignCertificate()、関数には、作成された秘密鍵の長さに厳しい制限があります (または少なくとも数年前にありました)。したがって、関数と同じことをCertCreateSelfSignCertificate()手動で行う必要があります。APIチェーンの最後のAPIは、CryptSignAndEncodeCertificate()あなたの質問に対する別の回答ルークに書いたようなものです。

さらに、あなたの目的のために、証明書チェーンを作成することは正しい方法ではないという大きな疑いがあります。認証のためだけにこれを行う必要がある場合は、クライアント側で証明書を作成する必要はありません。歌われたメッセージを扱うだけで十分だと私には思えます。キー ペアの代わりに証明書を 1 回使用するのは、キーの取り消しやキーの制限などの機能が必要な場合で、何らかの目的 (たとえば、データ暗号化ではなくコード署名のみ) でのみ使用する場合です。

証明書ベースの認証を本当に実装する必要がある場合は、少なくともクライアント コンピューターの証明書を作成し、それをサーバー コンピューターに送信し、サーバー コンピューターでクライアント証明書を署名する必要があります。これは、サーバー コンピューターのみがルート証明書の秘密鍵を保持できるためです。 .

また、 Certificate Enrollment APIに関して要件を実装できる可能性もあります。それはまさにあなたが求めたものではありませんが、あなたの主な問題を解決することができます.

追加の質問への回答 (コメント) : 「双方向認証」として実現しようとしている構造が理解できないか、概念が間違っています。

  1. すべてのクライアントからサーバーにルート クライアント証明書を転送する方法と時期がわかりません。
  2. クライアントのルート CA でクライアント証明書に署名する必要があるため、クライアントのルート CA 証明書の秘密鍵 (!!!???) をクライアントで保持する必要があります。
  3. サーバーがルート CA 証明書などのクライアントに関する検証済みの情報を持っていない場合、クライアントの認証にクライアント証明書を実際に使用することはできません。証明書を持つクライアントはすべて「承認」されます。
  4. クライアント証明書のない標準 SSL を使用すると、常に同じクライアントと通信することができます。そのため、クライアントのルート CA からサーバーへのトランスポートの問題を解決しないと、標準の (クライアント証明書を使用しない) SSL のようにセキュリティを強化することはできません。
  5. あなたが書いたものは、サーバーとクライアントの間で標準の通信プロトコルを使用しないようにしているようです. その代わりに、新しい独自のプロトコルを導入しようとします。とても危険です!暗号化の専門家でない場合は、安全に見えるだけで安全ではないプロトコルに気付くでしょう。秘密鍵の保持や、クライアントからサーバーへ、またはクライアントからサーバーへの公開鍵の安全な転送などの「小さな」問題は、非常に重要な暗号化の主題です。そこで解決すべきさまざまな暗号プロトコルがあります。したがって、この分野で何か新しいことを実装しようとしないでください。
  6. 本当にクライアント証明書を使用したい理由をもう一度考えてください。途中で本当にクライアント認証を実装していますか?たとえば、標準の SSL では、同じクライアントと常に通信し、セッション用に生成されたランダムナンスと対称キーのみを使用することができます。クライアントごとまたはセッションごとに証明書を生成しますか?

私はもっ​​と私の質問を書くことができます。彼自身の証明書ベースの通信プロトコルを実装する場合、私はあなたの概念を理解する前に私の推奨を行うには大きな問題があります.

于 2010-04-25T15:31:15.760 に答える
1

ここで必要なのは自己署名証明書ではないと思います。「ルート」証明書によって署名された新しい「クライアント」証明書を作成したいのですか? これを行うには CryptSignAndEncodeCertificate を使用する必要があると思います。

于 2010-03-07T21:52:30.230 に答える