Microsoft Crypto API Verwendung des RSAES-OAEP-Schlüsseltransportalgorithmus deaktivieren

Lesezeit: 8 Minuten

Benutzeravatar von user1775783
Benutzer1775783

Ich benutze CryptEncryptMessage ein zu erzeugen PKCS#7 umhüllte Nachricht. Ich benutze szOID_NIST_AES256_CBC als Verschlüsselungsalgorithmus.

Die generierte Nachricht scheint gültig zu sein, ist es aber RSAES-OAEP für den Schlüsseltransportalgorithmus, der in freier Wildbahn nur begrenzt unterstützt wird (Thunderbird, OpenSSL SMIME-Modul und viele andere unterstützen ihn nicht).

Ich möchte, dass CAPI zum älteren zurückkehrt RSAencryption für den Schlüsseltransport.

Gibt es eine Möglichkeit, dies zu tun, könnte ich auf die Nachrichtenfunktionen auf niedriger Ebene zurückgreifen, wenn es eine Möglichkeit gibt, anstatt sie zu verwenden CryptEncryptMessage aber ich kann keine Möglichkeit finden, das zu tun, selbst wenn ich die Low-Level-Funktionen verwende.

Code:

CRYPT_ENCRYPT_MESSAGE_PARA EncryptMessageParams;
EncryptMessageParams.cbSize = sizeof(CMSG_ENVELOPED_ENCODE_INFO);

EncryptMessageParams.dwMsgEncodingType = PKCS_7_ASN_ENCODING;

EncryptMessageParams.ContentEncryptionAlgorithm.pszObjId = szOID_NIST_AES256_CBC;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.cbData = 0;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.pbData = 0;

EncryptMessageParams.hCryptProv = NULL;
EncryptMessageParams.pvEncryptionAuxInfo = NULL;
EncryptMessageParams.dwFlags = 0;
EncryptMessageParams.dwInnerContentType = 0;

BYTE pbEncryptedBlob[640000];
DWORD pcbEncryptedBlob = 640000;

BOOL retval =  CryptEncryptMessage(&EncryptMessageParams, cRecipientCert, pRecipCertContextArray, pbMsgText, dwMsgTextSize, pbEncryptedBlob, &pcbEncryptedBlob);

  • Willkommen bei Stapelüberlauf! Dies ist eine nette Frage für Programmierer ohne komplizierte Kenntnisse über die API. Vielleicht könnten Sie auch einige Ressourcen rund um CAPI ausprobieren.

    – Maarten Bodewes

    27. Oktober 2012 um 16:37 Uhr

  • Verdammt, diese Microsoft-Krypto-API ist (wie üblich) völlig unterspezifiziert.

    – Maarten Bodewes

    30. Oktober 2012 um 20:23 Uhr

  • @owlstead, und total verwirrend. Es könnte einen völlig anderen Weg geben, auf dem das OP seine Nachrichten verschlüsseln könnte, was sein Problem lösen, aber durch 10 verschiedene ersetzen wird.

    – Adrian Ratnapala

    7. Mai 2013 um 14:15 Uhr

  • Die Gründe für die Verwendung von RSA-OAEP finden Sie im Blog von Dr. Matthew Gree Ein paar schlechte Jahre für die Krypto-Token-Industrie. Aber einige sagen, wir tauschen den Teufel, den wir kennen, gegen den Teufel, den wir nicht kennen.

    – jww

    28. Januar 2014 um 17:34 Uhr

  • Muss sich der Client/Server nicht auf einen gemeinsamen Algorithmus einigen? In diesem Fall können Sie einfach alle unerwünschten Algorithmen aus der Liste der unterstützten Algorithmen entfernen. stackoverflow.com/a/4807830/1162141 hat einige gute Beispiele und Sie können den Kontext wie in ändern msdn.microsoft.com/en-us/library/windows/desktop/…

    – Technosaurier

    14. Juni 2014 um 8:21 Uhr


Benutzeravatar von Timtech
Timtech

Der Schlüsseltransportalgorithmus ist etwas schwierig zu handhaben und erfüllt möglicherweise nicht seinen Zweck (wie ich sehe, haben Sie angemerkt, dass CAPI unterstützt werden soll RSAencryption; vertrau mir, das würde ich auch). Es sieht so aus, als hätten Sie den Großteil Ihres Problems bereits erkannt – Die generierte Nachricht ist gültig, aber Ihre Methode macht es notwendig, sie zu verwenden CryptEncryptMessagewas auf Dauer nicht gut/überhaupt nicht funktionieren wird.

Schritt 1 – Untersuchen Sie den Code

CRYPT_ENCRYPT_MESSAGE_PARA EncryptMessageParams;
EncryptMessageParams.cbSize = sizeof(CMSG_ENVELOPED_ENCODE_INFO);

EncryptMessageParams.dwMsgEncodingType = PKCS_7_ASN_ENCODING;

