From 50e8e9538511e3cdf837264f8d676be9899b2b07 Mon Sep 17 00:00:00 2001 From: "Richard J. Moore" Date: Sat, 10 May 2014 22:49:37 +0100 Subject: [PATCH] Add support for loading PKCS#12 bundles. Add support for loading certificates and keys from PKCS#12 bundles (also known as pfx files). Task-number: QTBUG-1565 [ChangeLog][QtNetwork][QSslSocket] Support for loading PKCS#12 bundles was added. These are often used to transport keys and certificates conveniently, particularly when making use of client certificates. Change-Id: Idaeb2cb4dac4b19881a5c99c7c0a7eea00c2b207 Reviewed-by: Daniel Molkentin --- src/network/ssl/qsslkey.cpp | 32 ++++++++ src/network/ssl/qsslkey.h | 1 + src/network/ssl/qsslkey_p.h | 1 + src/network/ssl/qsslsocket.cpp | 20 +++++ src/network/ssl/qsslsocket.h | 5 ++ src/network/ssl/qsslsocket_openssl.cpp | 69 ++++++++++++++++++ src/network/ssl/qsslsocket_openssl_p.h | 4 + .../ssl/qsslsocket_openssl_symbols.cpp | 12 +++ .../ssl/qsslsocket_openssl_symbols_p.h | 8 ++ .../auto/network/ssl/qsslsocket/certs/README | 7 ++ .../network/ssl/qsslsocket/certs/fluke.p12 | Bin 0 -> 2797 bytes .../network/ssl/qsslsocket/certs/leaf.p12 | Bin 0 -> 3821 bytes .../network/ssl/qsslsocket/tst_qsslsocket.cpp | 47 ++++++++++++ 13 files changed, 206 insertions(+) create mode 100644 tests/auto/network/ssl/qsslsocket/certs/README create mode 100644 tests/auto/network/ssl/qsslsocket/certs/fluke.p12 create mode 100644 tests/auto/network/ssl/qsslsocket/certs/leaf.p12 diff --git a/src/network/ssl/qsslkey.cpp b/src/network/ssl/qsslkey.cpp index 95eed6e4b3..b43e28589f 100644 --- a/src/network/ssl/qsslkey.cpp +++ b/src/network/ssl/qsslkey.cpp @@ -96,6 +96,38 @@ void QSslKeyPrivate::clear(bool deep) } } +bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey) +{ + if (pkey->type == EVP_PKEY_RSA) { + isNull = false; + algorithm = QSsl::Rsa; + type = QSsl::PrivateKey; + + rsa = q_RSA_new(); + memcpy(rsa, q_EVP_PKEY_get1_RSA(pkey), sizeof(RSA)); + + return true; + } + else if (pkey->type == EVP_PKEY_DSA) { + isNull = false; + algorithm = QSsl::Dsa; + type = QSsl::PrivateKey; + + dsa = q_DSA_new(); + memcpy(rsa, q_EVP_PKEY_get1_DSA(pkey), sizeof(DSA)); + + return true; + } + else { + // Unknown key type. This could be handled as opaque, but then + // we'd eventually leak memory since we wouldn't be able to free + // the underlying EVP_PKEY structure. For now, we won't support + // this. + } + + return false; +} + /*! \internal diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h index 145d4a28f1..59063e2e57 100644 --- a/src/network/ssl/qsslkey.h +++ b/src/network/ssl/qsslkey.h @@ -95,6 +95,7 @@ public: private: QExplicitlySharedDataPointer d; friend class QSslCertificate; + friend class QSslSocketBackendPrivate; }; Q_DECLARE_SHARED(QSslKey) diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h index 54d12d76fb..658be85621 100644 --- a/src/network/ssl/qsslkey_p.h +++ b/src/network/ssl/qsslkey_p.h @@ -79,6 +79,7 @@ public: void clear(bool deep = true); + bool fromEVP_PKEY(EVP_PKEY *pkey); void decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear = true); QByteArray pemHeader() const; diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 04c0fb0487..1c77fac769 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -1455,6 +1455,26 @@ QList QSslSocket::systemCaCertificates() return QSslSocketPrivate::systemCaCertificates(); } +/*! + \since 5.4 + + Imports a PKCS#12 (pfx) file from the specified \a device. A PKCS#12 + file is a bundle that can contain a number of certificates and keys. + This method reads a single \a key, it's \a certificate and any + associated \a caCertificates from the bundle. If a \a passPhrase is + specified then this will be used to decrypt the bundle. Returns + \c true if the PKCS#12 file was successfully loaded. + + \note The \a device must be open and ready to be read from. + */ +bool QSslSocket::importPKCS12(QIODevice *device, + QSslKey *key, QSslCertificate *certificate, + QList *caCertificates, + const QByteArray &passPhrase) +{ + return QSslSocketBackendPrivate::importPKCS12(device, key, certificate, caCertificates, passPhrase); +} + /*! Waits until the socket is connected, or \a msecs milliseconds, whichever happens first. If the connection has been established, diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h index 9cc5e02de3..a0c9002529 100644 --- a/src/network/ssl/qsslsocket.h +++ b/src/network/ssl/qsslsocket.h @@ -172,6 +172,11 @@ public: static QList defaultCaCertificates(); static QList systemCaCertificates(); + static bool importPKCS12(QIODevice *device, + QSslKey *key, QSslCertificate *cert, + QList *caCertificates=0, + const QByteArray &passPhrase=QByteArray()); + bool waitForConnected(int msecs = 30000); bool waitForEncrypted(int msecs = 30000); bool waitForReadyRead(int msecs = 30000); diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 1b0c5afa00..b0d1ed2c4f 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -62,6 +62,7 @@ #include "qsslsocket.h" #include "qsslcertificate_p.h" #include "qsslcipher_p.h" +#include "qsslkey_p.h" #include #include @@ -1737,4 +1738,72 @@ QList QSslSocketBackendPrivate::verify(QList certifi return errors; } +bool QSslSocketBackendPrivate::importPKCS12(QIODevice *device, + QSslKey *key, QSslCertificate *cert, + QList *caCertificates, + const QByteArray &passPhrase) +{ + if (!supportsSsl()) + return false; + + // These are required + Q_ASSERT(device); + Q_ASSERT(key); + Q_ASSERT(cert); + + // Read the file into a BIO + QByteArray pkcs12data = device->readAll(); + if (pkcs12data.size() == 0) + return false; + + BIO *bio = q_BIO_new_mem_buf(const_cast(pkcs12data.constData()), pkcs12data.size()); + + // Create the PKCS#12 object + PKCS12 *p12 = q_d2i_PKCS12_bio(bio, 0); + if (!p12) { + qWarning("Unable to read PKCS#12 structure, %s", q_ERR_error_string(q_ERR_get_error(), 0)); + q_BIO_free(bio); + return false; + } + + // Extract the data + EVP_PKEY *pkey; + X509 *x509; + STACK_OF(X509) *ca = 0; + + if (!q_PKCS12_parse(p12, passPhrase.constData(), &pkey, &x509, &ca)) { + qWarning("Unable to parse PKCS#12 structure, %s", q_ERR_error_string(q_ERR_get_error(), 0)); + q_PKCS12_free(p12); + q_BIO_free(bio); + return false; + } + + // Convert to Qt types + if (!key->d->fromEVP_PKEY(pkey)) { + qWarning("Unable to convert private key"); + q_sk_pop_free(reinterpret_cast(ca), reinterpret_cast(q_sk_free)); + q_X509_free(x509); + q_EVP_PKEY_free(pkey); + q_PKCS12_free(p12); + q_BIO_free(bio); + + return false; + } + + *cert = QSslCertificatePrivate::QSslCertificate_from_X509(x509); + + if (caCertificates) + *caCertificates = QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates(ca); + + // Clean up + q_sk_pop_free(reinterpret_cast(ca), reinterpret_cast(q_sk_free)); + q_X509_free(x509); + q_EVP_PKEY_free(pkey); + q_PKCS12_free(p12); + q_BIO_free(bio); + + return true; +} + + QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 0a9d02287d..f4f2fe842c 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -146,6 +146,10 @@ public: Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); static QList verify(QList certificateChain, const QString &hostName); static QString getErrorsFromOpenSsl(); + static bool importPKCS12(QIODevice *device, + QSslKey *key, QSslCertificate *cert, + QList *caCertificates, + const QByteArray &passPhrase); }; #ifdef Q_OS_WIN diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index b0e14e0de1..b75893d00f 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -146,6 +146,7 @@ DEFINEFUNC(int, CRYPTO_num_locks, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, CRYPTO_set_locking_callback, void (*a)(int, int, const char *, int), a, return, DUMMYARG) DEFINEFUNC(void, CRYPTO_set_id_callback, unsigned long (*a)(), a, return, DUMMYARG) DEFINEFUNC(void, CRYPTO_free, void *a, a, return, DUMMYARG) +DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG) #if OPENSSL_VERSION_NUMBER < 0x00908000L DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, unsigned char **b, b, long c, c, return 0, return) @@ -186,6 +187,7 @@ DEFINEFUNC2(int, PEM_write_bio_DSA_PUBKEY, BIO *a, a, DSA *b, b, return 0, retur DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, return) DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG) DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return) +DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return 0, return) DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG) @@ -369,6 +371,11 @@ DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return 0, return) DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG) +DEFINEFUNC5(int, PKCS12_parse, PKCS12 *p12, p12, const char *pass, pass, EVP_PKEY **pkey, pkey, \ + X509 **cert, cert, STACK_OF(X509) **ca, ca, return 1, return); +DEFINEFUNC2(PKCS12 *, d2i_PKCS12_bio, BIO *bio, bio, PKCS12 **pkcs12, pkcs12, return 0, return); +DEFINEFUNC(void, PKCS12_free, PKCS12 *pkcs12, pkcs12, return, DUMMYARG) + #define RESOLVEFUNC(func) \ if (!(_q_##func = _q_PTR_##func(libs.first->resolve(#func))) \ && !(_q_##func = _q_PTR_##func(libs.second->resolve(#func)))) \ @@ -687,6 +694,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(CRYPTO_num_locks) RESOLVEFUNC(CRYPTO_set_id_callback) RESOLVEFUNC(CRYPTO_set_locking_callback) + RESOLVEFUNC(DSA_new) RESOLVEFUNC(DSA_free) RESOLVEFUNC(ERR_error_string) RESOLVEFUNC(ERR_get_error) @@ -719,6 +727,7 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY) RESOLVEFUNC(RAND_seed) RESOLVEFUNC(RAND_status) + RESOLVEFUNC(RSA_new) RESOLVEFUNC(RSA_free) RESOLVEFUNC(sk_new_null) RESOLVEFUNC(sk_push) @@ -849,6 +858,9 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(BN_bin2bn) RESOLVEFUNC(EC_KEY_new_by_curve_name) RESOLVEFUNC(EC_KEY_free) + RESOLVEFUNC(PKCS12_parse) + RESOLVEFUNC(d2i_PKCS12_bio) + RESOLVEFUNC(PKCS12_free) symbolsResolved = true; delete libs.first; diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index 36e196b072..75e239237e 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -233,6 +233,7 @@ int q_CRYPTO_num_locks(); void q_CRYPTO_set_locking_callback(void (*a)(int, int, const char *, int)); void q_CRYPTO_set_id_callback(unsigned long (*a)()); void q_CRYPTO_free(void *a); +DSA *q_DSA_new(); void q_DSA_free(DSA *a); #if OPENSSL_VERSION_NUMBER >= 0x00908000L // 0.9.8 broke SC and BC by changing this function's signature. @@ -277,6 +278,7 @@ int q_PEM_write_bio_DSA_PUBKEY(BIO *a, DSA *b); int q_PEM_write_bio_RSA_PUBKEY(BIO *a, RSA *b); void q_RAND_seed(const void *a, int b); int q_RAND_status(); +RSA *q_RSA_new(); void q_RSA_free(RSA *a); int q_sk_num(STACK *a); void q_sk_pop_free(STACK *a, void (*b)(void *)); @@ -440,6 +442,12 @@ EC_KEY *q_EC_KEY_new_by_curve_name(int nid); void q_EC_KEY_free(EC_KEY *ecdh); #define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh) +// PKCS#12 support +int q_PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca); +PKCS12 *q_d2i_PKCS12_bio(BIO *bio, PKCS12 **pkcs12); +void q_PKCS12_free(PKCS12 *pkcs12); + + #define q_BIO_get_mem_data(b, pp) (int)q_BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp) #define q_BIO_pending(b) (int)q_BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) #ifdef SSLEAY_MACROS diff --git a/tests/auto/network/ssl/qsslsocket/certs/README b/tests/auto/network/ssl/qsslsocket/certs/README new file mode 100644 index 0000000000..704646c212 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/README @@ -0,0 +1,7 @@ +The PKCS#12 bundle was created by running: + +openssl pkcs12 -export -in leaf.crt -inkey leaf.key \ + -out leaf.p12 \ + -certfile inter.crt -CAfile ca.crt + +No password was provided. diff --git a/tests/auto/network/ssl/qsslsocket/certs/fluke.p12 b/tests/auto/network/ssl/qsslsocket/certs/fluke.p12 new file mode 100644 index 0000000000000000000000000000000000000000..f11e550e885e160d83127e2dc7f6f3fedcecfd2c GIT binary patch literal 2797 zcmVXQOQFQHSqq)x(M3}vD*}KepI{e;Gve$?=KP6 zT}0%tW&TU0yAn2mr_WU+!#&9c^^1%2jP1@ zyTX+Zoul~%{g$~}3RXLh?_H6KY`sZOOe+fce#;F9n|3!x9cur6up_J&U9<#Eyfx?r zsu?1FRavjM1J`Vo;{=$oX4^Z_OWlCHqq8s1Gfbo_4=o5pLK zY36zA;vDUq*489tCjlWgKgn~^0rvX^k2=&v8(jS^QE))rKCxXphy>&+I=2lE2r{+TFdV%9 zD6)es;-0zOet8|uM;zr^B7NN?P^1{I;8C(2kB}ehzvrTCx<5WZGwh-E|Dq z8x*OR!?f{WLqzr$VETZ0Ca~q0ryX=06=;3bFtSQpmfW%ziru@_>3zWgQ}9>4tR@Me zN^pot(BsgvN;)hDly27d1=$k!m>=Os$oB5{9dfrD^fN=?Z7_ILn;KD|1#0Ndr}mSG zibN7uhuTnyirDkTnE_9wv!U0i56!&VHm0_S3eeYK9`c00ELZ~meaxo0uyE~{t&Gvj z=_nt=e|NA7`sukb^*dm(1+vh8W`ZMHPeITiXT+Iq)Mvx44Wd=}n6}oL3zg}b^by^A zqJbUpi{@VmQO;(a=;i4MbGU5lIZF2DV5t~m9}{vGYyawz*}>A_|PLg3nvYU4|h#+ z%qc8xo^HrU7X~In$I|rLJU09+Cy+i%hDOoRh}AW92g&4X^t(Dur!;LmM;08ek_|xe z>~Ji#E8{YLmRCzXJ)ZiXE%d(cjGHZXR-j}V9LvgwLU{3dd^S0ly?jO6KtE3R(=>FoFX41_>&LNQUC0fQ>~G{YAZNwsiA|Cw&6acwTz)PYmkeFDU<L;GDuYkL zukRF%}=!qacY z!zX}&2JEh*U2bKxhCJfH+%YV3*dH)7;bdcFI^)W(ZGdD11sj*${sgm9fl{ZtAIcs1 z4~!k+{9txTSX{wzS^pCSfyswB1tn%-2*pa>?n*SkwEzzfLQfW z=ZaFQnFIxnTQ#&?d0-z%qoZ$QHt+OkF%=)XDFZWo$ec#74y(I;DWS+Cd@v>~3n~Xp z2GZ)d^*J$^)~+!nFe3&DDuzgg_YDCF6)_eB6f0K-50cVuer@^L_^=0QI{A7lC@?WF zAutIB1uG5%0vZJX1QfG(`;Xvn$Bjo2MN~$$41V8NwTc7?)VqYu6a~lA0s;sCz9}WC literal 0 HcmV?d00001 diff --git a/tests/auto/network/ssl/qsslsocket/certs/leaf.p12 b/tests/auto/network/ssl/qsslsocket/certs/leaf.p12 new file mode 100644 index 0000000000000000000000000000000000000000..cb89aadb7328e69f99afe1d1a30a39a7242c1429 GIT binary patch literal 3821 zcmY+GXEYm*qlSqjLQuq}HEK0hLXp<2+Pg+to7!sA+AG@HVyjJ5DGjxX+BEk16&0hX zJzCVJ#_jK%d+-0=PwzR;d(QjuJr4pyvk4$2LttpCz!dP;>aS0zNy$kIF*Joh3{CD| z{04!6`2D8>7h)hze~}%4l;m$%{HGyBaDXZQdx44+jGzHRv{mDkr{h{{0RRX|I0k}X zcuvcJ4;^2kEv?p`NVosB*6=~J`XZ{if8f39FiK9=t!fXzS-CCDh==DwtnHqk*AU91!{A=4jUl{3dJSfd)bMmc#LO zsH~*oEb8+mfW>jM$ZH}weUNE`FEI5)16&0MM4#yI;@M2V1jNncpHnN! zk1G7ITfs&xPn_RuUw?ELnO>W}6z!{c+gwGDCy^DA(WP36#6Q0jGswg;ZAZB+UtibZ zIGy%Gw%u$?8cBbfkn2f}G#PW<pkXfG{!5I zkBWYth0AZ6ly4AARK$ln`g@v{9B*;%~lMmlN@}!+fE2ZK+TB83vkyW7~-<9Kj zEh9h|Jmb;vGD$}y=3(ieUA0i780-~dI)_c}>%oF+G@L^F)v0kw;6Z$~|6tJm2H?|v zZCvYE@H1=20F#y}anoGy%2r7JDEx|U&ejOR6^o^ySgay1h~7|pgAgW5W2}Ur2ArS1{r}iF&X(`M`)(vc>%*FG&QOm{ZF-?)uO~tJcb?1BY zR($B`Ti;3(&y|aEP!_tBpa7Fk16Z)EmDqK2uJ7y>?d#7pzn_=QUu@sQmdRvxVOGif zAiSAprQeg;-E4BMM*Xe9YD+Iyc^)s2 z=eK-5$2h8`Rr(WQV*=5Hq;S^ zfebbrFX{-xm2!@h?juGm9Tjho6C3|r1&|vwTb(I>HEKZ>WSfLF`p*F(oR^yf%RILZ z>H7KwACixo1alxTktv>H&DoU~i3B^h)Bb*ex7!U>^kyvZEwmXfw*=CO-iwIk#RmrC z7ilt&t;M-aecFHzTi)XX_Rx-A3satPXvKm!r`bnhnxG+lE*7&Ok z#7RQ$Jz{sX&pVO-EF&tgmuvddxLDmOWZ*1F_=4`81{C_D>g?tR%IX@sHuu^Bc9?pH zpia{Fw0*a*?|L4%ifI=~3p37REsT%_=yng@B~d=?;UoD**dmrP?T9s6|A?j2wYz@$ zyCu=hTHIXSoFnNm%j(wv4#S_dM@~mR@ufNs?1s=2V>-TYUZH-WK=h7xyVkSXi-1-8 zNtXMD3-c{{mTPn3S{xdlCdpvS_HUl)-|E&jm4ikR+_o|?sPn7qDFd;On&2OX_cIK+ zCtaR&YSb(FMW#vNT6C!Sya~hgITsO34u^Qx7|q zLzZ`&GVb!#)RhGVnV+LSl2TfUSV}MzX8|b_BP72JF8u1gU>lN{Ej})nliRy6eau=TipBH4bu;Jw4DW+E-7{O9K{bUl3$x=L|K#J8V< z=qS9UQ4i|Rx-N~0gZP7yvq2MrF6g`S*Q5Z_T4Uom{V(akmr*cvw0$SOoVG4XPdVLl zNXx?UM9LnL>zbf!ne}vN%QMh4P_*BYy;4tu^yx0lDMj4w6>E3A^pF8Wc>);dFJ{tS zhVh59o;)$HF@*0~7)U;7VdyP!p9U9o3Ri*cE&h4db>m)u>{3EC;}g0z{jIY}D5D_f ztac&TPbR*~>9WpMXE%`_*VhV>E<#}!nS+gXb1+faKnBHq2dJcgr<&7Jwy`S@hF zIT&46{|O48JTF6u{DW4B2fpi%h%j9)JIMv!(Jozy{e8m0D<0atx@Am$6#HG?+SZM=%O8U`ikc4E~D&2n^^74E-P7pdLw$Vds)-RrpiX zPDOVoAEb@WN8`uWy%np+#&Gcbhw%!PKZ8`N6=j^9c8~jTY)HyRp*zn*!a!~Lzo?p` z40yXTLrz_@k?NH662g*%e!k)@C%hRVh+iP3v>-wpY#K)Y%&5e41aauV-j@FmDF*>G zyjBpZx_Oa%XoOvAGViOlhN1BQSDJMR|GWH4b6MF>Vu{dMpDd!b;2(1n4oX+iSGa=V zk>Y6SG4U7f$BvIbCr;~ofdEe<2c7bj%z)VbY5?2Kq`iTUa@>Pb zvJPU+cQ-%DyV6{G&~Byt?0APU!|*n1gkHqGzRdHBC_z?2e2zyLyWR@p7OC~uA0LeM z(rtt&-tNVXbO8drgK71b^yXRb^~zdv@j5fZh~G>}A{5MZs(<9#wUu6-V(l6r09 z!{6Rg;qy7YJXs9ha8f@?b0~)hg^Gn?r2#fJJBwX_@!p|Prdi$NZG!LWdH$(Vi%W6N zjur|E=8x8@foFEP8qTl}_zB(w$F8}pS`IZ~qQGHm4}8W6yID@x)DnbrqX7Cx+*lV; zgPWS9E-U9SFiA9*vwrHWWBfkvg*(Vor+{OF_Y)Ox_t)X~0)xtkTL!}($7~)}0Vqzx z{dkcy{g@1HRZs`BV^CCBlpM=1u993aE%&L@sd!e0WEySy)kh=Vy zSX1RVw{6yj;_D@|DRe-3G`Gk%O*SV7(vQWFP&r&bKjPewO)s?Z?%u3D@EJZ(y}!#B zqx0cO1$S>hzB}19aq-P3?1*ps?^i=-N}H$MGjAREEt9(BnTlCI_n(;6$340n4e!~U zcpmZ&QFNNB*WI^RPhL^mfKiF;6%gLyscM=TLf}c<8|t7SRRw}z_pGWLo6Kb*Oe|Cq zM)AexhGV`$plY<>^k>U-lrMf>AzLgRWt?$~U(|s(z28I;ctq_u=w6PNqZqGA zICX3FQJ4%WZ+lc!ORmPmSkjW&%-n#8jn%SJ+{Z{L}Py~>))bYrqIm?pBdg-y-irDDHM6Umo&aVh< zX(g#KzdL~ahog0(V5hSR?V_GAbR9wo$#7je(CiHsVyfXiP zVt;v`8?b@5-AUNwS&6m3>QHm%F6Wls*EYFOf(@`u^^#ffU}N-W^IN_*$Q~!g?|1BP z*AC8v$*LV3X`Zs}ypPmm>~HT9=UELPWHi!MS9;n4E6-Sy^ciR(h!X2QiG>l==nT`! zr-9X&MwFC zuqiUb30eN~8hh7yEuDMJV(D&pd9|aLRN0{dZIQ3h+JMU!@L^hn-Mj|iE%}>u$Dlf| z9rrA_OL$D@ufnu2F;!g)7|Lg2@~B6<6JhvFRVD~&zSU4hpVvJk7>j4ye!@z{joS>V@FkqtS&KRJn>iH?EIkyBb^QRiC$I z9~Ee1fs`hDE3x&tJx#_m?WEKPvX+!G!%%xC$`=)6FPBA(l3bA85JR$s4y+kstate() == QAbstractSocket::ConnectedState); } +void tst_QSslSocket::pkcs12() +{ + if (!QSslSocket::supportsSsl()) { + qWarning("SSL not supported, skipping test"); + return; + } + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFile f(QLatin1String(SRCDIR "certs/leaf.p12")); + bool ok = f.open(QIODevice::ReadOnly); + QVERIFY(ok); + + QSslKey key; + QSslCertificate cert; + QList caCerts; + + ok = QSslSocket::importPKCS12(&f, &key, &cert, &caCerts); + QVERIFY(ok); + f.close(); + + QList leafCert = QSslCertificate::fromPath(SRCDIR "certs/leaf.crt"); + QVERIFY(!leafCert.isEmpty()); + + QCOMPARE(cert, leafCert.first()); + + QFile f2(QLatin1String(SRCDIR "certs/leaf.key")); + ok = f2.open(QIODevice::ReadOnly); + QVERIFY(ok); + + QSslKey leafKey(&f2, QSsl::Rsa); + f2.close(); + + QVERIFY(!leafKey.isNull()); + QCOMPARE(key, leafKey); + + QList caCert = QSslCertificate::fromPath(SRCDIR "certs/inter.crt"); + QVERIFY(!caCert.isEmpty()); + + QVERIFY(!caCerts.isEmpty()); + QCOMPARE(caCerts.first(), caCert.first()); + QCOMPARE(caCerts, caCert); +} + void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects { // used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265