QSslKey - add a support for AES encrypted keys

for SecureTransport backend. OpenSSL, while reading
RSA/DSA, is internally calling EVP_BytesToKey that
essentially does the same thing this patch does in
'deriveAesKey' and thus able to correctly decrypt
whatever it first encrypted (while generating/
encrypting keys).

Fixes: QTBUG-54422
Change-Id: Ia9f7599c5b19bf364c179f2abd2aab7ea5359a65
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Timur Pocheptsov 2019-03-04 16:11:20 +01:00
parent f91aae6397
commit 01a5434252
9 changed files with 172 additions and 6 deletions

View File

@ -66,7 +66,13 @@ static QByteArray wrapCCCrypt(CCOperation ccOp,
blockSize = kCCBlockSizeRC2; blockSize = kCCBlockSizeRC2;
ccAlgorithm = kCCAlgorithmRC2; ccAlgorithm = kCCAlgorithmRC2;
break; break;
}; case QSslKeyPrivate::Aes128Cbc:
case QSslKeyPrivate::Aes192Cbc:
case QSslKeyPrivate::Aes256Cbc:
blockSize = kCCBlockSizeAES128;
ccAlgorithm = kCCAlgorithmAES;
break;
}
size_t plainLength = 0; size_t plainLength = 0;
QByteArray plain(data.size() + blockSize, 0); QByteArray plain(data.size() + blockSize, 0);
CCCryptorStatus status = CCCrypt( CCCryptorStatus status = CCCrypt(

View File

@ -333,6 +333,14 @@ static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, const QByteArray &data,
type = q_EVP_rc2_cbc(); type = q_EVP_rc2_cbc();
#endif #endif
break; break;
case QSslKeyPrivate::Aes128Cbc:
case QSslKeyPrivate::Aes192Cbc:
case QSslKeyPrivate::Aes256Cbc:
// Just to avoid compiler warnings/errors. OpenSSL uses a different
// codepath when reading encrypted keys, and they all correctly
// deduce the cipher and know how to derive a key.
Q_UNREACHABLE();
break;
} }
if (type == nullptr) if (type == nullptr)

View File

@ -105,7 +105,10 @@ public:
enum Cipher { enum Cipher {
DesCbc, DesCbc,
DesEde3Cbc, DesEde3Cbc,
Rc2Cbc Rc2Cbc,
Aes128Cbc,
Aes192Cbc,
Aes256Cbc
}; };
Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv); Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);

View File