EncryptMessageParams.ContentEncryptionAlgorithm.pszObjId = szOID_NIST_AES256_CBC;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.cbData = 0;
EncryptMessageParams.ContentEncryptionAlgorithm.Parameters.pbData = 0;

EncryptMessageParams.hCryptProv = NULL;
EncryptMessageParams.pvEncryptionAuxInfo = NULL;
EncryptMessageParams.dwFlags = 0;
EncryptMessageParams.dwInnerContentType = 0;

BYTE pbEncryptedBlob[640000];
DWORD pcbEncryptedBlob = 640000;

BOOL retval =  CryptEncryptMessage(&EncryptMessageParams, cRecipientCert, pRecipCertContextArray, pbMsgText, dwMsgTextSize, pbEncryptedBlob, &pcbEncryptedBlob);

Ziemlich einfach, nicht wahr? Obwohl effizient, löst es das Problem nicht wirklich. Wenn Sie sich das ansehen:

EncryptMessageParams.dwFlags = 0;
EncryptMessageParams.dwInnerContentType = 0;

Sie werden sehen, dass es vordefiniert ist, aber nur in der Definition von verwendet wird retval. Ich könnte dies jedoch definitiv als Mikrooptimierung sehen und nicht wirklich nützlich, wenn wir den Code neu schreiben. Ich habe jedoch die grundlegenden Schritte beschrieben, um dies zu integrieren, ohne den Code vollständig zu wiederholen (damit Sie weiterhin dieselben Parameter verwenden können):

Schritt 2 – Bearbeiten der Parameter

Wie @owlstead in seinen Kommentaren erwähnte, ist die Crypto-API nicht sehr benutzerfreundlich. Sie haben jedoch mit begrenzten Ressourcen großartige Arbeit geleistet. Was Sie hinzufügen möchten, ist a Kryptografischer Enumerationsanbieter um die Schlüssel einzugrenzen. Stellen Sie sicher, dass Sie entweder Microsoft Base Cryptographic Provider Version 1.0 oder Microsoft Enhanced Cryptographic Provider Version 1.0 haben, um diese effizient zu verwenden. Andernfalls müssen Sie die Funktion wie folgt hinzufügen:

DWORD cbName;
DWORD dwType;
DWORD dwIndex;
CHAR *pszName = NULL;
(regular crypt calls here)

Dies dient hauptsächlich der Vorbeugung NTE_BAD_FLAGS Fehler, obwohl Sie dies technisch mit einer Deklaration auf niedrigerer Ebene vermeiden könnten. Wenn Sie möchten, können Sie auch einen ganz neuen Hash erstellen (obwohl dies nur erforderlich ist, wenn die obige Implementierung nicht auf den erforderlichen Faktor Zeit / Geschwindigkeit skaliert):

DWORD dwBufferLen = strlen((char *)pbBuffer)+1*(0+5);
HCRYPTHASH hHash;
HCRYPTKEY hKey;
HCRYPTKEY hPubKey;
BYTE *pbKeyBlob;
BYTE *pbSignature;
DWORD dwSigLen;
DWORD dwBlobLen;
(use hash as normal w/ crypt calls and the pbKeyBlobs/Signatures)

Überprüfen Sie dieses Snippet, bevor Sie fortfahren. Das geht ganz einfach so:

if(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
     printf("CSP context acquired.\n");
}

Wenn Sie dokumentieren oder veröffentlichen, möchten Sie vielleicht eine hinzufügen void MyHandleError(char *s) um den Fehler abzufangen, damit jemand, der bearbeitet, aber fehlschlägt, ihn schnell abfangen kann.

Übrigens, wenn Sie es zum ersten Mal ausführen, müssen Sie ein neues Set erstellen, da es keinen Standardwert gibt. Ein schöner Einzeiler, der in einen eingefügt werden kann if ist unterhalb:

CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)

Denken Sie daran, dass die Synchronisierung von Serverressourcen erfolgt nicht so effizient sein wie die Überarbeitung, die ich im ersten Schritt vorgeschlagen habe. Das erkläre ich im Folgenden:

Schritt 3 – Neu codieren und neu starten

Als Programmierer mag das Umcodieren wie Zeitverschwendung erscheinen, aber es kann Ihnen auf lange Sicht definitiv helfen. Denken Sie daran, dass Sie beim Kodieren/Synchronisieren immer noch die benutzerdefinierten Parameter eingeben müssen; Ich werde Ihnen nicht den ganzen Code wie ein Baby von Hand füttern. Es sollte ausreichen, um Ihnen die grundlegenden Umrisse zu zeigen.

Ich gehe definitiv davon aus, dass Sie versuchen, mit dem Schlüsselcontainer des aktuellen Benutzers umzugehen innerhalb eines bestimmten CSP; ansonsten sehe ich keinen Nutzen davon. Wenn nicht, können Sie einige grundlegende Änderungen vornehmen, die Ihren Anforderungen entsprechen.

