summaryrefslogtreecommitdiffstats
path: root/src/network/ssl
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@digia.com>2014-09-10 11:41:29 +0200
committerOswald Buddenhagen <oswald.buddenhagen@digia.com>2014-09-10 11:42:50 +0200
commitd572ab1bb446e880fcb8d27294ba8149550f1659 (patch)
treed29c449b551e47569c6d9f146ba9e86810c05353 /src/network/ssl
parent211cef46f6d9d8738c09f906f9c0c3080b445dc8 (diff)
parent71df09b6cca7cd7a673bf39f49d0dda28b78a860 (diff)
Merge remote-tracking branch 'origin/5.4' into dev
Diffstat (limited to 'src/network/ssl')
-rw-r--r--src/network/ssl/qasn1element.cpp55
-rw-r--r--src/network/ssl/qasn1element_p.h20
-rw-r--r--src/network/ssl/qsslcertificate.cpp150
-rw-r--r--src/network/ssl/qsslcertificate_p.h5
-rw-r--r--src/network/ssl/qsslcertificate_qt.cpp256
-rw-r--r--src/network/ssl/qsslkey_openssl.cpp3
-rw-r--r--src/network/ssl/qsslkey_p.cpp52
-rw-r--r--src/network/ssl/qsslkey_p.h13
-rw-r--r--src/network/ssl/qsslkey_qt.cpp82
-rw-r--r--src/network/ssl/qsslkey_winrt.cpp98
10 files changed, 560 insertions, 174 deletions
diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp
index d282a02827..f3f280d863 100644
--- a/src/network/ssl/qasn1element.cpp
+++ b/src/network/ssl/qasn1element.cpp
@@ -56,6 +56,14 @@ static OidNameMap createOidMap()
// used by unit tests
oids.insert(oids.end(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink"));
oids.insert(oids.end(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress"));
+ oids.insert(oids.end(), QByteArrayLiteral("1.3.6.1.5.5.7.1.1"), QByteArrayLiteral("authorityInfoAccess"));
+ oids.insert(oids.end(), QByteArrayLiteral("1.3.6.1.5.5.7.48.1"), QByteArrayLiteral("OCSP"));
+ oids.insert(oids.end(), QByteArrayLiteral("1.3.6.1.5.5.7.48.2"), QByteArrayLiteral("caIssuers"));
+ oids.insert(oids.end(), QByteArrayLiteral("2.5.29.14"), QByteArrayLiteral("subjectKeyIdentifier"));
+ oids.insert(oids.end(), QByteArrayLiteral("2.5.29.15"), QByteArrayLiteral("keyUsage"));
+ oids.insert(oids.end(), QByteArrayLiteral("2.5.29.17"), QByteArrayLiteral("subjectAltName"));
+ oids.insert(oids.end(), QByteArrayLiteral("2.5.29.19"), QByteArrayLiteral("basicConstraints"));
+ oids.insert(oids.end(), QByteArrayLiteral("2.5.29.35"), QByteArrayLiteral("authorityKeyIdentifier"));
oids.insert(oids.end(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O"));
oids.insert(oids.end(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU"));
oids.insert(oids.end(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title"));
@@ -155,6 +163,12 @@ void QAsn1Element::write(QDataStream &stream) const
stream.writeRawData(mValue.data(), mValue.size());
}
+QAsn1Element QAsn1Element::fromBool(bool val)
+{
+ return QAsn1Element(QAsn1Element::BooleanType,
+ QByteArray(1, val ? 0xff : 0x00));
+}
+
QAsn1Element QAsn1Element::fromInteger(unsigned int val)
{
QAsn1Element elem(QAsn1Element::IntegerType);
@@ -199,6 +213,23 @@ QAsn1Element QAsn1Element::fromObjectId(const QByteArray &id)
return elem;
}
+bool QAsn1Element::toBool(bool *ok) const
+{
+ if (*this == fromBool(true)) {
+ if (ok)
+ *ok = true;
+ return true;
+ } else if (*this == fromBool(false)) {
+ if (ok)
+ *ok = true;
+ return false;
+ } else {
+ if (ok)
+ *ok = false;
+ return false;
+ }
+}
+
QDateTime QAsn1Element::toDateTime() const
{
if (mValue.endsWith('Z')) {
@@ -242,6 +273,30 @@ QMultiMap<QByteArray, QString> QAsn1Element::toInfo() const
return info;
}
+qint64 QAsn1Element::toInteger(bool *ok) const
+{
+ if (mType != QAsn1Element::IntegerType || mValue.isEmpty()) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+
+ // NOTE: negative numbers are not handled
+ if (mValue.at(0) & 0x80) {
+ if (ok)
+ *ok = false;
+ return 0;
+ }
+
+ qint64 value = mValue.at(0) & 0x7f;
+ for (int i = 1; i < mValue.size(); ++i)
+ value = (value << 8) | quint8(mValue.at(i));
+
+ if (ok)
+ *ok = true;
+ return value;
+}
+
QVector<QAsn1Element> QAsn1Element::toVector() const
{
QVector<QAsn1Element> items;
diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h
index 6b3179ac35..36a7c90de3 100644
--- a/src/network/ssl/qasn1element_p.h
+++ b/src/network/ssl/qasn1element_p.h
@@ -59,11 +59,15 @@
QT_BEGIN_NAMESPACE
+#define RSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.113549.1.1.1")
+#define DSA_ENCRYPTION_OID QByteArrayLiteral("1.2.840.10040.4.1")
+
class Q_AUTOTEST_EXPORT QAsn1Element
{
public:
enum ElementType {
// universal
+ BooleanType = 0x01,
IntegerType = 0x02,
BitStringType = 0x03,
OctetStringType = 0x04,
@@ -77,10 +81,6 @@ public:
SequenceType = 0x30,
SetType = 0x31,
- // application
- Rfc822NameType = 0x81,
- DnsNameType = 0x82,
-
// context specific
Context0Type = 0xA0,
Context3Type = 0xA3
@@ -91,12 +91,15 @@ public:
bool read(const QByteArray &data);
void write(QDataStream &data) const;
+ static QAsn1Element fromBool(bool val);
static QAsn1Element fromInteger(unsigned int val);
static QAsn1Element fromVector(const QVector<QAsn1Element> &items);
static QAsn1Element fromObjectId(const QByteArray &id);
+ bool toBool(bool *ok = 0) const;
QDateTime toDateTime() const;
QMultiMap<QByteArray, QString> toInfo() const;
+ qint64 toInteger(bool *ok = 0) const;
QVector<QAsn1Element> toVector() const;
QByteArray toObjectId() const;
QByteArray toObjectName() const;
@@ -105,12 +108,21 @@ public:
quint8 type() const { return mType; }
QByteArray value() const { return mValue; }
+ friend inline bool operator==(const QAsn1Element &, const QAsn1Element &);
+ friend inline bool operator!=(const QAsn1Element &, const QAsn1Element &);
+
private:
quint8 mType;
QByteArray mValue;
};
Q_DECLARE_TYPEINFO(QAsn1Element, Q_MOVABLE_TYPE);
+inline bool operator==(const QAsn1Element &e1, const QAsn1Element &e2)
+{ return e1.mType == e2.mType && e1.mValue == e2.mValue; }
+
+inline bool operator!=(const QAsn1Element &e1, const QAsn1Element &e2)
+{ return e1.mType != e2.mType || e1.mValue != e2.mValue; }
+
QT_END_NAMESPACE
#endif
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index bae78f4347..47ea3343ea 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -122,7 +122,6 @@
#include "qsslcertificate.h"
#include "qsslcertificate_p.h"
-#include "qasn1element_p.h"
#include "qsslkey_p.h"
#include <QtCore/qdir.h>
@@ -642,155 +641,6 @@ static const char *certificate_blacklist[] = {
0
};
-bool QSslCertificatePrivate::parse(const QByteArray &data)
-{
-#ifndef QT_NO_OPENSSL
- Q_UNUSED(data);
-#else
- QAsn1Element root;
-
- QDataStream dataStream(data);
- if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType)
- return false;
-
- QDataStream rootStream(root.value());
- QAsn1Element cert;
- if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType)
- return false;
-
- // version or serial number
- QAsn1Element elem;
- QDataStream certStream(cert.value());
- if (!elem.read(certStream))
- return false;
-
- if (elem.type() == QAsn1Element::Context0Type) {
- QDataStream versionStream(elem.value());
- if (!elem.read(versionStream) || elem.type() != QAsn1Element::IntegerType)
- return false;
-
- versionString = QByteArray::number(elem.value()[0] + 1);
- if (!elem.read(certStream))
- return false;
- } else {
- versionString = QByteArray::number(1);
- }
-
- // serial number
- if (elem.type() != QAsn1Element::IntegerType)
- return false;
-
- QByteArray hexString;
- hexString.reserve(elem.value().size() * 3);
- for (int a = 0; a < elem.value().size(); ++a) {
- const quint8 b = elem.value().at(a);
- if (b || !hexString.isEmpty()) { // skip leading zeros
- hexString += QByteArray::number(b, 16).rightJustified(2, '0');
- hexString += ':';
- }
- }
- hexString.chop(1);
- serialNumberString = hexString;
-
- // algorithm ID
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- //qDebug() << "algorithm ID" << elem.type() << elem.length << elem.value().toHex();
-
- // issuer info
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
- issuerInfo = elem.toInfo();
-
- // validity period
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- QDataStream validityStream(elem.value());
- if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
- return false;
-
- notValidBefore = elem.toDateTime();
- if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
- return false;
-
- notValidAfter = elem.toDateTime();
-
- // subject name
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
- subjectInfo = elem.toInfo();
- subjectMatchesIssuer = issuerDer == subjectDer;
-
- // public key
- qint64 keyStart = certStream.device()->pos();
- if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
- publicKeyDerData.resize(certStream.device()->pos() - keyStart);
- QDataStream keyStream(elem.value());
- if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
- return false;
-
-
- // key algorithm
- if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType)
- return false;
-
- const QByteArray oid = elem.toObjectId();
- if (oid == "1.2.840.113549.1.1.1")
- publicKeyAlgorithm = QSsl::Rsa;
- else if (oid == "1.2.840.10040.4.1")
- publicKeyAlgorithm = QSsl::Dsa;
- else
- publicKeyAlgorithm = QSsl::Opaque;
-
- certStream.device()->seek(keyStart);
- certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size());
-
- // extensions
- while (elem.read(certStream)) {
- if (elem.type() == QAsn1Element::Context3Type) {
- if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) {
- QDataStream extStream(elem.value());
- while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) {
- QAsn1Element oidElem, valElem;
- QDataStream seqStream(elem.value());
- if (oidElem.read(seqStream) && oidElem.type() == QAsn1Element::ObjectIdentifierType &&
- valElem.read(seqStream) && valElem.type() == QAsn1Element::OctetStringType) {
- // alternative name
- if (oidElem.toObjectId() == QByteArray("2.5.29.17")) {
- QAsn1Element sanElem;
- if (sanElem.read(valElem.value()) && sanElem.type() == QAsn1Element::SequenceType) {
- QDataStream nameStream(sanElem.value());
- QAsn1Element nameElem;
- while (nameElem.read(nameStream)) {
- if (nameElem.type() == QAsn1Element::Rfc822NameType) {
- subjectAlternativeNames.insert(QSsl::EmailEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size()));
- } else if (nameElem.type() == QAsn1Element::DnsNameType) {
- subjectAlternativeNames.insert(QSsl::DnsEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size()));
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- derData = data.left(dataStream.device()->pos());
- null = false;
-
-#endif // QT_NO_OPENSSL
- return true;
-}
-
bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
{
for (int a = 0; certificate_blacklist[a] != 0; a++) {
diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h
index 472553c30c..b0c99e545d 100644
--- a/src/network/ssl/qsslcertificate_p.h
+++ b/src/network/ssl/qsslcertificate_p.h
@@ -109,13 +109,16 @@ public:
QSsl::KeyAlgorithm publicKeyAlgorithm;
QByteArray publicKeyDerData;
QMultiMap<QSsl::AlternativeNameEntryType, QString> subjectAlternativeNames;
+ QList<QSslCertificateExtension> extensions;
QByteArray derData;
+
+ bool parse(const QByteArray &data);
+ bool parseExtension(const QByteArray &data, QSslCertificateExtension *extension);
#endif
X509 *x509;
void init(const QByteArray &data, QSsl::EncodingFormat format);
- bool parse(const QByteArray &data);
static QByteArray asn1ObjectId(ASN1_OBJECT *object);
static QByteArray asn1ObjectName(ASN1_OBJECT *object);
diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp
index 391ee6f7f9..6ea78a408b 100644
--- a/src/network/ssl/qsslcertificate_qt.cpp
+++ b/src/network/ssl/qsslcertificate_qt.cpp
@@ -47,9 +47,17 @@
#include "qsslkey_p.h"
#include "qsslcertificateextension.h"
#include "qsslcertificateextension_p.h"
+#include "qasn1element_p.h"
QT_BEGIN_NAMESPACE
+enum GeneralNameType
+{
+ Rfc822NameType = 0x81,
+ DnsNameType = 0x82,
+ UniformResourceIdentifierType = 0x86
+};
+
bool QSslCertificate::operator==(const QSslCertificate &other) const
{
if (d == other.d)
@@ -150,8 +158,7 @@ QSslKey QSslCertificate::publicKey() const
QList<QSslCertificateExtension> QSslCertificate::extensions() const
{
- Q_UNIMPLEMENTED();
- return QList<QSslCertificateExtension>();
+ return d->extensions;
}
#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----"
@@ -263,4 +270,249 @@ QList<QSslCertificate> QSslCertificatePrivate::certificatesFromDer(const QByteAr
return certificates;
}
+static QByteArray colonSeparatedHex(const QByteArray &value)
+{
+ QByteArray hexString;
+ hexString.reserve(value.size() * 3);
+ for (int a = 0; a < value.size(); ++a) {
+ const quint8 b = value.at(a);
+ if (b || !hexString.isEmpty()) { // skip leading zeros
+ hexString += QByteArray::number(b, 16).rightJustified(2, '0');
+ hexString += ':';
+ }
+ }
+ hexString.chop(1);
+ return hexString;
+}
+
+bool QSslCertificatePrivate::parse(const QByteArray &data)
+{
+ QAsn1Element root;
+
+ QDataStream dataStream(data);
+ if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType)
+ return false;
+
+ QDataStream rootStream(root.value());
+ QAsn1Element cert;
+ if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType)
+ return false;
+
+ // version or serial number
+ QAsn1Element elem;
+ QDataStream certStream(cert.value());
+ if (!elem.read(certStream))
+ return false;
+
+ if (elem.type() == QAsn1Element::Context0Type) {
+ QDataStream versionStream(elem.value());
+ if (!elem.read(versionStream) || elem.type() != QAsn1Element::IntegerType)
+ return false;
+
+ versionString = QByteArray::number(elem.value()[0] + 1);
+ if (!elem.read(certStream))
+ return false;
+ } else {
+ versionString = QByteArray::number(1);
+ }
+
+ // serial number
+ if (elem.type() != QAsn1Element::IntegerType)
+ return false;
+ serialNumberString = colonSeparatedHex(elem.value());
+
+ // algorithm ID
+ if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+ // issuer info
+ if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+ QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
+ issuerInfo = elem.toInfo();
+
+ // validity period
+ if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+ QDataStream validityStream(elem.value());
+ if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
+ return false;
+
+ notValidBefore = elem.toDateTime();
+ if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType))
+ return false;
+
+ notValidAfter = elem.toDateTime();
+
+ // subject name
+ if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+ QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length());
+ subjectInfo = elem.toInfo();
+ subjectMatchesIssuer = issuerDer == subjectDer;
+
+ // public key
+ qint64 keyStart = certStream.device()->pos();
+ if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+ publicKeyDerData.resize(certStream.device()->pos() - keyStart);
+ QDataStream keyStream(elem.value());
+ if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType)
+ return false;
+
+
+ // key algorithm
+ if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType)
+ return false;
+
+ const QByteArray oid = elem.toObjectId();
+ if (oid == RSA_ENCRYPTION_OID)
+ publicKeyAlgorithm = QSsl::Rsa;
+ else if (oid == RSA_ENCRYPTION_OID)
+ publicKeyAlgorithm = QSsl::Dsa;
+ else
+ publicKeyAlgorithm = QSsl::Opaque;
+
+ certStream.device()->seek(keyStart);
+ certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size());
+
+ // extensions
+ while (elem.read(certStream)) {
+ if (elem.type() == QAsn1Element::Context3Type) {
+ if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) {
+ QDataStream extStream(elem.value());
+ while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) {
+ QSslCertificateExtension extension;
+ if (!parseExtension(elem.value(), &extension))
+ return false;
+ extensions << extension;
+
+ if (extension.oid() == QLatin1String("2.5.29.17")) {
+ // subjectAltName
+ QAsn1Element sanElem;
+ if (sanElem.read(extension.value().toByteArray()) && sanElem.type() == QAsn1Element::SequenceType) {
+ QDataStream nameStream(sanElem.value());
+ QAsn1Element nameElem;
+ while (nameElem.read(nameStream)) {
+ if (nameElem.type() == Rfc822NameType) {
+ subjectAlternativeNames.insert(QSsl::EmailEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size()));
+ } else if (nameElem.type() == DnsNameType) {
+ subjectAlternativeNames.insert(QSsl::DnsEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size()));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ derData = data.left(dataStream.device()->pos());
+ null = false;
+ return true;
+}
+
+bool QSslCertificatePrivate::parseExtension(const QByteArray &data, QSslCertificateExtension *extension)
+{
+ bool ok;
+ bool critical = false;
+ QAsn1Element oidElem, valElem;
+
+ QDataStream seqStream(data);
+
+ // oid
+ if (!oidElem.read(seqStream) || oidElem.type() != QAsn1Element::ObjectIdentifierType)
+ return false;
+ const QByteArray oid = oidElem.toObjectId();
+
+ // critical and value
+ if (!valElem.read(seqStream))
+ return false;
+ if (valElem.type() == QAsn1Element::BooleanType) {
+ critical = valElem.toBool(&ok);
+ if (!ok || !valElem.read(seqStream))
+ return false;
+ }
+ if (valElem.type() != QAsn1Element::OctetStringType)
+ return false;
+
+ // interpret value
+ QAsn1Element val;
+ bool supported = true;
+ QVariant value;
+ if (oid == QByteArrayLiteral("1.3.6.1.5.5.7.1.1")) {
+ // authorityInfoAccess
+ if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
+ return false;
+ QVariantMap result;
+ foreach (const QAsn1Element &el, val.toVector()) {
+ QVector<QAsn1Element> items = el.toVector();
+ if (items.size() != 2)
+ return false;
+ const QString key = QString::fromLatin1(items.at(0).toObjectName());
+ switch (items.at(1).type()) {
+ case Rfc822NameType:
+ case DnsNameType:
+ case UniformResourceIdentifierType:
+ result[key] = QString::fromLatin1(items.at(1).value(), items.at(1).value().size());
+ break;
+ }
+ }
+ value = result;
+ } else if (oid == QByteArrayLiteral("2.5.29.14")) {
+ // subjectKeyIdentifier
+ if (!val.read(valElem.value()) || val.type() != QAsn1Element::OctetStringType)
+ return false;
+ value = colonSeparatedHex(val.value()).toUpper();
+ } else if (oid == QByteArrayLiteral("2.5.29.19")) {
+ // basicConstraints
+ if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
+ return false;
+
+ QVariantMap result;
+ QVector<QAsn1Element> items = val.toVector();
+ if (items.size() > 0) {
+ result[QStringLiteral("ca")] = items.at(0).toBool(&ok);
+ if (!ok)
+ return false;
+ } else {
+ result[QStringLiteral("ca")] = false;
+ }
+ if (items.size() > 1) {
+ result[QStringLiteral("pathLenConstraint")] = items.at(1).toInteger(&ok);
+ if (!ok)
+ return false;
+ }
+ value = result;
+ } else if (oid == QByteArrayLiteral("2.5.29.35")) {
+ // authorityKeyIdentifier
+ if (!val.read(valElem.value()) || val.type() != QAsn1Element::SequenceType)
+ return false;
+ QVariantMap result;
+ foreach (const QAsn1Element &el, val.toVector()) {
+ if (el.type() == 0x80) {
+ result[QStringLiteral("keyid")] = el.value().toHex();
+ } else if (el.type() == 0x82) {
+ result[QStringLiteral("serial")] = colonSeparatedHex(el.value());
+ }
+ }
+ value = result;
+ } else {
+ supported = false;
+ value = valElem.value();
+ }
+
+ extension->d->critical = critical;
+ extension->d->supported = supported;
+ extension->d->oid = QString::fromLatin1(oid);
+ extension->d->name = QString::fromLatin1(oidElem.toObjectName());
+ extension->d->value = value;
+
+ return true;
+}
+
QT_END_NAMESPACE
diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp
index 7e78ac0fee..6b0fa954eb 100644
--- a/src/network/ssl/qsslkey_openssl.cpp
+++ b/src/network/ssl/qsslkey_openssl.cpp
@@ -111,7 +111,8 @@ bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey)
void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
{
- decodePem(pemFromDer(der), QByteArray(), deepClear);
+ QMap<QByteArray, QByteArray> headers;
+ decodePem(pemFromDer(der, headers), QByteArray(), deepClear);
}
void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index 2b0dab9933..b051ec6874 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -63,6 +63,7 @@
#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
+#include <QtCore/qbytearraymatcher.h>
#include <QtCore/qiodevice.h>
#ifndef QT_NO_DEBUG_STREAM
#include <QtCore/qdebug.h>
@@ -130,7 +131,7 @@ QByteArray QSslKeyPrivate::pemFooter() const
Returns a DER key formatted as PEM.
*/
-QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
+QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const
{
QByteArray pem(der.toBase64());
@@ -144,7 +145,16 @@ QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
if (rem)
pem.append('\n'); // ###
- pem.prepend(pemHeader() + '\n');
+ QByteArray extra;
+ if (!headers.isEmpty()) {
+ QMap<QByteArray, QByteArray>::const_iterator it = headers.constEnd();
+ do {
+ it--;
+ extra += it.key() + ": " + it.value() + '\n';
+ } while (it != headers.constBegin());
+ extra += '\n';
+ }
+ pem.prepend(pemHeader() + '\n' + extra);
pem.append(pemFooter() + '\n');
return pem;
@@ -155,7 +165,7 @@ QByteArray QSslKeyPrivate::pemFromDer(const QByteArray &der) const
Returns a PEM key formatted as DER.
*/
-QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const
+QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const
{
const QByteArray header = pemHeader();
const QByteArray footer = pemFooter();
@@ -169,6 +179,39 @@ QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const
der = der.mid(headerIndex + header.size(), footerIndex - (headerIndex + header.size()));
+ if (der.contains("Proc-Type:")) {
+ // taken from QHttpNetworkReplyPrivate::parseHeader
+ const QByteArrayMatcher lf("\n");
+ const QByteArrayMatcher colon(":");
+ int i = 0;
+ while (i < der.count()) {
+ int j = colon.indexIn(der, i); // field-name
+ if (j == -1)
+ break;
+ const QByteArray field = der.mid(i, j - i).trimmed();
+ j++;
+ // any number of LWS is allowed before and after the value
+ QByteArray value;
+ do {
+ i = lf.indexIn(der, j);
+ if (i == -1)
+ break;
+ if (!value.isEmpty())
+ value += ' ';
+ // check if we have CRLF or only LF
+ bool hasCR = (i && der[i-1] == '\r');
+ int length = i -(hasCR ? 1: 0) - j;
+ value += der.mid(j, length).trimmed();
+ j = ++i;
+ } while (i < der.count() && (der.at(i) == ' ' || der.at(i) == '\t'));
+ if (i == -1)
+ break; // something is wrong
+
+ headers->insert(field, value);
+ }
+ der = der.mid(i);
+ }
+
return QByteArray::fromBase64(der); // ignores newlines
}
@@ -337,7 +380,8 @@ QByteArray QSslKey::toDer(const QByteArray &passPhrase) const
return QByteArray();
#ifndef QT_NO_OPENSSL
- return d->derFromPem(toPem(passPhrase));
+ QMap<QByteArray, QByteArray> headers;
+ return d->derFromPem(toPem(passPhrase), &headers);
#else
return d->derData;
#endif
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index 9c1476038a..d24606e6a6 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -91,8 +91,8 @@ public:
bool deepClear = true);
QByteArray pemHeader() const;
QByteArray pemFooter() const;
- QByteArray pemFromDer(const QByteArray &der) const;
- QByteArray derFromPem(const QByteArray &pem) const;
+ QByteArray pemFromDer(const QByteArray &der, const QMap<QByteArray, QByteArray> &headers) const;
+ QByteArray derFromPem(const QByteArray &pem, QMap<QByteArray, QByteArray> *headers) const;
int length() const;
QByteArray toPem(const QByteArray &passPhrase) const;
@@ -106,6 +106,15 @@ public:
RSA *rsa;
DSA *dsa;
#else
+ enum Cipher {
+ DesCbc,
+ DesEde3Cbc,
+ Rc2Cbc
+ };
+
+ Q_AUTOTEST_EXPORT static QByteArray decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
+ Q_AUTOTEST_EXPORT static QByteArray encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv);
+
Qt::HANDLE opaque;
QByteArray derData;
int keyLength;
diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp
index feeb7d6f87..bc1ebc3c08 100644
--- a/src/network/ssl/qsslkey_qt.cpp
+++ b/src/network/ssl/qsslkey_qt.cpp
@@ -43,6 +43,8 @@
#include "qsslkey_p.h"
#include "qasn1element_p.h"
+#include <QtCore/qcryptographichash.h>
+
QT_USE_NAMESPACE
static const quint8 bits_table[256] = {
@@ -78,6 +80,31 @@ static int numberOfBits(const QByteArray &modulus)
return bits;
}
+static QByteArray deriveKey(QSslKeyPrivate::Cipher cipher, const QByteArray &passPhrase, const QByteArray &iv)
+{
+ QByteArray key;
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ hash.addData(passPhrase);
+ hash.addData(iv);
+ switch (cipher) {
+ case QSslKeyPrivate::DesCbc:
+ key = hash.result().left(8);
+ break;
+ case QSslKeyPrivate::DesEde3Cbc:
+ key = hash.result();
+ hash.reset();
+ hash.addData(key);
+ hash.addData(passPhrase);
+ hash.addData(iv);
+ key += hash.result().left(8);
+ break;
+ case QSslKeyPrivate::Rc2Cbc:
+ key = hash.result();
+ break;
+ }
+ return key;
+}
+
void QSslKeyPrivate::clear(bool deep)
{
Q_UNUSED(deep);
@@ -106,7 +133,7 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType)
return;
if (algorithm == QSsl::Rsa) {
- if (infoItems[0].toObjectId() != "1.2.840.113549.1.1.1")
+ if (infoItems[0].toObjectId() != RSA_ENCRYPTION_OID)
return;
// key data
if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty())
@@ -117,7 +144,7 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
return;
keyLength = numberOfBits(elem.value());
} else if (algorithm == QSsl::Dsa) {
- if (infoItems[0].toObjectId() != "1.2.840.10040.4.1")
+ if (infoItems[0].toObjectId() != DSA_ENCRYPTION_OID)
return;
if (infoItems[1].type() != QAsn1Element::SequenceType)
return;
@@ -155,12 +182,32 @@ void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear)
void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase,
bool deepClear)
{
- if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) {
- Q_UNIMPLEMENTED();
- return;
- }
+ QMap<QByteArray, QByteArray> headers;
+ QByteArray data = derFromPem(pem, &headers);
+ if (headers.value("Proc-Type") == "4,ENCRYPTED") {
+ QList<QByteArray> dekInfo = headers.value("DEK-Info").split(',');
+ if (dekInfo.size() != 2) {
+ clear(deepClear);
+ return;
+ }
+
+ Cipher cipher;
+ if (dekInfo.first() == "DES-CBC") {
+ cipher = DesCbc;
+ } else if (dekInfo.first() == "DES-EDE3-CBC") {
+ cipher = DesEde3Cbc;
+ } else if (dekInfo.first() == "RC2-CBC") {
+ cipher = Rc2Cbc;
+ } else {
+ clear(deepClear);
+ return;
+ }
- decodeDer(derFromPem(pem), deepClear);
+ const QByteArray iv = QByteArray::fromHex(dekInfo.last());
+ const QByteArray key = deriveKey(cipher, passPhrase, iv);
+ data = decrypt(cipher, data, key, iv);
+ }
+ decodeDer(data, deepClear);
}
int QSslKeyPrivate::length() const
@@ -170,12 +217,27 @@ int QSslKeyPrivate::length() const
QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const
{
+ QByteArray data;
+ QMap<QByteArray, QByteArray> headers;
+
if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) {
- Q_UNIMPLEMENTED();
- return QByteArray();
+ // ### use a cryptographically secure random number generator
+ QByteArray iv;
+ iv.resize(8);
+ for (int i = 0; i < iv.size(); ++i)
+ iv[i] = (qrand() & 0xff);
+
+ Cipher cipher = DesEde3Cbc;
+ const QByteArray key = deriveKey(cipher, passPhrase, iv);
+ data = encrypt(cipher, derData, key, iv);
+
+ headers.insert("Proc-Type", "4,ENCRYPTED");
+ headers.insert("DEK-Info", "DES-EDE3-CBC," + iv.toHex());
+ } else {
+ data = derData;
}
- return pemFromDer(derData);
+ return pemFromDer(data, headers);
}
Qt::HANDLE QSslKeyPrivate::handle() const
diff --git a/src/network/ssl/qsslkey_winrt.cpp b/src/network/ssl/qsslkey_winrt.cpp
index 2c83069694..c5b4146ee9 100644
--- a/src/network/ssl/qsslkey_winrt.cpp
+++ b/src/network/ssl/qsslkey_winrt.cpp
@@ -61,3 +61,101 @@ using namespace ABI::Windows::Security::Cryptography::Core;
using namespace ABI::Windows::Storage::Streams;
QT_USE_NAMESPACE
+
+struct SslKeyGlobal
+{
+ SslKeyGlobal()
+ {
+ HRESULT hr;
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_CryptographicEngine).Get(),
+ &engine);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ ComPtr<ISymmetricKeyAlgorithmProviderStatics> keyProviderFactory;
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_SymmetricKeyAlgorithmProvider).Get(),
+ &keyProviderFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"DES_CBC").Get(),
+ &keyProviders[QSslKeyPrivate::DesCbc]);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"3DES_CBC").Get(),
+ &keyProviders[QSslKeyPrivate::DesEde3Cbc]);
+ Q_ASSERT_SUCCEEDED(hr);
+ hr = keyProviderFactory->OpenAlgorithm(HString::MakeReference(L"RC2_CBC").Get(),
+ &keyProviders[QSslKeyPrivate::Rc2Cbc]);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(),
+ &bufferFactory);
+ Q_ASSERT_SUCCEEDED(hr);
+ }
+
+ ComPtr<ICryptographicEngineStatics> engine;
+ QHash<QSslKeyPrivate::Cipher, ComPtr<ISymmetricKeyAlgorithmProvider>> keyProviders;
+ ComPtr<ICryptographicBufferStatics> bufferFactory;
+};
+Q_GLOBAL_STATIC(SslKeyGlobal, g)
+
+static QByteArray doCrypt(QSslKeyPrivate::Cipher cipher, QByteArray data, const QByteArray &key, const QByteArray &iv, bool encrypt)
+{
+ HRESULT hr;
+
+ ISymmetricKeyAlgorithmProvider *keyProvider = g->keyProviders[cipher].Get();
+ Q_ASSERT(keyProvider);
+
+ ComPtr<IBuffer> keyBuffer;
+ hr = g->bufferFactory->CreateFromByteArray(key.length(), (BYTE *)key.data(), &keyBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<ICryptographicKey> cryptographicKey;
+ hr = keyProvider->CreateSymmetricKey(keyBuffer.Get(), &cryptographicKey);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ UINT32 blockLength;
+ hr = keyProvider->get_BlockLength(&blockLength);
+ Q_ASSERT_SUCCEEDED(hr);
+ if (encrypt) { // Add padding
+ const char padding = blockLength - data.length() % blockLength;
+ data += QByteArray(padding, padding);
+ }
+
+ ComPtr<IBuffer> dataBuffer;
+ hr = g->bufferFactory->CreateFromByteArray(data.length(), (BYTE *)data.data(), &dataBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IBuffer> ivBuffer;
+ hr = g->bufferFactory->CreateFromByteArray(iv.length(), (BYTE *)iv.data(), &ivBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<IBuffer> resultBuffer;
+ hr = encrypt ? g->engine->Encrypt(cryptographicKey.Get(), dataBuffer.Get(), ivBuffer.Get(), &resultBuffer)
+ : g->engine->Decrypt(cryptographicKey.Get(), dataBuffer.Get(), ivBuffer.Get(), &resultBuffer);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ UINT32 resultLength;
+ hr = resultBuffer->get_Length(&resultLength);
+ Q_ASSERT_SUCCEEDED(hr);
+ ComPtr<Windows::Storage::Streams::IBufferByteAccess> bufferAccess;
+ hr = resultBuffer.As(&bufferAccess);
+ Q_ASSERT_SUCCEEDED(hr);
+ byte *resultData;
+ hr = bufferAccess->Buffer(&resultData);
+ Q_ASSERT_SUCCEEDED(hr);
+
+ if (!encrypt) { // Remove padding
+ const uchar padding = resultData[resultLength - 1];
+ if (padding > 0 && padding <= blockLength)
+ resultLength -= padding;
+ else
+ qWarning("Invalid padding length of %u; decryption likely failed.", padding);
+ }
+
+ return QByteArray(reinterpret_cast<const char *>(resultData), resultLength);
+}
+
+QByteArray QSslKeyPrivate::decrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
+{
+ return doCrypt(cipher, data, key, iv, false);
+}
+
+QByteArray QSslKeyPrivate::encrypt(Cipher cipher, const QByteArray &data, const QByteArray &key, const QByteArray &iv)
+{
+ return doCrypt(cipher, data, key, iv, true);
+}