@ -124,6 +124,37 @@ static int numberOfBits(const QByteArray &modulus)
return bits; return bits;
} }
static QByteArray deriveAesKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv)
{
// This is somewhat simplified and shortened version of what OpenSSL does.
// See, for example, EVP_BytesToKey for the "algorithm" itself and elsewhere
// in their code for what they pass as arguments to EVP_BytesToKey when
// deriving encryption keys (when reading/writing pems files with encrypted
// keys).
Q_ASSERT(iv.size() >= 8);
QCryptographicHash hash(QCryptographicHash::Md5);
QByteArray data(passPhrase);
data.append(iv.data(), 8); // AKA PKCS5_SALT_LEN in OpenSSL.
hash.addData(data);
if (cipher == QSslKeyPrivate::Aes128Cbc)
return hash.result();
QByteArray key(hash.result());
hash.reset();
hash.addData(key);
hash.addData(data);
if (cipher == QSslKeyPrivate::Aes192Cbc)
return key.append(hash.result().constData(), 8);
return key.append(hash.result());
}
static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv) static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv)
{ {
QByteArray key; QByteArray key;
@ -145,6 +176,10 @@ static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &pas
case QSslKeyPrivate::Rc2Cbc: case QSslKeyPrivate::Rc2Cbc:
key = hash.result(); key = hash.result();
break; break;
case QSslKeyPrivate::Aes128Cbc:
case QSslKeyPrivate::Aes192Cbc:
case QSslKeyPrivate::Aes256Cbc:
return deriveAesKey(cipher, passPhrase, iv);
} }
return key; return key;
} }
@ -378,6 +413,15 @@ void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhra
cipher = DesEde3Cbc; cipher = DesEde3Cbc;
} else if (dekInfo.first() == "RC2-CBC") { } else if (dekInfo.first() == "RC2-CBC") {
cipher = Rc2Cbc; cipher = Rc2Cbc;
// TODO: Add SChannel version too!
#ifdef QT_SECURETRANSPORT
} else if (dekInfo.first() == "AES-128-CBC") {
cipher = Aes128Cbc;
} else if (dekInfo.first() == "AES-192-CBC") {
cipher = Aes192Cbc;
} else if (dekInfo.first() == "AES-256-CBC") {
cipher = Aes256Cbc;
#endif // QT_SECURETRANSPORT
} else { } else {
clear(deepClear); clear(deepClear);
return; return;
@ -554,6 +598,10 @@ static EncryptionData readPbes2(const QVector<QAsn1Element> &element, const QByt
return {}; return {};
break; break;
} // @todo(?): case (RC5 , AES) } // @todo(?): case (RC5 , AES)
case QSslKeyPrivate::Cipher::Aes128Cbc:
case QSslKeyPrivate::Cipher::Aes192Cbc:
case QSslKeyPrivate::Cipher::Aes256Cbc:
Q_UNREACHABLE();
} }
if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) { if (Q_LIKELY(keyDerivationAlgorithm == PKCS5_PBKDF2_ENCRYPTION_OID)) {

View File

@ -57,6 +57,7 @@ const wchar_t *getName(QSslKeyPrivate::Cipher cipher)
return BCRYPT_3DES_ALGORITHM; return BCRYPT_3DES_ALGORITHM;
case QSslKeyPrivate::Cipher::Rc2Cbc: case QSslKeyPrivate::Cipher::Rc2Cbc:
return BCRYPT_RC2_ALGORITHM; return BCRYPT_RC2_ALGORITHM;
default:;
} }
Q_UNREACHABLE(); Q_UNREACHABLE();
} }