Denken Sie daran, wir werden umgehen CryptEncryptMessage durch die Nutzung CryptReleaseContextwodurch das von der erworbene Handle direkt freigegeben wird CryptAcquireContext Funktion. Der Standard von Microsoft zum CAC ist unten:

BOOL WINAPI CryptAcquireContext(
  _Out_  HCRYPTPROV *phProv,
  _In_   LPCTSTR pszContainer,
  _In_   LPCTSTR pszProvider,
  _In_   DWORD dwProvType,
  _In_   DWORD dwFlags
);

Beachten Sie, dass Microsoft Sie schimpft, wenn Sie eine Benutzeroberfläche verwenden:

Wenn der CSP die Benutzeroberfläche anzeigen muss, um zu funktionieren, schlägt der Aufruf fehl und der Fehlercode NTE_SILENT_CONTEXT wird als letzter Fehler festgelegt. Wenn Aufrufe an CryptGenKey mit dem CRYPT_USER_PROTECTED-Flag mit einem Kontext erfolgen, der mit dem CRYPT_SILENT-Flag erfasst wurde, schlagen die Aufrufe fehl und der CSP setzt NTE_SILENT_CONTEXT.

Dies ist hauptsächlich Servercode und die ERROR_BUSY wird neuen Benutzern auf jeden Fall angezeigt, wenn mehrere Verbindungen bestehen, insbesondere solche mit hoher Latenz. Über 300 ms wird nur a verursacht NTE_BAD_KEYSET_PARAM o.ä. aufgrund des Timeouts aufgerufen werden, ohne dass auch nur ein richtiger Fehler empfangen wird. (Übertragungsprobleme, jemand bei mir?)

Es sei denn, Sie sind besorgt über mehrere DLLs (die dies aufgrund von NTE_PROVIDER_DLL_FAIL Fehler), wäre die grundlegende Einrichtung zum Abrufen von Kryptdiensten clientseitig wie folgt (direkt aus den Beispielen von Microsoft kopiert):

if (GetLastError() == NTE_BAD_KEYSET)
 {
   if(CryptAcquireContext(
      &hCryptProv, 
      UserName, 
      NULL, 
      PROV_RSA_FULL, 
      CRYPT_NEWKEYSET)) 
    {
      printf("A new key container has been created.\n");
    }
    else
    {
      printf("Could not create a new key container.\n");
      exit(1);
    }
  }
  else
  {
      printf("A cryptographic service handle could not be "
          "acquired.\n");
      exit(1);
   }

So einfach dies auch erscheinen mag, Sie möchten auf keinen Fall daran hängen bleiben, dies an den Schlüsselaustauschalgorithmus (oder was auch immer Sie sonst noch handhaben) weiterzugeben. Sofern Sie keine symmetrischen Sitzungsschlüssel (Diffie-Hellman/KEA) verwenden, kann das Austauschschlüsselpaar zum Verschlüsseln von Sitzungsschlüsseln verwendet werden, damit sie sicher gespeichert und mit anderen Benutzern ausgetauscht werden können.

Jemand namens John Howard hat ein nettes Hyper-V Remote Management Configuration Utility (HVRemote) geschrieben, das eine große Zusammenstellung der hier besprochenen Techniken darstellt. Zusätzlich zur Verwendung der grundlegenden Krypten und Schlüsselpaare können sie zum Zulassen verwendet werden ANONYMOUS LOGON Fernbedienung DCOM Zugang (cscript hvremote.wsf, um genau zu sein). Sie können viele der Funktionen und Techniken in seinen neuesten Krypten (Sie müssen die Abfrage einschränken) in seinem Blog sehen:

http://blogs.technet.com/b/jhoward/

Wenn Sie weitere Hilfe bei den Grundlagen benötigen, hinterlassen Sie einfach einen Kommentar oder fordern Sie einen privaten Chat an.

Fazit

Obwohl es ziemlich einfach ist, sobald Sie die grundlegenden serverseitigen Methoden für das Hashing und die Art und Weise, wie der Client die „Krypten“ erfasst, verstanden haben, werden Sie sich fragen, warum Sie die Verschlüsselung während der Übertragung überhaupt versucht haben. Ohne die kryptierende Clientseite wäre die Verschlüsselung jedoch definitiv die einzige sichere Möglichkeit, das zu übertragen, was bereits gehasht wurde.

Obwohl Sie argumentieren könnten, dass die Pakete entschlüsselt und von den Salzen gehasht werden könnten, sollten Sie bedenken, dass sowohl ein- als auch ausgehende Daten zum richtigen Zeitpunkt verarbeitet und gespeichert werden müssten und um clientseitig erneut zu hashen.

1416560cookie-checkMicrosoft Crypto API Verwendung des RSAES-OAEP-Schlüsseltransportalgorithmus deaktivieren

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy