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.