View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,A2A6F6BA67CFB2A992BA4FD3A0984B59
L5G1mwcXwW30lFty1HaEHlswFXAGk9+qf0TdYYNAAvVrsTMgfMq/6xM5XWo3IgbN
gG4K6T57gQkAywn+upqMHobB+7qc3DRzYlrm89gb74gHOe95l/iUJp4ii+ROLcmY
fg/vNmDSB/D0eM91WfwId7ticYD29+BUbbnqSYyY2S7K7DytYLpXqg3u335GYCdT
JwOsgcgbOICytkgK6c9ZDF3IrkzvWospVuiG5IfpLQkUXlJO3YGJ/oGf1BXnRd/b
kTzUiimUVunX62muHaUXKkAmXS8FCdB0puI+52pzLJ5FHdFxCcnwSG09TmoXbwwa
KoNM+IshNHPBGM7QxflVbSDxDaF1FWLwWSb8+Fhb2fTpfEGMxRCQ8HB1ZeMV4E5W
DSiNhih8ziC0k957ZYv8iuLanoM1YYIdToHeBwjyBJA836eIcq/ElY2QtKUq5PRw
+sU1BdG+f9rf4iAPHpgWZAKFmJ42ya71bEEVAmfysAOPuc4hpn3SsDTtihm9RKc9
l7LWJHaTnTu6yJA+vMJwAmPWg+IdG5vntbb93X4cgl5ZadBySRtv37wWyQPnQcFh
ytX8z2CJNIFJb0ik8bXc39zOxExoTu/o86IuVJ87jFdS1wz3PRek6dJdl15icx76
yAT0YB2/ZlRcRrO9hSm0D6P+sLOh//dyhhFAlUrDxqrKngI3KF4kgIrSlva3wmx2
t16SiUKu6FGQZk6/KYOV27Cy+8UJEqlrNJzy+wSFi26d6e6xWTIR2ItzQCxhYDmq
Tpx0Mh0ml2+bgrKRoDAL5z6UNy0Pc6bYQjvMznIeiuGvL8bAKTDUFwbmrZqNScsl
tW7yNZG9iSJnAZGMTxuOhSvJRpQkxIcLICd+lsUxWZ2YvFxtSORuRNSwaC7oxtTD
gIXV08ayoDbDmcguqTXWuCxtguxNANjhsUOetNHL8iP8QFrzAd5Ith9FgASCIBJJ
3X7vL2YGc3E6DlAJE01loqySU/cnu6/zQapLB9BIzdtoLliwdrJ7PS8FSsBDfZ2X
i6/7gb1jxYkJAS1NqrUMJw6BphRAwF8ny+FtPJ23Oaf+1vRIGiHsh8qw6XBfwFw9
vtsUUL19r+8zMpvIB6gf34TLuM7AW7idu3c/486EWgZBDL3mOTd3fsyADKv/HCk7
c8M2dsafxI6QkTlWsB8G5vkZ8lCGKHjrmPWjfD7NXi+CvXIrDY+gOeVN3PlQCU/2
zF2vIxKtR0CXuxLzIjFhIgTYR5G5ZnddMmHeVkZdPRl7szGtrxOA4QGJQ6ZT4W2e
O1whVU2KB0aBYskhClimapM5ypRkcNQ97cUR6/iNgdgSLqxGHCGeMR9bEyLl7/wr
M0XeDjdVfm/Tj548oHgb0SKLsfL6nnKwqB2viKj81moK9A/wO1Ec9RNaw0jtp2j3
VIUnPj0GqEjnkHc2jWY2yt4SD6e2AZHwLyWi1q3pixZo1CFiEgFXxwNyYwyeJ6jV
CJHPRzoNjZ8dkvgRjsXdnWwN316JBNVcH8k7CCmg/8Gq3yAojXG1z8VJZ06GHckd
meCL1t89OgwIAmIsysKu7+DrKtSlhkQclZmdG6IrQzuPKaHzTPTDgg3ef3jQ4YQO
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-192-CBC,B408346ADE790F8CF0C902A4F0712B34
SwzPBGxmwW2JddOyug1LrWjlZn8siSp5yezjK1x/z2+J2r/vvH8OjGnA387tFtae
WVTmhT1ixQXMDI1UJuKx0gzrG2449c+BUVe2VXFPLZ2ocSgoXbBpVkfhEqtLAn91
MSOpQMxvobQKltKhxgXGvuBJwhwfT7yK5HamohFGbxLUh4Dh+NBXwoYH4Qt+kM7C
kV8VIKvkr/QAL/SRxNoY8rVResPgYvUjdtiGSNZ6CZhNRu42Q2FqbH817cE0NDsN
il/xvWu4T/6VY1KpwMad/v6BhO45EeKz7YjbF/3Y5jj2JV9r45uf79lM9htMBw0d
L+Cc3YHeFffgU8NZo0+iUoroXcb7mjWNmgYksbkaZPbLG383YXAXwbkQS7zDMVIx
QhXn9w+78hNmEV/7PQ8mGXHEFwnfSR05phXoj8IyL5v0grRMA2dsjfxCgfQjH+kc
Miwr5pD/Flw175OpPFCb2qladdTKoIWiVShspbteoRC0EuiWHzkl5z6Tneyb/sam
yduLmSYD+RA6OBgUPY95Xm4AowlFFsuV/fxYZ53rFf4cZn9Z6VBVmvIEmapV7CtB
JzyIVclocwM0ag5u/esdEt/jndJq9chZlIsDS30y3gP6Rlqk5mj90DAs98l28FVG
WY9jP0babk8mxjYCcnAy7ikUc0D+vJVO6OTmfO3dkGjLpMBM6OlvfhN/0qeXrMDI
nU2qOshUrVna2kRe6FrcvosFTD8wvQ1/BjmCp1iWWsGdc/q1BqI3pgOlgq3TYfl6
iUJoji3V2iexH+GPkHsrs+kii1clsO2tgIP7doIooSVkcTsRTHHxKeeHn3qL2028
pTvieIFD/T4biLZ9Q8sX3XWiHNmXZlCx8lX8MDjTavWES8gY4H5Sr6FjRMy1qpZY
5w1aAyJ9YZ0J/jLPmFxt8mWgqHPiPlrQkryBBE3l1MSQ/hCEwlf9dP8a+ayINfd7
3yNkHKjZ5fuoA+TZUQb/fyVM5o1zJ8ML01PaXWrMEgr3b36QL+Ivo2Rpnp9FpwuH
E405cwCEy5fNSyhHFqqatCbsPl80nkP8OpW6jdWvNy9u0Ap9PS+MbHGq/pfkaazl
fbKGOckrENzEXi6Uj/yY/0sMtbTJuC70n09X3edHyhl/RJPPUoNnwDM5W1FHfS3r
qqSOl/r3y5pEErRdBpR4wEgB7DCLBALGDPfXNAAga4ez/Z2X9Zj234+4ZbUzWoLN
1ER0QYyxLN7oz1qMA15J7nRyRIhDXNlXjyISOqy26T6/d4X0M+6RhNWfT/MHAjJ8
6ogoGlQUITV3gPO4R6+FGZMF4R6zZXuKVtOXyzRwWnCLfo4gzBHmq+5mmCjGWRq8
rSSkc6334bYuZOaEoR5EM5sh6ewkjSDPRrKR7EHO02YbiscyT/99TwT5pdIOPK3u
2T6/40fSmCWQyBLuWxV9CMD+rB/q8Ja5KisEseck7PgI2pMmHfiD5yQXhKR9eDrB
sRqZxjgRYxup+/0CIBshL8s1R88xelhXyIKyqFfVudM09yAZxEJLQhpDZ27g45ea
FMX2Ve+ah2NjYBgzAhwKouWg5RyWb6X99NsrCEU75fn/ek86LGs3FxRgB4Uv7Udv
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,30 @@
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,0F2F4695C8FFA35F4076FA0273A3A4E3
GCnMcAhhGuNkJ7SSMBrgNOaDfRtG22J0mdf/0VbrMOJF40P13YBjN3Kd6LpTqBya
TCIaxQqtfjH1ffhJk8qhwG7uJFGgcY9i0dkrEYklgThzTVqHp7FsQ2jjgJs5HKpc
euuVD1bxtuc9qI2hq4miA7Z/uDe3M34n+3xcpqccWS1dLFNFZ+fIDwIazfDCu3ah
fUQHDeWLwOqYiQxhUjjrHpZkI1FE2JYZFaf4zIagIIgzI2O+33fgbrTSoeN5meRs
F7V2fhDpyEoIlchwAmp6HE6ngtKP4Ecju00yn99AO42fn097yEVwvGFClQTaIzur
aPEtuKZ4kdc/lmzL2tqNcZckq28ZxpeMq0Fgdcpeg5sDcut811scOjQLFs551On1
j3E1WfiLoBLKgd9cgmCrZb8hMO+UjcCaV7Jv3T9vDrbvhWs/YhTTwo8UFVplh7Vx
R1h3cKfzlbtOC5WHXGNK5dBu7SnpEk0+pscY5cxTrzN0odjMbbsjZQmKDZXbmZON
USzG3Qtafm6Nw/jwQeIjeqaxSho6xGdadTteGaURw6iGio3h0c6/dHayCsxye4tk
vODa0ZdJASwVh1605qDk9n/iYUT5B46KJCYwO/iN2kUmOcUcZeBqEfV1GfRmepZJ
bwM4sipzE15hOJ5DKSkHWnSlByRMAdSrMxZWraKUczn5frEBAqEFLlBAvf/FnjWa
yZJitgryCI2Y2bww1DEMnTCX345kUQIFmmjbzTIXnM28gW+fR3Br9dCf2FAsLNKr
tru1cYXocPaCUHEqS+XZqVb6BQVQ11YAAde0+x9RknJgsBc9Y7TLobaDBvrV5/nK
T3vm8el08upum6qPTPh6Z0zBbjx4sp6DYT977N1dYeH4n+0JqcSwIeZg/VAdG0RL
GzgZVADpiRlStmy65W95KExBjbO0tRTVk/nB1U1nfLbsswp9EKxXgwtpE/ECeTOi
hzeJBSsXGZ/ZXu/y+NlIu0B/GasFbfrHKslSSrUPTjGEtaEbLNiOEu2Etu3lRcMZ
oDtMxgNR0TUgCS5nte8lLauYYfB6IuxZXpvJdcI5ushqdOJvvYgJ9Yb2ZPlY8Bt5
C92Ga69aPcMYk24BPpe15eBbXMsFF8RF+CprVoUPCc+PcuROtxdt+rqoqjQeZPmV
WQqq+pT2bychpwD7U5jxQnu4u2m+zeBXyk80euBbwEld9BCgfk9mFj6CdBJSEiGV
qL0ivxd3mDaIKPGd5tcbrOMK2uD7duZY7FrQpAYgryoQJoUHccL7cr9fDC75akHg
AbrG1+vAYEla/y+SlOg5VTHhiuIl17ZGMViXSqh7iqnnD0dNsZ/HDvk3XouhNxQy
RQmfdqyIqLuAcfWwQxCQ2E/oMUIHjNhyYmfLVLVGsfxuevMa1eJv7rZ5vIkD2Vpe
4VveZkNDSpCCNqnvub8+bMW+UXyzbxEZbK5PLkRp7cvtKdA5CUbTlT4060IV0YZ1
vfMtzXRw8JDD9c1F1WF14afk+y9kvZN88XOH12bSKj+Re06Xx7OzuYU8fclq/pZB
UZVtRETFnLgb8neMuz3vCoPWK/DSHDZGAAicxq7vTljyoU/QP71Dw7UJIAuYx6Mc
-----END RSA PRIVATE KEY-----

