diff --git a/reactos/dll/win32/crypt32/chain.c b/reactos/dll/win32/crypt32/chain.c index 6d7bb54e2d6..5943117ddda 100644 --- a/reactos/dll/win32/crypt32/chain.c +++ b/reactos/dll/win32/crypt32/chain.c @@ -28,6 +28,7 @@ #include "crypt32_private.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); +WINE_DECLARE_DEBUG_CHANNEL(chain); #define DEFAULT_CYCLE_MODULUS 7 @@ -355,7 +356,7 @@ static void CRYPT_CheckRootCert(HCERTCHAINENGINE hRoot, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)root, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root, 0, NULL)) { - TRACE("Last certificate's signature is invalid\n"); + TRACE_(chain)("Last certificate's signature is invalid\n"); rootElement->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_SIGNATURE_VALID; } @@ -411,8 +412,12 @@ static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert, } /* Checks element's basic constraints to see if it can act as a CA, with - * remainingCAs CAs left in this chain. Updates chainConstraints with the - * element's constraints, if: + * remainingCAs CAs left in this chain. A root certificate is assumed to be + * allowed to be a CA whether or not the basic constraints extension is present, + * whereas an intermediate CA cert is not. This matches the expected usage in + * RFC 3280: a conforming intermediate CA MUST contain the basic constraints + * extension. It also appears to match Microsoft's implementation. + * Updates chainConstraints with the element's constraints, if: * 1. chainConstraints doesn't have a path length constraint, or * 2. element's path length constraint is smaller than chainConstraints's * Sets *pathLengthConstraintViolated to TRUE if a path length violation @@ -422,17 +427,17 @@ static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert, */ static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert, CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints, DWORD remainingCAs, - BOOL *pathLengthConstraintViolated) + BOOL isRoot, BOOL *pathLengthConstraintViolated) { BOOL validBasicConstraints; CERT_BASIC_CONSTRAINTS2_INFO constraints; if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert, - &constraints, TRUE))) + &constraints, isRoot))) { if (!constraints.fCA) { - TRACE("chain element %d can't be a CA\n", remainingCAs + 1); + TRACE_(chain)("chain element %d can't be a CA\n", remainingCAs + 1); validBasicConstraints = FALSE; } else if (constraints.fPathLenConstraint) @@ -444,7 +449,7 @@ static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert, constraints.dwPathLenConstraint < chainConstraints->dwPathLenConstraint) { - TRACE("setting path length constraint to %d\n", + TRACE_(chain)("setting path length constraint to %d\n", chainConstraints->dwPathLenConstraint); chainConstraints->fPathLenConstraint = TRUE; chainConstraints->dwPathLenConstraint = @@ -455,8 +460,8 @@ static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert, if (chainConstraints->fPathLenConstraint && remainingCAs > chainConstraints->dwPathLenConstraint) { - TRACE("remaining CAs %d exceed max path length %d\n", remainingCAs, - chainConstraints->dwPathLenConstraint); + TRACE_(chain)("remaining CAs %d exceed max path length %d\n", + remainingCAs, chainConstraints->dwPathLenConstraint); validBasicConstraints = FALSE; *pathLengthConstraintViolated = TRUE; } @@ -709,6 +714,100 @@ static void CRYPT_CheckChainNameConstraints(PCERT_SIMPLE_CHAIN chain) } } +static void dump_basic_constraints(PCERT_EXTENSION ext) +{ + CERT_BASIC_CONSTRAINTS_INFO *info; + DWORD size = 0; + + if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS, + ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, + NULL, &info, &size)) + { + TRACE_(chain)("SubjectType: %02x\n", info->SubjectType.pbData[0]); + TRACE_(chain)("%s path length constraint\n", + info->fPathLenConstraint ? "has" : "doesn't have"); + TRACE_(chain)("path length=%d\n", info->dwPathLenConstraint); + LocalFree(info); + } +} + +static void dump_basic_constraints2(PCERT_EXTENSION ext) +{ + CERT_BASIC_CONSTRAINTS2_INFO constraints; + DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO); + + if (CryptDecodeObjectEx(X509_ASN_ENCODING, + szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData, + 0, NULL, &constraints, &size)) + { + TRACE_(chain)("basic constraints:\n"); + TRACE_(chain)("can%s be a CA\n", constraints.fCA ? "" : "not"); + TRACE_(chain)("%s path length constraint\n", + constraints.fPathLenConstraint ? "has" : "doesn't have"); + TRACE_(chain)("path length=%d\n", constraints.dwPathLenConstraint); + } +} + +static void dump_extension(PCERT_EXTENSION ext) +{ + TRACE_(chain)("%s (%scritical)\n", debugstr_a(ext->pszObjId), + ext->fCritical ? "" : "not "); + if (!strcmp(ext->pszObjId, szOID_BASIC_CONSTRAINTS)) + dump_basic_constraints(ext); + else if (!strcmp(ext->pszObjId, szOID_BASIC_CONSTRAINTS2)) + dump_basic_constraints2(ext); +} + +static LPCWSTR filetime_to_str(const FILETIME *time) +{ + static WCHAR date[80]; + WCHAR dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */ + SYSTEMTIME sysTime; + + if (!time) return NULL; + + GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt, + sizeof(dateFmt) / sizeof(dateFmt[0])); + FileTimeToSystemTime(time, &sysTime); + GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date, + sizeof(date) / sizeof(date[0])); + return date; +} + +static void dump_element(PCCERT_CONTEXT cert) +{ + LPWSTR name = NULL; + DWORD len, i; + + TRACE_(chain)("%p\n", cert); + len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, + CERT_NAME_ISSUER_FLAG, NULL, NULL, 0); + name = CryptMemAlloc(len * sizeof(WCHAR)); + if (name) + { + CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, + CERT_NAME_ISSUER_FLAG, NULL, name, len); + TRACE_(chain)("issued by %s\n", debugstr_w(name)); + CryptMemFree(name); + } + len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, + NULL, 0); + name = CryptMemAlloc(len * sizeof(WCHAR)); + if (name) + { + CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, + name, len); + TRACE_(chain)("issued to %s\n", debugstr_w(name)); + CryptMemFree(name); + } + TRACE_(chain)("valid from %s to %s\n", + debugstr_w(filetime_to_str(&cert->pCertInfo->NotBefore)), + debugstr_w(filetime_to_str(&cert->pCertInfo->NotAfter))); + TRACE_(chain)("%d extensions\n", cert->pCertInfo->cExtension); + for (i = 0; i < cert->pCertInfo->cExtension; i++) + dump_extension(&cert->pCertInfo->rgExtension[i]); +} + static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine, PCERT_SIMPLE_CHAIN chain, LPFILETIME time) { @@ -717,14 +816,25 @@ static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine, BOOL pathLengthConstraintViolated = FALSE; CERT_BASIC_CONSTRAINTS2_INFO constraints = { TRUE, FALSE, 0 }; + TRACE_(chain)("checking chain with %d elements for time %s\n", + chain->cElement, debugstr_w(filetime_to_str(time))); for (i = chain->cElement - 1; i >= 0; i--) { + if (TRACE_ON(chain)) + dump_element(chain->rgpElement[i]->pCertContext); if (CertVerifyTimeValidity(time, chain->rgpElement[i]->pCertContext->pCertInfo) != 0) chain->rgpElement[i]->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_NOT_TIME_VALID; if (i != 0) { + BOOL isRoot; + + if (i == chain->cElement - 1) + isRoot = CRYPT_IsCertificateSelfSigned( + chain->rgpElement[i]->pCertContext); + else + isRoot = FALSE; /* Check the signature of the cert this issued */ if (!CryptVerifyCertificateSignatureEx(0, X509_ASN_ENCODING, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, @@ -741,7 +851,7 @@ static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine, CERT_TRUST_INVALID_BASIC_CONSTRAINTS; else if (!CRYPT_CheckBasicConstraintsForCA( chain->rgpElement[i]->pCertContext, &constraints, i - 1, - &pathLengthConstraintViolated)) + isRoot, &pathLengthConstraintViolated)) chain->rgpElement[i]->TrustStatus.dwErrorStatus |= CERT_TRUST_INVALID_BASIC_CONSTRAINTS; else if (constraints.fPathLenConstraint && @@ -884,8 +994,7 @@ static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject, issuer = CertFindCertificateInStore(store, subject->dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME, &subject->pCertInfo->Issuer, prevIssuer); - if (issuer) - *infoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER; + *infoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER; } return issuer; } @@ -902,12 +1011,13 @@ static BOOL CRYPT_BuildSimpleChain(PCertificateChainEngine engine, while (ret && !CRYPT_IsSimpleChainCyclic(chain) && !CRYPT_IsCertificateSelfSigned(cert)) { - DWORD infoStatus; - PCCERT_CONTEXT issuer = CRYPT_GetIssuer(world, cert, NULL, &infoStatus); + PCCERT_CONTEXT issuer = CRYPT_GetIssuer(world, cert, NULL, + &chain->rgpElement[chain->cElement - 1]->TrustStatus.dwInfoStatus); if (issuer) { - ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer, infoStatus); + ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer, + chain->rgpElement[chain->cElement - 1]->TrustStatus.dwInfoStatus); /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it to * close the enumeration that found it */ @@ -916,7 +1026,7 @@ static BOOL CRYPT_BuildSimpleChain(PCertificateChainEngine engine, } else { - TRACE("Couldn't find issuer, halting chain creation\n"); + TRACE_(chain)("Couldn't find issuer, halting chain creation\n"); chain->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_PARTIAL_CHAIN; break; } diff --git a/reactos/dll/win32/crypt32/crypt32_Ko.rc b/reactos/dll/win32/crypt32/crypt32_Ko.rc index 2968e52856d..51e3fc32f67 100644 --- a/reactos/dll/win32/crypt32/crypt32_Ko.rc +++ b/reactos/dll/win32/crypt32/crypt32_Ko.rc @@ -172,6 +172,7 @@ STRINGTABLE DISCARDABLE IDS_LOCALIZEDNAME_MY "°³ÀÎ" IDS_LOCALIZEDNAME_CA "Áß°³ °ËÁõ ±â°ü" IDS_LOCALIZEDNAME_ADDRESSBOOK "´Ù¸¥ »ç¶÷" + IDS_LOCALIZEDNAME_TRUSTEDPUBLISHER "½Å·ÚÇÒ ¼ö ÀÖ´Â ¹ßÇàÀÚ" } STRINGTABLE DISCARDABLE diff --git a/reactos/dll/win32/cryptui/cryptui_En.rc b/reactos/dll/win32/cryptui/cryptui_En.rc index 1e311a1aa48..b10e1015522 100644 --- a/reactos/dll/win32/cryptui/cryptui_En.rc +++ b/reactos/dll/win32/cryptui/cryptui_En.rc @@ -102,8 +102,8 @@ STRINGTABLE DISCARDABLE IDS_FRIENDLY_NAME_COLUMN "Friendly Name" IDS_ALLOWED_PURPOSE_ALL "" IDS_ALLOWED_PURPOSE_NONE "" - IDS_WARN_REMOVE_MY "You will no longer be able to decrypt mesages with this certificate, or sign messages with it.\nAre you sure you want to remove this certificate?" - IDS_WARN_REMOVE_PLURAL_MY "You will no longer be able to decrypt mesages with these certificate, or sign messages with them.\nAre you sure you want to remove these certificates?" + IDS_WARN_REMOVE_MY "You will no longer be able to decrypt messages with this certificate, or sign messages with it.\nAre you sure you want to remove this certificate?" + IDS_WARN_REMOVE_PLURAL_MY "You will no longer be able to decrypt messages with these certificates, or sign messages with them.\nAre you sure you want to remove these certificates?" IDS_WARN_REMOVE_ADDRESSBOOK "You will no longer be able to encrypt messages with this certificate, or verify messages signed with it.\nAre you sure you want to remove this certificate?" IDS_WARN_REMOVE_PLURAL_ADDRESSBOOK "You will no longer be able to encrypt messages with these certificates, or verify messages signed with it.\nAre you sure you want to remove these certificates?" IDS_WARN_REMOVE_CA "Certificates issued by this certification authority will no longer be trusted.\nAre you sure you want to remove this certificate?" @@ -161,6 +161,13 @@ STRINGTABLE DISCARDABLE IDS_NO "No" IDS_EXPORT_SUCCEEDED "The export was successful." IDS_EXPORT_FAILED "The export failed." + IDS_EXPORT_PRIVATE_KEY_TITLE "Export Private Key" + IDS_EXPORT_PRIVATE_KEY_SUBTITLE "The certificate contains a private key which may be exported along with the certificate." + IDS_EXPORT_PASSWORD_TITLE "Enter Password" + IDS_EXPORT_PASSWORD_SUBTITLE "You may password-protect a private key." + IDS_EXPORT_PASSWORD_MISMATCH "The passwords do not match." + IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE "Note: The private key for this certificate could not be opened." + IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE "Note: The private key for this certificate is not exportable." } IDD_GENERAL DIALOG DISCARDABLE 0, 0, 255, 236 @@ -381,6 +388,29 @@ BEGIN -1, 115,103,195,8 END +IDD_EXPORT_PRIVATE_KEY DIALOG DISCARDABLE 0,0,317,143 +CAPTION "Certificate Export Wizard" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "If you choose to export the private key, you will be prompted for a password to protect the private key on a later page.", -1, 21,1,195,23 + LTEXT "Do you wish to export the private key?", -1, 21,24,195,15 + AUTORADIOBUTTON "&Yes, export the private key", + IDC_EXPORT_PRIVATE_KEY_YES, 31,36,200,12, BS_AUTORADIOBUTTON|WS_TABSTOP + AUTORADIOBUTTON "N&o, do not export the private key", + IDC_EXPORT_PRIVATE_KEY_NO, 31,48,200,12, BS_AUTORADIOBUTTON + LTEXT "", IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE, 21,60,200,24 +END + +IDD_EXPORT_PASSWORD DIALOG DISCARDABLE 0,0,317,143 +CAPTION "Certificate Export Wizard" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "&Password:", -1, 21,1,195,10 + EDITTEXT IDC_EXPORT_PASSWORD, 21,11,208,14, ES_AUTOHSCROLL|WS_TABSTOP + LTEXT "&Confirm password:", -1, 21,35,195,10 + EDITTEXT IDC_EXPORT_PASSWORD_CONFIRM, 21,45,208,14, ES_AUTOHSCROLL|WS_TABSTOP +END + IDD_EXPORT_FORMAT DIALOG DISCARDABLE 0,0,317,143 CAPTION "Certificate Export Wizard" FONT 8, "MS Shell Dlg" diff --git a/reactos/dll/win32/cryptui/cryptui_Ko.rc b/reactos/dll/win32/cryptui/cryptui_Ko.rc index b08671b639e..57e91386bd8 100644 --- a/reactos/dll/win32/cryptui/cryptui_Ko.rc +++ b/reactos/dll/win32/cryptui/cryptui_Ko.rc @@ -143,6 +143,34 @@ STRINGTABLE DISCARDABLE IDS_PURPOSE_CA_EXCHANGE "»çÀû Å° º¸°ü¼Ò" IDS_PURPOSE_KEY_RECOVERY_AGENT "Å° º¹±¸ ¿¡ÀÌÀüÆ®" IDS_PURPOSE_DS_EMAIL_REPLICATION "µð·ºÅ丮 ¼­ºñ½º ÀüÀÚ¿ìÆí º¹Á¦" + IDS_EXPORT_WIZARD "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç" + IDS_EXPORT_FORMAT_TITLE "³»º¸³¾ Çü½Ä" + IDS_EXPORT_FORMAT_SUBTITLE "³»¿ëÀ» ÀúÀåÇÒ Çü½Ä ¼±ÅÃ." + IDS_EXPORT_FILE_TITLE "³»º¸³¾ ÆÄÀÏÀ̸§" + IDS_EXPORT_FILE_SUBTITLE "³»¿ëÀ» ÀúÀåÇÒ ÆÄÀÏ À̸§ ÁöÁ¤." + IDS_EXPORT_FILE_EXISTS "ÁöÁ¤µÈ ÆÄÀÏÀº ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù.µ¤¾î ¾²½Ã°Ú½À´Ï±î?" + IDS_EXPORT_FILTER_CERT "DER-¾ÏȣȭµÈ ¹ÙÀ̳ʸ® X.509 (*.cer)" + IDS_EXPORT_FILTER_BASE64_CERT "Base64-¾ÏȣȭµÈ X.509 (*.cer)" + IDS_EXPORT_FILTER_CRL "ÀÎÁõ¼­ Æó±â ¸ñ·Ï (*.crl)" + IDS_EXPORT_FILTER_CTL "ÀÎÁõ¼­ ½Å·Ú ¸ñ·Ï (*.stl)" + IDS_EXPORT_FILTER_CMS "CMS/PKCS #7 ¸Þ¼¼Áö (*.p7b)" + IDS_EXPORT_FILTER_PFX "°³ÀÎ Á¤º¸ ±³È¯ (*.pfx)" + IDS_EXPORT_FILTER_SERIALIZED_CERT_STORE "³ª¿­µÈ ÀÎÁõ¼­ ÀúÀå¼Ò (*.sst)" + IDS_EXPORT_FORMAT "ÆÄÀÏ Çü½Ä" + IDS_EXPORT_INCLUDE_CHAIN "ÀÎÁõ °æ·Î¿¡ ÀÖ´Â ¸ðµç ÀÎÁõ¼­ Æ÷ÇÔ" + IDS_EXPORT_KEYS "³»º¸³¾ Å°" + IDS_YES "¿¹" + IDS_NO "¾Æ´Ï¿À" + IDS_EXPORT_SUCCEEDED "³»º¸³»±â ¼º°ø." + IDS_EXPORT_FAILED "³»º¸³»±â ½ÇÆÐ." + IDS_EXPORT_PRIVATE_KEY_TITLE "³»º¸³¾ °³ÀÎ Å°" + IDS_EXPORT_PRIVATE_KEY_SUBTITLE "ÀÌ ÀÎÁõ¼­´Â ÀÎÁõ¼­¸¦ ³»º¸³¾ ¶§ °°ÀÌ ³ª°¥ °³ÀÎÅ°¸¦ Æ÷ÇÔÇÏ°í ÀÖ½À´Ï´Ù." + IDS_EXPORT_PASSWORD_TITLE "¾ÏÈ£ ÀÔ·Â" + IDS_EXPORT_PASSWORD_SUBTITLE "ÀÌ °³ÀÎÅ°´Â ¾Æ¸¶µµ ¾ÏÈ£·Î º¸È£µÇ¾îÀÖ´Â °Í °°½À´Ï´Ù." + IDS_EXPORT_PASSWORD_MISMATCH "ÀÌ ¾ÏÈ£´Â ¸ÂÁö ¾Ê½À´Ï´Ù." + IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE "ÁÖÀÇ: ÀÌ ÀÎÁõ¼­¸¦ À§ÇÑ °³ÀÎ Å°¸¦ ¿­ ¼ö ¾ø½À´Ï´Ù." + IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE "ÁÖÀÇ: ÀÌ ÀÎÁõ¼­¸¦ À§ÇÑ °³ÀÎ Å°¸¦ ³»º¸³¾ ¼ö ¾ø½À´Ï´Ù." + } IDD_GENERAL DIALOG DISCARDABLE 0, 0, 255, 236 @@ -330,3 +358,90 @@ BEGIN PUSHBUTTON "È®ÀÎ", IDOK, 132,155,51,14, BS_DEFPUSHBUTTON PUSHBUTTON "Ãë¼Ò", IDCANCEL, 190,155,51,14 END + + +IDD_EXPORT_WELCOME DIALOG DISCARDABLE 0,0,317,143 +CAPTION "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç¿¡ ¿À½Å °ÍÀ» ȯ¿µÇÕ´Ï´Ù", IDC_EXPORT_TITLE, + 115,1,195,40 + LTEXT "ÀÌ ¸¶¹ý»ç´Â ÀÎÁõ¼­,ÀÎÁõ¼­ Æó±â ¸ñ·Ï,ÀÎÁõ¼­ ½Å·Ú ¸ñ·ÏÀ» ÀÎÁõ¼­ ÀúÀå¼Ò·ÎºÎÅÍ ÆÄÀÏ·Î ³»º¸³»´Â °ÍÀ» µµ¿ÍÁÙ°Ì´Ï´Ù.", + -1, 115,33,195,16 + LTEXT " ÀÎÁõ¼­´Â ´ç½ÅÀ̳ª ´ç½ÅÀÌ Åë½Å¿¡ »ç¿ëÇÏ´Â ÄÄÇ»Å͸¦ ½Å¿øº¸ÁõÇÏ´Â µ¥ »ç¿ëµË´Ï´Ù. ¶ÇÇÑ ¸Þ¼¼Áö¿¡ »çÀÎÇÏ°í ÀÎÁõÇÏ´Â µ¥µµ »ç¿ëµË´Ï´Ù. ÀÎÁõ¼­ º¸°ü¼Ò´Â ÀÎÁõ¼­, ÀÎÁõ¼­ Æı⠸ñ·Ï, ÀÎÁõ¼­ ½Å·Ú ¸ñ·ÏÀÇ ÀúÀå¼ÒÀÔ´Ï´Ù..", + -1, 115,56,195,40 + LTEXT "°è¼Ó ÇϽǷÁ¸é, <´ÙÀ½>À» Ŭ¸¯ÇϽʽÿÀ.", + -1, 115,103,195,8 +END + +IDD_EXPORT_PRIVATE_KEY DIALOG DISCARDABLE 0,0,317,143 +CAPTION "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "´ç½ÅÀÌ °³ÀÎ Å°¸¦ ³»º¸³»±â¸¦ ¼±ÅÃÇϸé, ´ç½ÅÀº ´ÙÀ½ ÆäÀÌÁö¿¡¼­ °³ÀÎ Å°¸¦ º¸È£ÇÒ ¾ÏÈ£¸¦ ÀÔ·ÂÇÏ°Ô µÉ °ÍÀÔ´Ï´Ù.", -1, 21,1,195,23 + LTEXT "°³ÀÎ Å°¸¦ ³»º¸³»±â¸¦ ¿øÇմϱî?", -1, 21,24,195,15 + AUTORADIOBUTTON "¿¹(&Y), °³ÀÎ Å° ³»º¸³»±â", + IDC_EXPORT_PRIVATE_KEY_YES, 31,36,200,12, BS_AUTORADIOBUTTON|WS_TABSTOP + AUTORADIOBUTTON "¾Æ´Ï¿À(&O), °³ÀÎ Å° ¾È ³»º¸³»±â", + IDC_EXPORT_PRIVATE_KEY_NO, 31,48,200,12, BS_AUTORADIOBUTTON + LTEXT "", IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE, 21,60,200,24 +END + +IDD_EXPORT_PASSWORD DIALOG DISCARDABLE 0,0,317,143 +CAPTION "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç" +FONT 8, "MS Shell Dlg" +BEGIN +LTEXT "¾ÏÈ£(&P):", -1, 21,1,195,10 +EDITTEXT IDC_EXPORT_PASSWORD, 21,11,208,14, ES_AUTOHSCROLL|WS_TABSTOP +LTEXT "¾ÏÈ£ È®ÀÎ(&C):", -1, 21,35,195,10 +EDITTEXT IDC_EXPORT_PASSWORD_CONFIRM, 21,45,208,14, ES_AUTOHSCROLL|WS_TABSTOP +END + +IDD_EXPORT_FORMAT DIALOG DISCARDABLE 0,0,317,143 +CAPTION "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "»ç¿ëÇÒ ÆÄÀÏ Çü½Ä ¼±ÅÃ:", -1, 21,1,195,10 + AUTORADIOBUTTON "&DER-¾ÏȣȭµÈ X.509 (.cer)", + IDC_EXPORT_FORMAT_DER, 31,18,200,12, BS_AUTORADIOBUTTON|WS_TABSTOP + AUTORADIOBUTTON "Ba&se64-¾ÏȣȭµÈ X.509 (.cer):", + IDC_EXPORT_FORMAT_BASE64, 31,30,200,12, BS_AUTORADIOBUTTON + AUTORADIOBUTTON "¾ÏÈ£ ¸Þ½ÃÁö ¹®¹ý Ç¥ÁØ/PKCS #7 ¸Þ½ÃÁö(&C) (.p7b)", + IDC_EXPORT_FORMAT_CMS, 31,42,200,12, BS_AUTORADIOBUTTON + CHECKBOX "°¡´ÉÇÑ ÀÎÁõ¼­ °æ·Î¿¡ ÀÖ´Â ¸ðµç ÀÎÁõ¼­ Æ÷ÇÔ(&I)", + IDC_EXPORT_CMS_INCLUDE_CHAIN, 44,57,200,8, BS_AUTOCHECKBOX|WS_TABSTOP|WS_DISABLED + AUTORADIOBUTTON "°³ÀÎ Á¤º¸ ±³È¯(&P)/PKCS #12 (.pfx)", + IDC_EXPORT_FORMAT_PFX, 31,72,200,12, BS_AUTORADIOBUTTON|WS_DISABLED + CHECKBOX "°¡´ÉÇÑ ÀÎÁõ¼­ °æ·Î¿¡ ÀÖ´Â ¸ðµç ÀÎÁõ¼­ Æ÷ÇÔ(&U)", + IDC_EXPORT_PFX_INCLUDE_CHAIN, 44,87,200,8, BS_AUTOCHECKBOX|WS_TABSTOP|WS_DISABLED + CHECKBOX "°­ÇÑ ¾Ïȣȭ °¡´É(&E)", + IDC_EXPORT_PFX_STRONG_ENCRYPTION, 44,102,200,8, + BS_AUTOCHECKBOX|WS_TABSTOP|WS_DISABLED + CHECKBOX "³»º¸³»±â°¡ ¼º°øÇÏ¸é °³ÀÎ Å° Áö¿ì±â(&K)", + IDC_EXPORT_PFX_DELETE_PRIVATE_KEY, 44,117,200,8, + BS_AUTOCHECKBOX|WS_TABSTOP|WS_DISABLED +END + +IDD_EXPORT_FILE DIALOG DISCARDABLE 0,0,317,143 +CAPTION "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ÆÄÀÏ À̸§(&F):", -1, 21,1,195,10 + EDITTEXT IDC_EXPORT_FILENAME, 21,11,208,14, ES_AUTOHSCROLL|WS_TABSTOP + PUSHBUTTON "ã±â(&R)...", IDC_EXPORT_BROWSE_FILE, 236,11,60,14 +END + +IDD_EXPORT_FINISH DIALOG DISCARDABLE 0,0,317,143 +CAPTION "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý»ç" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý¼­ ¿Ï·áÇÏ´Â Áß", IDC_EXPORT_TITLE, + 115,1,195,40 + LTEXT "´ç½ÅÀº ÀÎÁõ¼­ ³»º¸³»±â ¸¶¹ý¸¦ ¿Ï·áÇÏ´Â µ¥ ¼º°øÇÏ¿´½À´Ï´Ù.", + -1, 115,33,195,24 + LTEXT "´ç½ÅÀº ´ÙÀ½ ¼³Á¤À» ÁöÁ¤Çß½À´Ï´Ù:", + -1, 115,57,195,12 + CONTROL "", IDC_EXPORT_SETTINGS, "SysListView32", + LVS_REPORT|LVS_NOCOLUMNHEADER|LVS_SINGLESEL|WS_CHILD|WS_VISIBLE|WS_TABSTOP|WS_BORDER, + 115,67,174,100 +END diff --git a/reactos/dll/win32/cryptui/cryptuires.h b/reactos/dll/win32/cryptui/cryptuires.h index 486bae02b58..b46ea4e128e 100644 --- a/reactos/dll/win32/cryptui/cryptuires.h +++ b/reactos/dll/win32/cryptui/cryptuires.h @@ -160,6 +160,13 @@ #define IDS_NO 1217 #define IDS_EXPORT_SUCCEEDED 1218 #define IDS_EXPORT_FAILED 1219 +#define IDS_EXPORT_PRIVATE_KEY_TITLE 1220 +#define IDS_EXPORT_PRIVATE_KEY_SUBTITLE 1221 +#define IDS_EXPORT_PASSWORD_TITLE 1222 +#define IDS_EXPORT_PASSWORD_SUBTITLE 1223 +#define IDS_EXPORT_PASSWORD_MISMATCH 1224 +#define IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE 1225 +#define IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE 1226 #define IDD_GENERAL 100 #define IDD_DETAIL 101 @@ -175,9 +182,11 @@ #define IDD_CERT_MGR 111 #define IDD_CERT_MGR_ADVANCED 112 #define IDD_EXPORT_WELCOME 113 -#define IDD_EXPORT_FORMAT 114 -#define IDD_EXPORT_FILE 115 -#define IDD_EXPORT_FINISH 116 +#define IDD_EXPORT_PRIVATE_KEY 114 +#define IDD_EXPORT_PASSWORD 115 +#define IDD_EXPORT_FORMAT 116 +#define IDD_EXPORT_FILE 117 +#define IDD_EXPORT_FINISH 118 #define IDB_SMALL_ICONS 200 #define IDB_CERT 201 @@ -253,5 +262,10 @@ #define IDC_EXPORT_FILENAME 2909 #define IDC_EXPORT_BROWSE_FILE 2910 #define IDC_EXPORT_SETTINGS 2911 +#define IDC_EXPORT_PRIVATE_KEY_YES 2912 +#define IDC_EXPORT_PRIVATE_KEY_NO 2913 +#define IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE 2914 +#define IDC_EXPORT_PASSWORD 2915 +#define IDC_EXPORT_PASSWORD_CONFIRM 2916 #endif /* ndef __CRYPTUIRES_H_ */ diff --git a/reactos/dll/win32/cryptui/main.c b/reactos/dll/win32/cryptui/main.c index 498c158f9bf..af864088c76 100644 --- a/reactos/dll/win32/cryptui/main.c +++ b/reactos/dll/win32/cryptui/main.c @@ -1076,25 +1076,24 @@ static LRESULT CALLBACK cert_mgr_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, HWND tab = GetDlgItem(hwnd, IDC_MGR_STORES); data = HeapAlloc(GetProcessHeap(), 0, sizeof(struct CertMgrData)); - if (data) + if (!data) + return 0; + data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, 2, 0); + if (data->imageList) { - data->imageList = ImageList_Create(16, 16, ILC_COLOR4 | ILC_MASK, - 2, 0); - if (data->imageList) - { - HBITMAP bmp; - COLORREF backColor = RGB(255, 0, 255); + HBITMAP bmp; + COLORREF backColor = RGB(255, 0, 255); - bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS)); - ImageList_AddMasked(data->imageList, bmp, backColor); - DeleteObject(bmp); - ImageList_SetBkColor(data->imageList, CLR_NONE); - SendMessageW(GetDlgItem(hwnd, IDC_MGR_CERTS), LVM_SETIMAGELIST, - LVSIL_SMALL, (LPARAM)data->imageList); - } - SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); - data->title = pCryptUICertMgr->pwszTitle; + bmp = LoadBitmapW(hInstance, MAKEINTRESOURCEW(IDB_SMALL_ICONS)); + ImageList_AddMasked(data->imageList, bmp, backColor); + DeleteObject(bmp); + ImageList_SetBkColor(data->imageList, CLR_NONE); + SendMessageW(GetDlgItem(hwnd, IDC_MGR_CERTS), LVM_SETIMAGELIST, + LVSIL_SMALL, (LPARAM)data->imageList); } + SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); + data->title = pCryptUICertMgr->pwszTitle; + initialize_purpose_selection(hwnd); add_cert_columns(hwnd); if (pCryptUICertMgr->pwszTitle) @@ -5516,8 +5515,11 @@ struct ExportWizData HFONT titleFont; DWORD dwFlags; LPCWSTR pwszWizardTitle; - PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo; + CRYPTUI_WIZ_EXPORT_INFO exportInfo; CRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO contextInfo; + BOOL freePassword; + PCRYPT_KEY_PROV_INFO keyProvInfo; + BOOL deleteKeys; LPWSTR fileName; HANDLE file; BOOL success; @@ -5567,6 +5569,141 @@ static LRESULT CALLBACK export_welcome_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, return ret; } +static PCRYPT_KEY_PROV_INFO export_get_private_key_info(PCCERT_CONTEXT cert) +{ + PCRYPT_KEY_PROV_INFO info = NULL; + DWORD size; + + if (CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, + NULL, &size)) + { + info = HeapAlloc(GetProcessHeap(), 0, size); + if (info) + { + if (!CertGetCertificateContextProperty(cert, + CERT_KEY_PROV_INFO_PROP_ID, info, &size)) + { + HeapFree(GetProcessHeap(), 0, info); + info = NULL; + } + } + } + return info; +} + +static BOOL export_acquire_private_key(PCRYPT_KEY_PROV_INFO info, + HCRYPTPROV *phProv) +{ + BOOL ret; + + ret = CryptAcquireContextW(phProv, info->pwszContainerName, + info->pwszProvName, info->dwProvType, 0); + if (ret) + { + DWORD i; + + for (i = 0; i < info->cProvParam; i++) + CryptSetProvParam(*phProv, info->rgProvParam[i].dwParam, + info->rgProvParam[i].pbData, info->rgProvParam[i].dwFlags); + } + return ret; +} + +static BOOL export_is_key_exportable(HCRYPTPROV hProv, DWORD keySpec) +{ + BOOL ret; + HCRYPTKEY key; + + if ((ret = CryptGetUserKey(hProv, keySpec, &key))) + { + DWORD permissions, size = sizeof(permissions); + + if ((ret = CryptGetKeyParam(key, KP_PERMISSIONS, (BYTE *)&permissions, + &size, 0)) && !(permissions & CRYPT_EXPORT)) + ret = FALSE; + CryptDestroyKey(key); + } + return ret; +} + +static LRESULT CALLBACK export_private_key_dlg_proc(HWND hwnd, UINT msg, + WPARAM wp, LPARAM lp) +{ + LRESULT ret = 0; + struct ExportWizData *data; + + switch (msg) + { + case WM_INITDIALOG: + { + PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; + PCRYPT_KEY_PROV_INFO info; + HCRYPTPROV hProv = 0; + int errorID = 0; + + data = (struct ExportWizData *)page->lParam; + SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); + /* Get enough information about a key to see whether it's exportable. + */ + if (!(info = export_get_private_key_info( + data->exportInfo.u.pCertContext))) + errorID = IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE; + else if (!export_acquire_private_key(info, &hProv)) + errorID = IDS_EXPORT_PRIVATE_KEY_UNAVAILABLE; + else if (!export_is_key_exportable(hProv, info->dwKeySpec)) + errorID = IDS_EXPORT_PRIVATE_KEY_NON_EXPORTABLE; + + if (errorID) + { + WCHAR error[MAX_STRING_LEN]; + + LoadStringW(hInstance, errorID, error, + sizeof(error) / sizeof(error[0])); + SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_UNAVAILABLE), + WM_SETTEXT, 0, (LPARAM)error); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_YES), FALSE); + } + else + data->keyProvInfo = info; + if (hProv) + CryptReleaseContext(hProv, 0); + SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_PRIVATE_KEY_NO), BM_CLICK, + 0, 0); + break; + } + case WM_NOTIFY: + { + NMHDR *hdr = (NMHDR *)lp; + + switch (hdr->code) + { + case PSN_SETACTIVE: + PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, + PSWIZB_BACK | PSWIZB_NEXT); + ret = TRUE; + break; + case PSN_WIZNEXT: + data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); + if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PRIVATE_KEY_NO)) + { + data->contextInfo.dwExportFormat = + CRYPTUI_WIZ_EXPORT_FORMAT_DER; + data->contextInfo.fExportPrivateKeys = FALSE; + } + else + { + data->contextInfo.dwExportFormat = + CRYPTUI_WIZ_EXPORT_FORMAT_PFX; + data->contextInfo.fExportPrivateKeys = TRUE; + } + break; + } + break; + } + } + return ret; +} + static BOOL export_info_has_private_key(PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo) { BOOL ret = FALSE; @@ -5585,6 +5722,41 @@ static BOOL export_info_has_private_key(PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo) return ret; } +static void export_format_enable_controls(HWND hwnd, struct ExportWizData *data) +{ + int defaultFormatID; + + switch (data->contextInfo.dwExportFormat) + { + case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64: + defaultFormatID = IDC_EXPORT_FORMAT_BASE64; + break; + case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7: + defaultFormatID = IDC_EXPORT_FORMAT_CMS; + break; + case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: + defaultFormatID = IDC_EXPORT_FORMAT_PFX; + break; + default: + defaultFormatID = IDC_EXPORT_FORMAT_DER; + } + SendMessageW(GetDlgItem(hwnd, defaultFormatID), BM_CLICK, 0, 0); + if (defaultFormatID == IDC_EXPORT_FORMAT_PFX) + { + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_DER), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_BASE64), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_CMS), FALSE); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_PFX), TRUE); + } + else + { + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_DER), TRUE); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_BASE64), TRUE); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_CMS), TRUE); + EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_PFX), FALSE); + } +} + static LRESULT CALLBACK export_format_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { @@ -5596,32 +5768,10 @@ static LRESULT CALLBACK export_format_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, case WM_INITDIALOG: { PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; - int defaultFormatID; - BOOL hasPrivateKey; data = (struct ExportWizData *)page->lParam; SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); - hasPrivateKey = export_info_has_private_key(data->pExportInfo); - if (hasPrivateKey) - EnableWindow(GetDlgItem(hwnd, IDC_EXPORT_FORMAT_PFX), TRUE); - switch (data->contextInfo.dwExportFormat) - { - case CRYPTUI_WIZ_EXPORT_FORMAT_BASE64: - defaultFormatID = IDC_EXPORT_FORMAT_BASE64; - break; - case CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7: - defaultFormatID = IDC_EXPORT_FORMAT_CMS; - break; - case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: - if (hasPrivateKey) - defaultFormatID = IDC_EXPORT_FORMAT_PFX; - else - defaultFormatID = IDC_EXPORT_FORMAT_DER; - break; - default: - defaultFormatID = IDC_EXPORT_FORMAT_DER; - } - SendMessageW(GetDlgItem(hwnd, defaultFormatID), BM_CLICK, 0, 0); + export_format_enable_controls(hwnd, data); break; } case WM_NOTIFY: @@ -5633,10 +5783,14 @@ static LRESULT CALLBACK export_format_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, case PSN_SETACTIVE: PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, PSWIZB_BACK | PSWIZB_NEXT); + data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); + export_format_enable_controls(hwnd, data); ret = TRUE; break; case PSN_WIZNEXT: { + BOOL skipPasswordPage = TRUE; + data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); if (IsDlgButtonChecked(hwnd, IDC_EXPORT_FORMAT_DER)) data->contextInfo.dwExportFormat = @@ -5661,8 +5815,12 @@ static LRESULT CALLBACK export_format_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_STRONG_ENCRYPTION)) data->contextInfo.fStrongEncryption = TRUE; if (IsDlgButtonChecked(hwnd, IDC_EXPORT_PFX_DELETE_PRIVATE_KEY)) - data->contextInfo.fExportPrivateKeys = TRUE; + data->deleteKeys = TRUE; + skipPasswordPage = FALSE; } + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, + skipPasswordPage ? IDD_EXPORT_FILE : 0); + ret = 1; break; } } @@ -5705,6 +5863,111 @@ static LRESULT CALLBACK export_format_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, return ret; } +static void export_password_mismatch(HWND hwnd, struct ExportWizData *data) +{ + WCHAR title[MAX_STRING_LEN], error[MAX_STRING_LEN]; + LPCWSTR pTitle; + + if (data->pwszWizardTitle) + pTitle = data->pwszWizardTitle; + else + { + LoadStringW(hInstance, IDS_EXPORT_WIZARD, title, + sizeof(title) / sizeof(title[0])); + pTitle = title; + } + LoadStringW(hInstance, IDS_EXPORT_PASSWORD_MISMATCH, error, + sizeof(error) / sizeof(error[0])); + MessageBoxW(hwnd, error, pTitle, MB_ICONERROR | MB_OK); + SetFocus(GetDlgItem(hwnd, IDC_EXPORT_PASSWORD)); +} + +static LRESULT CALLBACK export_password_dlg_proc(HWND hwnd, UINT msg, + WPARAM wp, LPARAM lp) +{ + LRESULT ret = 0; + struct ExportWizData *data; + + switch (msg) + { + case WM_INITDIALOG: + { + PROPSHEETPAGEW *page = (PROPSHEETPAGEW *)lp; + + data = (struct ExportWizData *)page->lParam; + SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); + break; + } + case WM_NOTIFY: + { + NMHDR *hdr = (NMHDR *)lp; + + switch (hdr->code) + { + case PSN_SETACTIVE: + PostMessageW(GetParent(hwnd), PSM_SETWIZBUTTONS, 0, + PSWIZB_BACK | PSWIZB_NEXT); + ret = TRUE; + break; + case PSN_WIZNEXT: + { + HWND passwordEdit = GetDlgItem(hwnd, IDC_EXPORT_PASSWORD); + HWND passwordConfirmEdit = GetDlgItem(hwnd, + IDC_EXPORT_PASSWORD_CONFIRM); + DWORD passwordLen = SendMessageW(passwordEdit, WM_GETTEXTLENGTH, + 0, 0); + DWORD passwordConfirmLen = SendMessageW(passwordConfirmEdit, + WM_GETTEXTLENGTH, 0, 0); + + data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); + if (!passwordLen && !passwordConfirmLen) + data->contextInfo.pwszPassword = NULL; + else if (passwordLen != passwordConfirmLen) + { + export_password_mismatch(hwnd, data); + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); + ret = 1; + } + else + { + LPWSTR password = HeapAlloc(GetProcessHeap(), 0, + (passwordLen + 1) * sizeof(WCHAR)); + LPWSTR passwordConfirm = HeapAlloc(GetProcessHeap(), 0, + (passwordConfirmLen + 1) * sizeof(WCHAR)); + BOOL freePassword = TRUE; + + if (password && passwordConfirm) + { + SendMessageW(passwordEdit, WM_GETTEXT, passwordLen + 1, + (LPARAM)password); + SendMessageW(passwordConfirmEdit, WM_GETTEXT, + passwordConfirmLen + 1, (LPARAM)passwordConfirm); + if (strcmpW(password, passwordConfirm)) + { + export_password_mismatch(hwnd, data); + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 1); + ret = 1; + } + else + { + data->contextInfo.pwszPassword = password; + freePassword = FALSE; + data->freePassword = TRUE; + } + } + if (freePassword) + HeapFree(GetProcessHeap(), 0, password); + HeapFree(GetProcessHeap(), 0, passwordConfirm); + } + break; + } + } + break; + } + } + return ret; +} + static LPWSTR export_append_extension(struct ExportWizData *data, LPWSTR fileName) { @@ -5727,7 +5990,7 @@ static LPWSTR export_append_extension(struct ExportWizData *data, extension = pfx; break; default: - switch (data->pExportInfo->dwSubjectChoice) + switch (data->exportInfo.dwSubjectChoice) { case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: extension = crl; @@ -5926,9 +6189,9 @@ static LRESULT CALLBACK export_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, data = (struct ExportWizData *)page->lParam; SetWindowLongPtrW(hwnd, DWLP_USER, (LPARAM)data); - if (data->pExportInfo->pwszExportFileName) + if (data->exportInfo.pwszExportFileName) SendMessageW(GetDlgItem(hwnd, IDC_EXPORT_FILENAME), WM_SETTEXT, 0, - (LPARAM)data->pExportInfo->pwszExportFileName); + (LPARAM)data->exportInfo.pwszExportFileName); break; } case WM_NOTIFY: @@ -5937,6 +6200,15 @@ static LRESULT CALLBACK export_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, switch (hdr->code) { + case PSN_WIZBACK: + data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); + if (data->contextInfo.dwExportFormat != + CRYPTUI_WIZ_EXPORT_FORMAT_PFX) + { + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, IDD_EXPORT_FORMAT); + ret = 1; + } + break; case PSN_WIZNEXT: { HWND fileNameEdit = GetDlgItem(hwnd, IDC_EXPORT_FILENAME); @@ -6006,7 +6278,7 @@ static LRESULT CALLBACK export_file_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, ofn.hwndOwner = hwnd; ofn.lpstrFilter = make_export_file_filter( data->contextInfo.dwExportFormat, - data->pExportInfo->dwSubjectChoice); + data->exportInfo.dwSubjectChoice); ofn.lpstrFile = fileBuf; ofn.nMaxFile = sizeof(fileBuf) / sizeof(fileBuf[0]); fileBuf[0] = 0; @@ -6043,7 +6315,7 @@ static void show_export_details(HWND lv, struct ExportWizData *data) } item.pszText = text; - switch (data->pExportInfo->dwSubjectChoice) + switch (data->exportInfo.dwSubjectChoice) { case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: case CRYPTUI_WIZ_EXPORT_CTL_CONTEXT: @@ -6084,7 +6356,7 @@ static void show_export_details(HWND lv, struct ExportWizData *data) SendMessageW(lv, LVM_INSERTITEMW, 0, (LPARAM)&item); item.iSubItem = 1; - switch (data->pExportInfo->dwSubjectChoice) + switch (data->exportInfo.dwSubjectChoice) { case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: contentID = IDS_EXPORT_FILTER_CRL; @@ -6227,8 +6499,140 @@ static BOOL save_serialized_store(HANDLE file, HCERTSTORE store) CERT_STORE_SAVE_AS_STORE, CERT_STORE_SAVE_TO_FILE, file, 0); } +static BOOL save_pfx(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, + PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO pContextInfo, + PCRYPT_KEY_PROV_INFO keyProvInfo, BOOL deleteKeys) +{ + HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, + 0, CERT_STORE_CREATE_NEW_FLAG, NULL); + BOOL ret = FALSE; + + if (store) + { + CRYPT_DATA_BLOB pfxBlob = { 0, NULL }; + PCCERT_CONTEXT cert = NULL; + BOOL freeKeyProvInfo = FALSE; + + if (pContextInfo->fExportChain) + { + HCERTCHAINENGINE engine = NULL; + + if (pExportInfo->cStores) + { + CERT_CHAIN_ENGINE_CONFIG config; + + memset(&config, 0, sizeof(config)); + config.cbSize = sizeof(config); + config.cAdditionalStore = pExportInfo->cStores; + config.rghAdditionalStore = pExportInfo->rghStores; + ret = CertCreateCertificateChainEngine(&config, &engine); + } + else + ret = TRUE; + if (ret) + { + CERT_CHAIN_PARA chainPara; + PCCERT_CHAIN_CONTEXT chain; + + memset(&chainPara, 0, sizeof(chainPara)); + chainPara.cbSize = sizeof(chainPara); + ret = CertGetCertificateChain(engine, + pExportInfo->u.pCertContext, NULL, NULL, &chainPara, 0, NULL, + &chain); + if (ret) + { + DWORD i, j; + + for (i = 0; ret && i < chain->cChain; i++) + for (j = 0; ret && j < chain->rgpChain[i]->cElement; + j++) + { + if (i == 0 && j == 0) + ret = CertAddCertificateContextToStore(store, + chain->rgpChain[i]->rgpElement[j]->pCertContext, + CERT_STORE_ADD_ALWAYS, &cert); + else + ret = CertAddCertificateContextToStore(store, + chain->rgpChain[i]->rgpElement[j]->pCertContext, + CERT_STORE_ADD_ALWAYS, NULL); + } + CertFreeCertificateChain(chain); + } + } + if (engine) + CertFreeCertificateChainEngine(engine); + } + else + ret = CertAddCertificateContextToStore(store, + pExportInfo->u.pCertContext, CERT_STORE_ADD_ALWAYS, &cert); + /* Copy private key info to newly created cert, so it'll get exported + * along with the cert. + */ + if (ret && pContextInfo->fExportPrivateKeys) + { + if (keyProvInfo) + ret = CertSetCertificateContextProperty(cert, + CERT_KEY_PROV_INFO_PROP_ID, 0, keyProvInfo); + else + { + if (!(keyProvInfo = export_get_private_key_info(cert))) + ret = FALSE; + else + { + ret = CertSetCertificateContextProperty(cert, + CERT_KEY_PROV_INFO_PROP_ID, 0, keyProvInfo); + freeKeyProvInfo = TRUE; + } + } + } + if (ret) + { + DWORD exportFlags = + REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY | EXPORT_PRIVATE_KEYS; + + ret = PFXExportCertStore(store, &pfxBlob, + pContextInfo->pwszPassword, exportFlags); + if (ret) + { + pfxBlob.pbData = HeapAlloc(GetProcessHeap(), 0, pfxBlob.cbData); + if (pfxBlob.pbData) + { + ret = PFXExportCertStore(store, &pfxBlob, + pContextInfo->pwszPassword, exportFlags); + if (ret) + { + DWORD bytesWritten; + + ret = WriteFile(file, pfxBlob.pbData, pfxBlob.cbData, + &bytesWritten, NULL); + } + } + else + { + SetLastError(ERROR_OUTOFMEMORY); + ret = FALSE; + } + } + } + if (ret && deleteKeys) + { + HCRYPTPROV prov; + + CryptAcquireContextW(&prov, keyProvInfo->pwszContainerName, + keyProvInfo->pwszProvName, keyProvInfo->dwProvType, + CRYPT_DELETEKEYSET); + } + if (freeKeyProvInfo) + HeapFree(GetProcessHeap(), 0, keyProvInfo); + CertFreeCertificateContext(cert); + CertCloseStore(store, 0); + } + return ret; +} + static BOOL do_export(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, - PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO pContextInfo) + PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO pContextInfo, + PCRYPT_KEY_PROV_INFO keyProvInfo, BOOL deleteKeys) { BOOL ret; @@ -6272,8 +6676,8 @@ static BOOL do_export(HANDLE file, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, pContextInfo->fExportChain); break; case CRYPTUI_WIZ_EXPORT_FORMAT_PFX: - FIXME("unimplemented for PFX\n"); - ret = FALSE; + ret = save_pfx(file, pExportInfo, pContextInfo, keyProvInfo, + deleteKeys); break; default: SetLastError(E_FAIL); @@ -6336,8 +6740,8 @@ static LRESULT CALLBACK export_finish_dlg_proc(HWND hwnd, UINT msg, WPARAM wp, DWORD mbFlags; data = (struct ExportWizData *)GetWindowLongPtrW(hwnd, DWLP_USER); - if ((data->success = do_export(data->file, data->pExportInfo, - &data->contextInfo))) + if ((data->success = do_export(data->file, &data->exportInfo, + &data->contextInfo, data->keyProvInfo, data->deleteKeys))) { messageID = IDS_EXPORT_SUCCEEDED; mbFlags = MB_OK; @@ -6371,24 +6775,33 @@ static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, LPCWSTR pwszWizardTitle, PCCRYPTUI_WIZ_EXPORT_INFO pExportInfo, void *pvoid) { PROPSHEETHEADERW hdr; - PROPSHEETPAGEW pages[4]; + PROPSHEETPAGEW pages[6]; struct ExportWizData data; int nPages = 0; - BOOL showFormatPage = TRUE; + BOOL hasPrivateKey, showFormatPage = TRUE; + INT_PTR l; data.dwFlags = dwFlags; data.pwszWizardTitle = pwszWizardTitle; - data.pExportInfo = pExportInfo; + memset(&data.exportInfo, 0, sizeof(data.exportInfo)); + memcpy(&data.exportInfo, pExportInfo, + min(sizeof(data.exportInfo), pExportInfo->dwSize)); + if (pExportInfo->dwSize > sizeof(data.exportInfo)) + data.exportInfo.dwSize = sizeof(data.exportInfo); data.contextInfo.dwSize = sizeof(data.contextInfo); data.contextInfo.dwExportFormat = CRYPTUI_WIZ_EXPORT_FORMAT_DER; data.contextInfo.fExportChain = FALSE; data.contextInfo.fStrongEncryption = FALSE; data.contextInfo.fExportPrivateKeys = FALSE; + data.contextInfo.pwszPassword = NULL; + data.freePassword = FALSE; if (pExportInfo->dwSubjectChoice == CRYPTUI_WIZ_EXPORT_CERT_CONTEXT && pvoid) memcpy(&data.contextInfo, pvoid, min(((PCCRYPTUI_WIZ_EXPORT_CERTCONTEXT_INFO)pvoid)->dwSize, sizeof(data.contextInfo))); + data.keyProvInfo = NULL; + data.deleteKeys = FALSE; data.fileName = NULL; data.file = INVALID_HANDLE_VALUE; data.success = FALSE; @@ -6403,6 +6816,7 @@ static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, pages[nPages].lParam = (LPARAM)&data; nPages++; + hasPrivateKey = export_info_has_private_key(pExportInfo); switch (pExportInfo->dwSubjectChoice) { case CRYPTUI_WIZ_EXPORT_CRL_CONTEXT: @@ -6420,6 +6834,21 @@ static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, data.contextInfo.dwExportFormat = CRYPTUI_WIZ_EXPORT_FORMAT_PKCS7; break; } + + if (hasPrivateKey && showFormatPage) + { + pages[nPages].dwSize = sizeof(pages[0]); + pages[nPages].hInstance = hInstance; + pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_PRIVATE_KEY); + pages[nPages].pfnDlgProc = export_private_key_dlg_proc; + pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; + pages[nPages].pszHeaderTitle = + MAKEINTRESOURCEW(IDS_EXPORT_PRIVATE_KEY_TITLE); + pages[nPages].pszHeaderSubTitle = + MAKEINTRESOURCEW(IDS_EXPORT_PRIVATE_KEY_SUBTITLE); + pages[nPages].lParam = (LPARAM)&data; + nPages++; + } if (showFormatPage) { pages[nPages].dwSize = sizeof(pages[0]); @@ -6434,6 +6863,20 @@ static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, pages[nPages].lParam = (LPARAM)&data; nPages++; } + if (hasPrivateKey && showFormatPage) + { + pages[nPages].dwSize = sizeof(pages[0]); + pages[nPages].hInstance = hInstance; + pages[nPages].u.pszTemplate = MAKEINTRESOURCEW(IDD_EXPORT_PASSWORD); + pages[nPages].pfnDlgProc = export_password_dlg_proc; + pages[nPages].dwFlags = PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; + pages[nPages].pszHeaderTitle = + MAKEINTRESOURCEW(IDS_EXPORT_PASSWORD_TITLE); + pages[nPages].pszHeaderSubTitle = + MAKEINTRESOURCEW(IDS_EXPORT_PASSWORD_SUBTITLE); + pages[nPages].lParam = (LPARAM)&data; + nPages++; + } pages[nPages].dwSize = sizeof(pages[0]); pages[nPages].hInstance = hInstance; @@ -6468,11 +6911,21 @@ static BOOL show_export_ui(DWORD dwFlags, HWND hwndParent, hdr.nPages = nPages; hdr.u4.pszbmWatermark = MAKEINTRESOURCEW(IDB_CERT_WATERMARK); hdr.u5.pszbmHeader = MAKEINTRESOURCEW(IDB_CERT_HEADER); - PropertySheetW(&hdr); + l = PropertySheetW(&hdr); DeleteObject(data.titleFont); + if (data.freePassword) + HeapFree(GetProcessHeap(), 0, + (LPWSTR)data.contextInfo.pwszPassword); + HeapFree(GetProcessHeap(), 0, data.keyProvInfo); CloseHandle(data.file); HeapFree(GetProcessHeap(), 0, data.fileName); - return data.success; + if (l == 0) + { + SetLastError(ERROR_CANCELLED); + return FALSE; + } + else + return data.success; } BOOL WINAPI CryptUIWizExport(DWORD dwFlags, HWND hwndParent, @@ -6494,7 +6947,7 @@ BOOL WINAPI CryptUIWizExport(DWORD dwFlags, HWND hwndParent, if (file != INVALID_HANDLE_VALUE) { - ret = do_export(file, pExportInfo, pvoid); + ret = do_export(file, pExportInfo, pvoid, NULL, FALSE); CloseHandle(file); } else