mirror of https://github.com/JakubMelka/PDF4QT.git
Certificate info
This commit is contained in:
parent
f602eb5a73
commit
6a089a953e
|
@ -383,6 +383,23 @@ void PDFPublicKeySignatureHandler::verifyCertificate(PDFSignatureVerificationRes
|
||||||
result.addCertificateOtherError(error);
|
result.addCertificateOtherError(error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will add certificate info for all certificates
|
||||||
|
const int count = sk_X509_num(certificates);
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
result.addCertificateInfo(getCertificateInfo(sk_X509_value(certificates, i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
STACK_OF(X509)* validChain = X509_STORE_CTX_get1_chain(context);
|
||||||
|
const int count = sk_X509_num(validChain);
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
result.addCertificateInfo(getCertificateInfo(sk_X509_value(validChain, i)));
|
||||||
|
}
|
||||||
|
sk_X509_pop_free(validChain, X509_free);
|
||||||
}
|
}
|
||||||
X509_STORE_CTX_cleanup(context);
|
X509_STORE_CTX_cleanup(context);
|
||||||
}
|
}
|
||||||
|
@ -401,6 +418,11 @@ void PDFPublicKeySignatureHandler::verifyCertificate(PDFSignatureVerificationRes
|
||||||
{
|
{
|
||||||
result.addInvalidCertificateError();
|
result.addInvalidCertificateError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!result.hasCertificateError())
|
||||||
|
{
|
||||||
|
result.setFlag(PDFSignatureVerificationResult::Certificate_OK, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFPublicKeySignatureHandler::verifySignature(PDFSignatureVerificationResult& result) const
|
void PDFPublicKeySignatureHandler::verifySignature(PDFSignatureVerificationResult& result) const
|
||||||
|
@ -417,6 +439,175 @@ PDFSignatureVerificationResult PDFSignatureHandler_adbe_pkcs7_detached::verify()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PDFCertificateInfo PDFPublicKeySignatureHandler::getCertificateInfo(X509* certificate)
|
||||||
|
{
|
||||||
|
PDFCertificateInfo info;
|
||||||
|
|
||||||
|
if (X509_NAME* subjectName = X509_get_subject_name(certificate))
|
||||||
|
{
|
||||||
|
// List of these properties are in RFC 5280, section 4.1.2.4, these attributes
|
||||||
|
// are standard and all implementations must be prepared to process them.
|
||||||
|
QString countryName = getStringFromX509Name(subjectName, NID_countryName);
|
||||||
|
QString organizationName = getStringFromX509Name(subjectName, NID_organizationName);
|
||||||
|
QString organizationalUnitName = getStringFromX509Name(subjectName, NID_organizationalUnitName);
|
||||||
|
QString distinguishedName = getStringFromX509Name(subjectName, NID_distinguishedName);
|
||||||
|
QString stateOrProvinceName = getStringFromX509Name(subjectName, NID_stateOrProvinceName);
|
||||||
|
QString commonName = getStringFromX509Name(subjectName, NID_commonName);
|
||||||
|
QString serialNumber = getStringFromX509Name(subjectName, NID_serialNumber);
|
||||||
|
|
||||||
|
// These attributes are defined also in section 4.1.2.4, they are not mandatory,
|
||||||
|
// but application should be able to process them.
|
||||||
|
QString localityName = getStringFromX509Name(subjectName, NID_localityName);
|
||||||
|
QString title = getStringFromX509Name(subjectName, NID_title);
|
||||||
|
QString surname = getStringFromX509Name(subjectName, NID_surname);
|
||||||
|
QString givenName = getStringFromX509Name(subjectName, NID_givenName);
|
||||||
|
QString initials = getStringFromX509Name(subjectName, NID_initials);
|
||||||
|
QString pseudonym = getStringFromX509Name(subjectName, NID_pseudonym);
|
||||||
|
QString generationQualifier = getStringFromX509Name(subjectName, NID_generationQualifier);
|
||||||
|
|
||||||
|
// This entry is not defined in section 4.1.2.4, but is commonly used
|
||||||
|
QString email = getStringFromX509Name(subjectName, NID_pkcs9_emailAddress);
|
||||||
|
|
||||||
|
info.setName(PDFCertificateInfo::CountryName, qMove(countryName));
|
||||||
|
info.setName(PDFCertificateInfo::OrganizationName, qMove(organizationName));
|
||||||
|
info.setName(PDFCertificateInfo::OrganizationalUnitName, qMove(organizationalUnitName));
|
||||||
|
info.setName(PDFCertificateInfo::DistinguishedName, qMove(distinguishedName));
|
||||||
|
info.setName(PDFCertificateInfo::StateOrProvinceName, qMove(stateOrProvinceName));
|
||||||
|
info.setName(PDFCertificateInfo::CommonName, qMove(commonName));
|
||||||
|
info.setName(PDFCertificateInfo::SerialNumber, qMove(serialNumber));
|
||||||
|
|
||||||
|
info.setName(PDFCertificateInfo::LocalityName, qMove(localityName));
|
||||||
|
info.setName(PDFCertificateInfo::Title, qMove(title));
|
||||||
|
info.setName(PDFCertificateInfo::Surname, qMove(surname));
|
||||||
|
info.setName(PDFCertificateInfo::GivenName, qMove(givenName));
|
||||||
|
info.setName(PDFCertificateInfo::Initials, qMove(initials));
|
||||||
|
info.setName(PDFCertificateInfo::Pseudonym, qMove(pseudonym));
|
||||||
|
info.setName(PDFCertificateInfo::GenerationalQualifier, qMove(generationQualifier));
|
||||||
|
|
||||||
|
info.setName(PDFCertificateInfo::Email, qMove(email));
|
||||||
|
|
||||||
|
const long version = X509_get_version(certificate);
|
||||||
|
info.setVersion(version);
|
||||||
|
|
||||||
|
const ASN1_TIME* notBeforeTime = X509_get0_notBefore(certificate);
|
||||||
|
const ASN1_TIME* notAfterTime = X509_get0_notAfter(certificate);
|
||||||
|
|
||||||
|
info.setNotValidBefore(getDateTimeFromASN(notBeforeTime));
|
||||||
|
info.setNotValidAfter(getDateTimeFromASN(notAfterTime));
|
||||||
|
|
||||||
|
X509_PUBKEY* publicKey = X509_get_X509_PUBKEY(certificate);
|
||||||
|
EVP_PKEY* evpKey = X509_PUBKEY_get(publicKey);
|
||||||
|
const int keyType = EVP_PKEY_type(EVP_PKEY_base_id(evpKey));
|
||||||
|
|
||||||
|
PDFCertificateInfo::PublicKey key = PDFCertificateInfo::KeyUnknown;
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case EVP_PKEY_RSA:
|
||||||
|
key = PDFCertificateInfo::KeyRSA;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVP_PKEY_DSA:
|
||||||
|
key = PDFCertificateInfo::KeyDSA;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVP_PKEY_DH:
|
||||||
|
key = PDFCertificateInfo::KeyDH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EVP_PKEY_EC:
|
||||||
|
key = PDFCertificateInfo::KeyEC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
info.setPublicKey(key);
|
||||||
|
|
||||||
|
EVP_PKEY_bits();
|
||||||
|
EVP_PKEY_security_bits();
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime PDFCertificateInfo::getNotValidBefore() const
|
||||||
|
{
|
||||||
|
return m_notValidBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFCertificateInfo::setNotValidBefore(const QDateTime& notValidBefore)
|
||||||
|
{
|
||||||
|
m_notValidBefore = notValidBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime PDFCertificateInfo::getNotValidAfter() const
|
||||||
|
{
|
||||||
|
return m_notValidAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFCertificateInfo::setNotValidAfter(const QDateTime& notValidAfter)
|
||||||
|
{
|
||||||
|
m_notValidAfter = notValidAfter;
|
||||||
|
}
|
||||||
|
|
||||||
|
long PDFCertificateInfo::getVersion() const
|
||||||
|
{
|
||||||
|
return m_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFCertificateInfo::setVersion(long version)
|
||||||
|
{
|
||||||
|
m_version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFCertificateInfo::PublicKey PDFCertificateInfo::getPublicKey() const
|
||||||
|
{
|
||||||
|
return m_publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFCertificateInfo::setPublicKey(const PublicKey& publicKey)
|
||||||
|
{
|
||||||
|
m_publicKey = publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString PDFPublicKeySignatureHandler::getStringFromX509Name(X509_NAME* name, int nid)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
const int stringLocation = X509_NAME_get_index_by_NID(name, nid, -1);
|
||||||
|
X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, stringLocation);
|
||||||
|
if (ASN1_STRING* string = X509_NAME_ENTRY_get_data(entry))
|
||||||
|
{
|
||||||
|
// Jakub Melka: we must convert entry to UTF8 encoding using function ASN1_STRING_to_UTF8
|
||||||
|
unsigned char* utf8Buffer = nullptr;
|
||||||
|
int errorCodeOrLength = ASN1_STRING_to_UTF8(&utf8Buffer, string);
|
||||||
|
if (errorCodeOrLength > 0)
|
||||||
|
{
|
||||||
|
result = QString::fromUtf8(reinterpret_cast<const char*>(utf8Buffer), errorCodeOrLength);
|
||||||
|
}
|
||||||
|
OPENSSL_free(utf8Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime pdf::PDFPublicKeySignatureHandler::getDateTimeFromASN(const ASN1_TIME* time)
|
||||||
|
{
|
||||||
|
QDateTime result;
|
||||||
|
|
||||||
|
if (time)
|
||||||
|
{
|
||||||
|
tm internalTime = { };
|
||||||
|
if (ASN1_TIME_to_tm(time, &internalTime) > 0)
|
||||||
|
{
|
||||||
|
time_t localTime = mktime(&internalTime);
|
||||||
|
result = QDateTime::fromSecsSinceEpoch(localTime, Qt::LocalTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
#include "pdfglobal.h"
|
#include "pdfglobal.h"
|
||||||
#include "pdfobject.h"
|
#include "pdfobject.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
|
@ -144,6 +147,69 @@ private:
|
||||||
AuthentificationType m_propType = AuthentificationType::Invalid;
|
AuthentificationType m_propType = AuthentificationType::Invalid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Info about certificate, various details etc.
|
||||||
|
class PDFFORQTLIBSHARED_EXPORT PDFCertificateInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline PDFCertificateInfo() = default;
|
||||||
|
|
||||||
|
/// These entries are taken from RFC 5280, section 4.1.2.4,
|
||||||
|
/// they are supported and loaded from the certificate, with
|
||||||
|
/// exception of Email entry.
|
||||||
|
enum NameEntry
|
||||||
|
{
|
||||||
|
CountryName,
|
||||||
|
OrganizationName,
|
||||||
|
OrganizationalUnitName,
|
||||||
|
DistinguishedName,
|
||||||
|
StateOrProvinceName,
|
||||||
|
CommonName,
|
||||||
|
SerialNumber,
|
||||||
|
LocalityName,
|
||||||
|
Title,
|
||||||
|
Surname,
|
||||||
|
GivenName,
|
||||||
|
Initials,
|
||||||
|
Pseudonym,
|
||||||
|
GenerationalQualifier,
|
||||||
|
Email,
|
||||||
|
NameEnd
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PublicKey
|
||||||
|
{
|
||||||
|
KeyRSA,
|
||||||
|
KeyDSA,
|
||||||
|
KeyEC,
|
||||||
|
KeyDH,
|
||||||
|
KeyUnknown
|
||||||
|
};
|
||||||
|
|
||||||
|
const QString& getName(NameEntry name) const { return m_nameEntries[name]; }
|
||||||
|
void setName(NameEntry name, QString string) { m_nameEntries[name] = qMove(string); }
|
||||||
|
|
||||||
|
QDateTime getNotValidBefore() const;
|
||||||
|
void setNotValidBefore(const QDateTime& notValidBefore);
|
||||||
|
|
||||||
|
QDateTime getNotValidAfter() const;
|
||||||
|
void setNotValidAfter(const QDateTime& notValidAfter);
|
||||||
|
|
||||||
|
long getVersion() const;
|
||||||
|
void setVersion(long version);
|
||||||
|
|
||||||
|
PublicKey getPublicKey() const;
|
||||||
|
void setPublicKey(const PublicKey& publicKey);
|
||||||
|
|
||||||
|
private:
|
||||||
|
long m_version = 0;
|
||||||
|
PublicKey m_publicKey = KeyUnknown;
|
||||||
|
std::array<QString, NameEnd> m_nameEntries;
|
||||||
|
QDateTime m_notValidBefore;
|
||||||
|
QDateTime m_notValidAfter;
|
||||||
|
};
|
||||||
|
|
||||||
|
using PDFCertificateInfos = std::vector<PDFCertificateInfo>;
|
||||||
|
|
||||||
class PDFFORQTLIBSHARED_EXPORT PDFSignatureVerificationResult
|
class PDFFORQTLIBSHARED_EXPORT PDFSignatureVerificationResult
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -159,19 +225,25 @@ public:
|
||||||
{
|
{
|
||||||
None = 0x00000, ///< Used only for initialization
|
None = 0x00000, ///< Used only for initialization
|
||||||
OK = 0x00001, ///< Both certificate and signature is OK
|
OK = 0x00001, ///< Both certificate and signature is OK
|
||||||
Error_NoHandler = 0x00002, ///< No signature handler for given signature
|
Certificate_OK = 0x00002, ///< Certificate is OK
|
||||||
Error_Generic = 0x00004, ///< Generic error (uknown general error)
|
Signature_OK = 0x00004, ///< Signature is OK
|
||||||
|
Error_NoHandler = 0x00008, ///< No signature handler for given signature
|
||||||
|
Error_Generic = 0x00010, ///< Generic error (uknown general error)
|
||||||
|
|
||||||
Error_Certificate_Invalid = 0x00008, ///< Certificate is invalid
|
Error_Certificate_Invalid = 0x00020, ///< Certificate is invalid
|
||||||
Error_Certificate_NoSignatures = 0x00010, ///< No signature found in certificate data
|
Error_Certificate_NoSignatures = 0x00040, ///< No signature found in certificate data
|
||||||
Error_Certificate_Missing = 0x00020, ///< Certificate is missing
|
Error_Certificate_Missing = 0x00080, ///< Certificate is missing
|
||||||
Error_Certificate_Generic = 0x00040, ///< Generic error during certificate verification
|
Error_Certificate_Generic = 0x00100, ///< Generic error during certificate verification
|
||||||
Error_Certificate_Expired = 0x00080, ///< Certificate has expired
|
Error_Certificate_Expired = 0x00200, ///< Certificate has expired
|
||||||
Error_Certificate_SelfSigned = 0x00100, ///< Self signed certificate
|
Error_Certificate_SelfSigned = 0x00400, ///< Self signed certificate
|
||||||
Error_Certificate_SelfSignedChain = 0x00200, ///< Self signed certificate in chain
|
Error_Certificate_SelfSignedChain = 0x00800, ///< Self signed certificate in chain
|
||||||
Error_Certificate_TrustedNotFound = 0x00400, ///< No trusted certificate was found
|
Error_Certificate_TrustedNotFound = 0x01000, ///< No trusted certificate was found
|
||||||
Error_Certificate_Revoked = 0x00800, ///< Certificate has been revoked
|
Error_Certificate_Revoked = 0x02000, ///< Certificate has been revoked
|
||||||
Error_Certificate_Other = 0x01000, ///< Other certificate error. See OpenSSL code for details.
|
Error_Certificate_Other = 0x04000, ///< Other certificate error. See OpenSSL code for details.
|
||||||
|
|
||||||
|
Error_Certificates_Mask = Error_Certificate_Invalid | Error_Certificate_NoSignatures | Error_Certificate_Missing | Error_Certificate_Generic |
|
||||||
|
Error_Certificate_Expired | Error_Certificate_SelfSigned | Error_Certificate_SelfSignedChain | Error_Certificate_TrustedNotFound |
|
||||||
|
Error_Certificate_Revoked | Error_Certificate_Other
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(VerificationFlags, VerificationFlag)
|
Q_DECLARE_FLAGS(VerificationFlags, VerificationFlag)
|
||||||
|
|
||||||
|
@ -191,8 +263,12 @@ public:
|
||||||
void addCertificateOtherError(int error);
|
void addCertificateOtherError(int error);
|
||||||
|
|
||||||
bool isValid() const { return hasFlag(OK); }
|
bool isValid() const { return hasFlag(OK); }
|
||||||
|
bool isCertificateValid() const { return hasFlag(Certificate_OK); }
|
||||||
|
bool isSignatureValid() const { return hasFlag(Signature_OK); }
|
||||||
bool hasError() const { return !isValid(); }
|
bool hasError() const { return !isValid(); }
|
||||||
|
bool hasCertificateError() const { return m_flags & Error_Certificates_Mask; }
|
||||||
bool hasFlag(VerificationFlag flag) const { return m_flags.testFlag(flag); }
|
bool hasFlag(VerificationFlag flag) const { return m_flags.testFlag(flag); }
|
||||||
|
void setFlag(VerificationFlag flag, bool value) { m_flags.setFlag(flag, value); }
|
||||||
|
|
||||||
PDFObjectReference getSignatureFieldReference() const { return m_signatureFieldReference; }
|
PDFObjectReference getSignatureFieldReference() const { return m_signatureFieldReference; }
|
||||||
const QString& getSignatureFieldQualifiedName() const { return m_signatureFieldQualifiedName; }
|
const QString& getSignatureFieldQualifiedName() const { return m_signatureFieldQualifiedName; }
|
||||||
|
@ -201,11 +277,14 @@ public:
|
||||||
void setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName);
|
void setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName);
|
||||||
void setSignatureFieldReference(PDFObjectReference signatureFieldReference);
|
void setSignatureFieldReference(PDFObjectReference signatureFieldReference);
|
||||||
|
|
||||||
|
void addCertificateInfo(PDFCertificateInfo info) { m_certificateInfos.emplace_back(qMove(info)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VerificationFlags m_flags = None;
|
VerificationFlags m_flags = None;
|
||||||
PDFObjectReference m_signatureFieldReference;
|
PDFObjectReference m_signatureFieldReference;
|
||||||
QString m_signatureFieldQualifiedName;
|
QString m_signatureFieldQualifiedName;
|
||||||
QStringList m_errors;
|
QStringList m_errors;
|
||||||
|
PDFCertificateInfos m_certificateInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Signature handler. Can verify both certificate and signature validity.
|
/// Signature handler. Can verify both certificate and signature validity.
|
||||||
|
|
|
@ -46,6 +46,16 @@ protected:
|
||||||
/// Return a list of certificates from PKCS7 object
|
/// Return a list of certificates from PKCS7 object
|
||||||
static STACK_OF(X509)* getCertificates(PKCS7* pkcs7);
|
static STACK_OF(X509)* getCertificates(PKCS7* pkcs7);
|
||||||
|
|
||||||
|
/// Return certificate info for given certificate
|
||||||
|
static PDFCertificateInfo getCertificateInfo(X509* certificate);
|
||||||
|
|
||||||
|
/// Returns name converted to QString
|
||||||
|
static QString getStringFromX509Name(X509_NAME* name, int nid);
|
||||||
|
|
||||||
|
/// Converts ASN time to QDateTime. If conversion fails, then invalid
|
||||||
|
/// datetime is returned.
|
||||||
|
static QDateTime getDateTimeFromASN(const ASN1_TIME* time);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const PDFFormFieldSignature* m_signatureField;
|
const PDFFormFieldSignature* m_signatureField;
|
||||||
QByteArray m_sourceData;
|
QByteArray m_sourceData;
|
||||||
|
|
Loading…
Reference in New Issue