View File

@ -466,15 +466,25 @@ void tst_QSslKey::toEncryptedPemOrDer()
void tst_QSslKey::passphraseChecks_data() void tst_QSslKey::passphraseChecks_data()
{ {
QTest::addColumn<QString>("fileName"); QTest::addColumn<QString>("fileName");
QTest::addColumn<QByteArray>("passphrase");
QTest::newRow("DES") << (testDataDir + "rsa-with-passphrase-des.pem"); const QByteArray pass("123");
QTest::newRow("3DES") << (testDataDir + "rsa-with-passphrase-3des.pem"); const QByteArray aesPass("1234");
QTest::newRow("RC2") << (testDataDir + "rsa-with-passphrase-rc2.pem");
QTest::newRow("DES") << QString(testDataDir + "rsa-with-passphrase-des.pem") << pass;
QTest::newRow("3DES") << QString(testDataDir + "rsa-with-passphrase-3des.pem") << pass;
QTest::newRow("RC2") << QString(testDataDir + "rsa-with-passphrase-rc2.pem") << pass;
#if (!defined(QT_NO_OPENSSL) && !defined(OPENSSL_NO_AES)) || defined(QT_SECURETRANSPORT)
QTest::newRow("AES128") << QString(testDataDir + "rsa-with-passphrase-aes128.pem") << aesPass;
QTest::newRow("AES192") << QString(testDataDir + "rsa-with-passphrase-aes192.pem") << aesPass;
QTest::newRow("AES256") << QString(testDataDir + "rsa-with-passphrase-aes256.pem") << aesPass;
#endif
} }
void tst_QSslKey::passphraseChecks() void tst_QSslKey::passphraseChecks()
{ {
QFETCH(QString, fileName); QFETCH(QString, fileName);
QFETCH(QByteArray, passphrase);
QFile keyFile(fileName); QFile keyFile(fileName);
QVERIFY(keyFile.exists()); QVERIFY(keyFile.exists());
@ -507,7 +517,7 @@ void tst_QSslKey::passphraseChecks()
keyFile.open(QIODevice::ReadOnly); keyFile.open(QIODevice::ReadOnly);
else else
keyFile.reset(); keyFile.reset();
QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "123"); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, passphrase);
QVERIFY(!key.isNull()); // correct passphrase QVERIFY(!key.isNull()); // correct passphrase
} }
} }