70 votes

L'API Crypto de Microsoft désactive l'utilisation de l'algorithme de transport de clés RSAES-OAEP

J'utilise CryptEncryptMessage pour générer un PKCS#7 message enveloppé. J'utilise szOID_NIST_AES256_CBC comme algorithme de cryptage.

Le message généré semble être valide mais est-ce que le RSAES-OAEP pour l'algorithme de transport des clés, qui n'est que peu pris en charge dans la nature (Thunderbird, le module SMIME d'OpenSSL, entre autres, ne le prennent pas en charge).

J'aimerais que le CAPI revienne à l'ancienne version. RSAencryption pour le transport des clés.

Y a-t-il un moyen de le faire ? Je pourrais revenir aux fonctions de messagerie de bas niveau s'il y a un moyen de le faire plutôt que d'utiliser la fonction CryptEncryptMessage mais je ne trouve pas le moyen de le faire, même en utilisant les fonctions de bas niveau.

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);

2voto

Timtech Points 228

L'algorithme de transport de clés est un peu délicat à manipuler, et il peut ne pas servir son objectif (je vois que vous avez noté que vous aimeriez que l'interface CAPI supporte RSAencryption (croyez-moi, je le ferais aussi). Il semble que vous ayez déjà détecté l'essentiel de votre problème - Le message généré apparaît est valide, mais votre méthode rend nécessaire l'utilisation de CryptEncryptMessage ce qui ne fonctionnera pas bien/du tout sur le long terme.

Étape 1 - Examiner le 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);

Plutôt basique, n'est-ce pas ? Bien qu'efficace, elle ne permet pas vraiment de résoudre le problème. Si vous regardez ça :

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

vous verrez qu'il est prédéfini, mais utilisé uniquement dans la définition de retval . Cependant, je pourrais certainement voir cela comme une micro-optimisation, et pas vraiment utile si nous allons réécrire le code. Cependant, j'ai décrit les étapes de base pour intégrer cette fonctionnalité sans avoir à refaire le code (vous pouvez donc continuer à utiliser les mêmes paramètres) :

Étape 2 - Modification des paramètres

Comme @owlstead l'a mentionné dans ses commentaires, l'API Crypto n'est pas très conviviale. Cependant, vous avez fait un excellent travail avec des ressources limitées. Ce que vous voulez ajouter est un Fournisseur d'énumération cryptographique pour aider à réduire les clés. Assurez-vous que vous disposez de Microsoft Base Cryptographic Provider version 1.0 ou de Microsoft Enhanced Cryptographic Provider version 1.0 pour les utiliser efficacement. Sinon, vous devrez ajouter la fonction comme suit :

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

Ceci est principalement utilisé pour éviter que le NTE_BAD_FLAGS bien que, techniquement, vous puissiez éviter cette erreur avec une déclaration de plus bas niveau. Si vous le souhaitez, vous pouvez également créer un tout nouveau hachage (bien que cela ne soit nécessaire que si l'implémentation ci-dessus ne s'adapte pas au facteur temps/vitesse nécessaire) :

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)

Veillez à valider cet extrait avant de poursuivre. Vous pouvez le faire facilement comme ceci :

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

Si vous documentez ou publiez, vous pouvez ajouter un fichier de type void MyHandleError(char *s) pour détecter l'erreur afin que quelqu'un qui édite mais échoue puisse la détecter rapidement.

À propos, la première fois que vous l'exécutez, vous devez créer un nouveau jeu car il n'y a pas de valeur par défaut. Une belle ligne simple qui peut être insérée dans un fichier if est ci-dessous :

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

N'oubliez pas que la synchronisation des ressources du serveur no être aussi efficace que de refaire le travail que j'ai suggéré dans la première étape. C'est ce que je vais expliquer ci-dessous :

Étape 3 - Recoder et relancer

En tant que programmeur, le recodage peut sembler être une perte de temps, mais il peut certainement vous aider à long terme. Rappelez-vous que vous devrez toujours coder les paramètres personnalisés lors de l'encodage/synchronisation ; je ne vais pas vous donner tout le code comme un bébé. Cela devrait être bien suffisant pour vous montrer les grandes lignes de base.

Je suppose que vous essayez de gérer le conteneur de clés de l'utilisateur actuel. au sein d'un CSP particulier ; sinon, je n'en vois pas vraiment l'utilité. Sinon, vous pouvez faire quelques modifications de base pour répondre à vos besoins.

Rappelez-vous, nous allons contourner CryptEncryptMessage en utilisant CryptReleaseContext qui libère directement le handle acquis par la fonction CryptAcquireContext fonction. La norme de Microsoft sur le CAC est présentée ci-dessous :

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

Notez que Microsoft vous gronde si vous utilisez une interface utilisateur :

Si le CSP doit afficher l'interface utilisateur pour fonctionner, l'appel échoue et le code d'erreur NTE_SILENT_CONTEXT est défini comme dernière erreur. En outre, si des appels sont faits à CryptGenKey avec l'indicateur CRYPT_USER_PROTECTED avec un contexte qui a été acquis avec l'indicateur CRYPT_SILENT, les appels échouent et le CSP définit NTE_SILENT_CONTEXT.

Il s'agit principalement de code serveur, et le ERROR_BUSY s'affichera définitivement pour les nouveaux utilisateurs lorsqu'il y a plusieurs connexions, surtout celles avec une latence élevée. Au-delà de 300 ms, un message NTE_BAD_KEYSET_PARAM ou similaire à être appelé, en raison du délai d'attente sans même qu'une erreur correcte soit reçue. (Problèmes de transmission, quelqu'un me suit ?)

À moins que vous ne soyez concerné par les DLL multiples (ce qui n'est pas pris en charge par le système en raison de l'absence d'un système de contrôle de la qualité). NTE_PROVIDER_DLL_FAIL ), la configuration de base pour récupérer les services de cryptage côté client serait la suivante (copiée directement des exemples de Microsoft) :

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);
   }

Aussi simple que cela puisse paraître, vous ne voulez surtout pas vous retrouver coincé à transmettre cette information à l'algorithme d'échange de clés (ou à tout autre algorithme qui gère cette fonction). À moins que vous n'utilisiez des clés de session symétriques (Diffie-Hellman/KEA), la paire de clés d'échange peut être utilisée pour chiffrer les clés de session afin qu'elles puissent être stockées et échangées en toute sécurité avec d'autres utilisateurs.

Un certain John Howard a écrit un bel utilitaire de configuration de la gestion à distance de Hyper-V (HVRemote) qui est une grande compilation des techniques discutées ici. En plus de l'utilisation des cryptes et des paires de clés de base, elles peuvent être utilisées pour permettre ANONYMOUS LOGON à distance DCOM accès ( cscript hvremote.wsf pour être précis). Vous pouvez voir un grand nombre de fonctions et de techniques dans ses derniers cryptes (vous devrez affiner la recherche) sur son blog :

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

Si vous avez besoin d'une aide supplémentaire pour les notions de base, laissez un commentaire ou demandez un chat privé.

Conclusion

Bien que ce soit assez simple, une fois que vous aurez compris les méthodes de hachage de base côté serveur et la façon dont le client saisit les "cryptes", vous vous demanderez pourquoi vous avez même essayé le cryptage pendant les transmissions. Cependant, sans le cryptage côté client, les cryptages seraient certainement le seul moyen sûr de transmettre ce qui a déjà été haché.

Bien que vous puissiez argumenter que les paquets pourraient être décryptés et hachés à partir des sels, considérez que les entrées et les sorties devraient être traitées et stockées dans le bon timing. y l'ordre nécessaire à la reprise de la clientèle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X