From 61f1f9ca8d43b1741c0852aec99b4aab606b2a35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hans-Ulrich=20J=C3=BCttner?= <huj@froreich-bioscientia.de>
Date: Mon, 26 Jun 2017 11:56:47 +0200
Subject: [PATCH] Export signature via Qt5 interface. Add support for
 signatures of SubFilter "ETSI.CAdES.detached". Add an optional validation
 time to method validateSignature(). Print full Subject Distinguished Name,
 signing time, hash algorithm and a statement wether the total document is
 signed in pdfsig.

Fixes bug #99271
---
 poppler/Form.cc             | 191 ++++++++++++++++++++++++++++++++++++++++++--
 poppler/Form.h              |  33 +++++++-
 poppler/SignatureHandler.cc |  34 +++++++-
 poppler/SignatureHandler.h  |   5 +-
 poppler/SignatureInfo.cc    |  33 +++++++-
 poppler/SignatureInfo.h     |   9 ++-
 qt5/src/poppler-form.cc     | 111 +++++++++++++++++++++++++
 qt5/src/poppler-form.h      |  58 ++++++++++++++
 utils/pdfsig.1              |   6 +-
 utils/pdfsig.cc             |  85 ++++++++++++++++++++
 10 files changed, 547 insertions(+), 18 deletions(-)

diff --git a/poppler/Form.cc b/poppler/Form.cc
index 4627a43..89bf5e0 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -28,10 +28,12 @@
 #endif
 
 #include <set>
+#include <limits>
 #include <stddef.h>
 #include <string.h>
 #include "goo/gmem.h"
 #include "goo/GooString.h"
+#include "goo/GooList.h"
 #include "Error.h"
 #include "Object.h"
 #include "Array.h"
@@ -442,11 +444,166 @@ FormWidgetSignature::FormWidgetSignature(PDFDoc *docA, Object *aobj, unsigned nu
 	FormWidget(docA, aobj, num, ref, p)
 {
   type = formSignature;
+  file_size = 0;
 }
 
-SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation)
+SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime)
 {
-  return static_cast<FormFieldSignature*>(field)->validateSignature(doVerifyCert, forceRevalidation);
+  return static_cast<FormFieldSignature*>(field)->validateSignature(doVerifyCert, forceRevalidation, validationTime);
+}
+
+GooList* FormWidgetSignature::getSignedRangeBounds()
+{
+  Object* obj = static_cast<FormFieldSignature*>(field)->getByteRange();
+  GooList* list = nullptr;
+  if (obj && obj->isArray())
+  {
+    int arrayLen = obj->arrayGetLength();
+    if (arrayLen%2 == 0)
+    {
+      list = new GooList;
+      for (int i = 0; i < arrayLen/2; ++i)
+      {
+        Object offsetObj, lenObj;
+        obj->arrayGet(2*i, &offsetObj);
+        obj->arrayGet(2*i+1, &lenObj);
+        if (offsetObj.isIntOrInt64() && lenObj.isIntOrInt64())
+        {
+          Goffset offset = offsetObj.getIntOrInt64();
+          Goffset len = lenObj.getIntOrInt64();
+          list->append(new Goffset(offset));
+          list->append(new Goffset(offset+len));
+        }
+        offsetObj.free();
+        lenObj.free();
+      }
+    }
+  }
+  return list;
+}
+
+GooString* FormWidgetSignature::getCheckedSignature()
+{
+  Goffset start = 0;
+  Goffset end = 0;
+  Object* obj = static_cast<FormFieldSignature*>(field)->getByteRange();
+  if (obj->isArray() && obj->arrayGetLength() == 4)
+  {
+    Object offsetObj1, lenObj, offsetObj2;
+    obj->arrayGet(0, &offsetObj1);
+    obj->arrayGet(1, &lenObj);
+    obj->arrayGet(2, &offsetObj2);
+    if (offsetObj1.isIntOrInt64() && offsetObj2.isIntOrInt64() && lenObj.isIntOrInt64())
+    {
+      start = offsetObj1.getIntOrInt64() + lenObj.getIntOrInt64();
+      end = offsetObj2.getIntOrInt64();
+    }
+    offsetObj1.free();
+    lenObj.free();
+    offsetObj2.free();
+  }
+  if (end >= start+6)
+  {
+    BaseStream* stream = doc->getBaseStream();
+    file_size = stream->getLength();
+    Goffset len = end-start;
+    stream->setPos(end-1);
+    int c2 = stream->lookChar();
+    stream->setPos(start);
+    int c1 = stream->getChar();
+    // PDF signatures are first ASN1 DER, then hex encoded PKCS#7 structures,
+    // possibly padded with 0 characters and enclosed in '<' and '>'.
+    // The ASN1 DER encoding of a PKCS#7 structure must start with the tag 0x30
+    // for SEQUENCE. The next byte must be 0x80 for ASN1 DER indefinite length
+    // encoding or (0x80 + n) for ASN1 DER definite length encoding
+    // where n is the number of subsequent "length bytes" which big-endian
+    // encode the length of the content of the SEQUENCE following them.
+    if (len <= std::numeric_limits<int>::max() && file_size > end && c1 == '<' && c2 == '>')
+    {
+      GooString gstr;
+      ++start;
+      --end;
+      len = end-start;
+      Goffset pos = 0;
+      do
+      {
+        c1 = stream->getChar();
+        if (c1 == EOF)
+          return nullptr;
+        gstr.append(static_cast<char>(c1));
+      } while (++pos < len);
+      if (gstr.getChar(0) == '3' && gstr.getChar(1) == '0')
+      {
+        if (gstr.getChar(2) == '8' && gstr.getChar(3) == '0')
+        {
+          // ASN1 DER indefinite length encoding:
+          // We only check that all characters up to the enclosing '>'
+          // are hex characters and that there are two hex encoded 0 bytes
+          // just before the enclosing '>' marking the end of the indefinite
+          // length encoding.
+          int paddingCount = 0;
+          while (gstr.getChar(len-1) == '0' && gstr.getChar(len-2) == '0')
+          {
+            ++paddingCount;
+            len -= 2;
+          }
+          if (paddingCount < 2 || len%2 == 1)
+            len = 0;
+        }
+        else if (gstr.getChar(2) == '8')
+        {
+          // ASN1 DER definite length encoding:
+          // We calculate the length of the following bytes from the length bytes and
+          // check that after the length bytes and the following calculated number of
+          // bytes all bytes up to the enclosing '>' character are hex encoded 0 bytes.
+          int lenBytes = gstr.getChar(3) - '0';
+          if (lenBytes > 0 && lenBytes <= 4)
+          {
+            int sigLen = 0;
+            for (int i = 0; i < 2*lenBytes; ++i)
+            {
+              sigLen <<= 4;
+              char c = gstr.getChar(i+4);
+              if (isdigit(c))
+                sigLen += c - '0';
+              else if (isxdigit(c) && c >= 'a')
+                sigLen += c - 'a' + 10;
+              else if (isxdigit(c) && c >= 'A')
+                sigLen += c - 'A' + 10;
+              else
+              {
+                len = 0;
+                break;
+              }
+            }
+            if (sigLen > 0 && 2*(sigLen+lenBytes) <= len-4)
+            {
+              for (int i = 2*(sigLen+lenBytes)+4; i < len; ++i)
+              {
+                if (gstr.getChar(i) != '0')
+                {
+                  len = 0;
+                  break;
+                }
+              }
+            }
+            else
+              len = 0;
+          }
+        }
+        for (int i = 0; i < len; ++i)
+        {
+          if (!isxdigit(gstr.getChar(i)))
+            len = 0;
+        }
+        if (len > 0)
+        {
+          return new GooString(&gstr, 0, len);
+        }
+      }
+    }
+  }
+  return nullptr;
 }
 
 void FormWidgetSignature::updateWidgetAppearance()
@@ -1386,7 +1543,9 @@ GooString *FormFieldChoice::getSelectedChoice() {
 // FormFieldSignature
 //------------------------------------------------------------------------
 FormFieldSignature::FormFieldSignature(PDFDoc *docA, Object *dict, const Ref& ref, FormField *parent, std::set<int> *usedParents)
-  : FormField(docA, dict, ref, parent, usedParents, formSignature)
+  : FormField(docA, dict, ref, parent, usedParents, formSignature),
+    signature_type(adbe_pkcs7_detached), byte_range(),
+    signature(nullptr), signature_info(nullptr)
 {
   signature = NULL;
 
@@ -1433,7 +1592,16 @@ void FormFieldSignature::parseInfo()
 
   // check if subfilter is supported for signature validation, only detached signatures work for now
   sig_dict.dictLookup("SubFilter", &subfilterName);
-  if (subfilterName.isName("adbe.pkcs7.detached") || subfilterName.isName("adbe.pkcs7.sha1")) {
+  if (subfilterName.isName("adbe.pkcs7.sha1")) {
+    signature_type = adbe_pkcs7_sha1;
+    signature_info->setSubFilterSupport(true);
+  }
+  if (subfilterName.isName("adbe.pkcs7.detached")) {
+    signature_type = adbe_pkcs7_detached;
+    signature_info->setSubFilterSupport(true);
+  }
+  else if (subfilterName.isName("ETSI.CAdES.detached")) {
+    signature_type = ETSI_CAdES_detached;
     signature_info->setSubFilterSupport(true);
   }
 
@@ -1467,8 +1635,17 @@ void FormFieldSignature::hashSignedDataBlock(SignatureHandler *handler, Goffset
 #endif
 }
 
+FormSignatureType FormWidgetSignature::signatureType()
+{
+  return static_cast<FormFieldSignature*>(field)->signature_type;
+}
+
+void FormWidgetSignature::setFormSignatureType(FormSignatureType type)
+{
+  static_cast<FormFieldSignature*>(field)->signature_type = type;
+}
 
-SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation)
+SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime)
 {
 #ifdef ENABLE_NSS3
   if (!signature_info->isSubfilterSupported()) {
@@ -1529,6 +1706,8 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
   sig_val_state = signature_handler.validateSignature();
   signature_info->setSignatureValStatus(SignatureHandler::NSS_SigTranslate(sig_val_state));
   signature_info->setSignerName(signature_handler.getSignerName());
+  signature_info->setSubjectDN(signature_handler.getSignerSubjectDN());
+  signature_info->setHashAlgorithm(signature_handler.getHashAlgorithm());
 
   // verify if signature contains a 'signing time' attribute
   if (signature_handler.getSigningTime() != 0) {
@@ -1539,7 +1718,7 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
     return signature_info;
   }
 
-  cert_val_state = signature_handler.validateCertificate();
+  cert_val_state = signature_handler.validateCertificate(validationTime);
   signature_info->setCertificateValStatus(SignatureHandler::NSS_CertTranslate(cert_val_state));
 
 #endif
diff --git a/poppler/Form.h b/poppler/Form.h
index 8ddb6fe..8e79c94 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -13,6 +13,7 @@
 // Copyright 2013 Adrian Johnson <ajohnson@redneon.com>
 // Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
 // Copyright 2015 André Esser <bepandre@hotmail.com>
+// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
 //
 //========================================================================
 
@@ -29,6 +30,7 @@
 #include <set>
 
 class GooString;
+class GooList;
 class Array;
 class Dict;
 class Annot;
@@ -60,6 +62,12 @@ enum VariableTextQuadding {
   quaddingRightJustified
 };
 
+enum FormSignatureType {
+  adbe_pkcs7_sha1,
+  adbe_pkcs7_detached,
+  ETSI_CAdES_detached
+};
+
 class Form;
 class FormField;
 class FormFieldButton;
@@ -251,7 +259,23 @@ public:
   FormWidgetSignature(PDFDoc *docA, Object *dict, unsigned num, Ref ref, FormField *p);
   void updateWidgetAppearance() override;
 
-  SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation);
+  FormSignatureType signatureType();
+  void setFormSignatureType(FormSignatureType type);
+  SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime = -1);
+
+  // returns a list with the boundaries of the signed ranges
+  // the elements of the list are of type Goffset
+  GooList* getSignedRangeBounds();
+
+  // checks the length encoding of the signature and returns the hex encoded signature
+  // if the check passed otherwise a nullptr is returned
+  GooString* getCheckedSignature();
+
+  // this method only gives the correct file size if getCheckedSignature()
+  // has been called before
+  Goffset getCheckedFileSize() const { return file_size; }
+protected:
+  Goffset file_size;
 };
 
 //------------------------------------------------------------------------
@@ -490,16 +514,21 @@ protected:
 //------------------------------------------------------------------------
 
 class FormFieldSignature: public FormField {
+  friend class FormWidgetSignature;
 public:
   FormFieldSignature(PDFDoc *docA, Object *dict, const Ref& ref, FormField *parent, std::set<int> *usedParents);
 
-  SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation);
+  SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime = -1);
 
   ~FormFieldSignature();
+  Object* getByteRange() { return &byte_range; }
+  GooString* getSignature() { return signature; }
 
 private:
   void parseInfo();
   void hashSignedDataBlock(SignatureHandler *handler, Goffset block_len);
+
+  FormSignatureType signature_type;
   Object byte_range;
   GooString *signature;
   SignatureInfo *signature_info;
diff --git a/poppler/SignatureHandler.cc b/poppler/SignatureHandler.cc
index 71644e5..bddc45f 100644
--- a/poppler/SignatureHandler.cc
+++ b/poppler/SignatureHandler.cc
@@ -9,6 +9,7 @@
 // Copyright 2015, 2016 Albert Astals Cid <aacid@kde.org>
 // Copyright 2015 Markus Kilås <digital@markuspage.com>
 // Copyright 2017 Sebastian Rasmussen <sebras@gmail.com>
+// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
 //
 //========================================================================
 
@@ -47,6 +48,26 @@ char *SignatureHandler::getSignerName()
   return CERT_GetCommonName(&cert->subject);
 }
 
+const char * SignatureHandler::getSignerSubjectDN()
+{
+  if (!CMSSignerInfo)
+    return nullptr;
+
+  CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
+  if (!cert)
+    return nullptr;
+  return cert->subjectName;
+}
+
+HASH_HashType SignatureHandler::getHashAlgorithm()
+{
+  if (hash_context && hash_context->hashobj)
+  {
+    return hash_context->hashobj->type;
+  }
+  return HASH_AlgNULL;
+}
+
 time_t SignatureHandler::getSigningTime()
 {
   PRTime sTime; // time in microseconds since the epoch
@@ -54,7 +75,7 @@ time_t SignatureHandler::getSigningTime()
   if (NSS_CMSSignerInfo_GetSigningTime (CMSSignerInfo, &sTime) != SECSuccess)
     return 0;
 
-  return (time_t) sTime/1000000;
+  return static_cast<time_t>(sTime/1000000);
 }
 
 
@@ -271,7 +292,7 @@ NSSCMSVerificationStatus SignatureHandler::validateSignature()
   }
 }
 
-SECErrorCodes SignatureHandler::validateCertificate()
+SECErrorCodes SignatureHandler::validateCertificate(time_t validation_time)
 {
   SECErrorCodes retVal;
   CERTCertificate *cert;
@@ -282,10 +303,15 @@ SECErrorCodes SignatureHandler::validateCertificate()
   if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == NULL)
     CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
 
-  CERTValInParam inParams[2];
+  PRTime vTime = 0; // time in microseconds since the epoch, special value 0 means now
+  if (validation_time > 0)
+    vTime = 1000000*(PRTime)validation_time;
+  CERTValInParam inParams[3];
   inParams[0].type = cert_pi_revocationFlags;
   inParams[0].value.pointer.revocation = CERT_GetClassicOCSPEnabledSoftFailurePolicy();
-  inParams[1].type = cert_pi_end;
+  inParams[1].type = cert_pi_date;
+  inParams[1].value.scalar.time = vTime;
+  inParams[2].type = cert_pi_end;
 
   CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, NULL,
                 CMSSignerInfo->cmsg->pwfn_arg);
diff --git a/poppler/SignatureHandler.h b/poppler/SignatureHandler.h
index 8e2a4da..715ab9f 100644
--- a/poppler/SignatureHandler.h
+++ b/poppler/SignatureHandler.h
@@ -7,6 +7,7 @@
 // Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
 // Copyright 2015 André Esser <bepandre@hotmail.com>
 // Copyright 2015 Albert Astals Cid <aacid@kde.org>
+// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
 //
 //========================================================================
 
@@ -36,10 +37,12 @@ public:
   ~SignatureHandler();
   time_t getSigningTime();
   char * getSignerName();
+  const char * getSignerSubjectDN();
+  HASH_HashType getHashAlgorithm();
   void setSignature(unsigned char *, int);
   void updateHash(unsigned char * data_block, int data_len);
   NSSCMSVerificationStatus validateSignature();
-  SECErrorCodes validateCertificate();
+  SECErrorCodes validateCertificate(time_t validation_time = -1);
 
   //Translate NSS error codes
   static SignatureValidationStatus NSS_SigTranslate(NSSCMSVerificationStatus nss_code);
diff --git a/poppler/SignatureInfo.cc b/poppler/SignatureInfo.cc
index 8f7ec45..86b1cfa 100644
--- a/poppler/SignatureInfo.cc
+++ b/poppler/SignatureInfo.cc
@@ -6,6 +6,7 @@
 //
 // Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
 // Copyright 2015 André Esser <bepandre@hotmail.com>
+// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
 //
 //========================================================================
 
@@ -16,13 +17,17 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <hasht.h>
+
 /* Constructor & Destructor */
 
 SignatureInfo::SignatureInfo()
 {
   sig_status = SIGNATURE_NOT_VERIFIED;
   cert_status = CERTIFICATE_NOT_VERIFIED;
-  signer_name = NULL;
+  signer_name = nullptr;
+  subject_dn = nullptr;
+  hash_type = HASH_AlgNULL;
   signing_time = 0;
   sig_subfilter_supported = false;
 }
@@ -31,7 +36,9 @@ SignatureInfo::SignatureInfo(SignatureValidationStatus sig_val_status, Certifica
 {
   sig_status = sig_val_status;
   cert_status = cert_val_status;
-  signer_name = NULL;
+  signer_name = nullptr;
+  subject_dn = nullptr;
+  hash_type = HASH_AlgNULL;
   signing_time = 0;
   sig_subfilter_supported = false;
 }
@@ -53,11 +60,21 @@ CertificateValidationStatus SignatureInfo::getCertificateValStatus()
   return cert_status;
 }
 
-char *SignatureInfo::getSignerName()
+const char *SignatureInfo::getSignerName()
 {
   return signer_name;
 }
 
+const char *SignatureInfo::getSubjectDN()
+{
+  return subject_dn;
+}
+
+int SignatureInfo::getHashAlgorithm()
+{
+  return hash_type;
+}
+
 time_t SignatureInfo::getSigningTime()
 {
   return signing_time;
@@ -81,6 +98,16 @@ void SignatureInfo::setSignerName(char *signerName)
   signer_name = signerName;
 }
 
+void SignatureInfo::setSubjectDN(const char *subjectDN)
+{
+  subject_dn = subjectDN;
+}
+
+void SignatureInfo::setHashAlgorithm(int type)
+{
+  hash_type = type;
+}
+
 void SignatureInfo::setSigningTime(time_t signingTime)
 {
   signing_time = signingTime;
diff --git a/poppler/SignatureInfo.h b/poppler/SignatureInfo.h
index 82b4ec4..7ed4428 100644
--- a/poppler/SignatureInfo.h
+++ b/poppler/SignatureInfo.h
@@ -7,6 +7,7 @@
 // Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
 // Copyright 2015 André Esser <bepandre@hotmail.com>
 // Copyright 2015 Albert Astals Cid <aacid@kde.org>
+// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
 //
 //========================================================================
 
@@ -46,7 +47,9 @@ public:
   /* GETTERS */
   SignatureValidationStatus getSignatureValStatus();
   CertificateValidationStatus getCertificateValStatus();
-  char *getSignerName();
+  const char *getSignerName();
+  const char *getSubjectDN();
+  int getHashAlgorithm();
   time_t getSigningTime();
   bool isSubfilterSupported() { return sig_subfilter_supported; }
 
@@ -54,6 +57,8 @@ public:
   void setSignatureValStatus(enum SignatureValidationStatus );
   void setCertificateValStatus(enum CertificateValidationStatus );
   void setSignerName(char *);
+  void setSubjectDN(const char *);
+  void setHashAlgorithm(int);
   void setSigningTime(time_t);
   void setSubFilterSupport(bool isSupported) { sig_subfilter_supported = isSupported; }
 
@@ -64,6 +69,8 @@ private:
   SignatureValidationStatus sig_status;
   CertificateValidationStatus cert_status;
   char *signer_name;
+  const char *subject_dn;
+  int hash_type;
   time_t signing_time;
   bool sig_subfilter_supported;
 };
diff --git a/qt5/src/poppler-form.cc b/qt5/src/poppler-form.cc
index 1ccb26c..2a11c78 100644
--- a/qt5/src/poppler-form.cc
+++ b/qt5/src/poppler-form.cc
@@ -4,6 +4,7 @@
  * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org>
  * Copyright (C) 2012, Adam Reichold <adamreichold@myopera.com>
  * Copyright (C) 2016, Hanno Meyer-Thurow <h.mth@web.de>
+ * Copyright (C) 2017, Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,6 +36,20 @@
 #include "poppler-annotation-helper.h"
 
 #include <math.h>
+#include <ctype.h>
+
+enum HASH_HashType
+{
+    HASH_AlgNULL = 0,
+    HASH_AlgMD2 = 1,
+    HASH_AlgMD5 = 2,
+    HASH_AlgSHA1 = 3,
+    HASH_AlgSHA256 = 4,
+    HASH_AlgSHA384 = 5,
+    HASH_AlgSHA512 = 6,
+    HASH_AlgSHA224 = 7,
+    HASH_AlgTOTAL
+};
 
 namespace {
 
@@ -448,8 +463,13 @@ struct SignatureValidationInfoPrivate {
 	SignatureValidationInfo::SignatureStatus signature_status;
 	SignatureValidationInfo::CertificateStatus certificate_status;
 
+	QByteArray signature;
 	QString signer_name;
+	QString signer_subject_dn;
+	int hash_algorithm;
 	time_t signing_time;
+	QList<qint64> range_bounds;
+	qint64 docLength;
 };
 
 
@@ -485,12 +505,62 @@ QString SignatureValidationInfo::signerName() const
   return d->signer_name;
 }
 
+QString SignatureValidationInfo::signerSubjectDN() const
+{
+  Q_D(const SignatureValidationInfo);
+  return d->signer_subject_dn;
+}
+
+SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const
+{
+  Q_D(const SignatureValidationInfo);
+  return static_cast<HashAlgorithm>(d->hash_algorithm);
+}
+
 time_t SignatureValidationInfo::signingTime() const
 {
   Q_D(const SignatureValidationInfo);
   return d->signing_time;
 }
 
+QDateTime SignatureValidationInfo::signingDateTime() const
+{
+  return QDateTime::fromTime_t(signingTime());
+}
+
+QByteArray SignatureValidationInfo::signature() const
+{
+  Q_D(const SignatureValidationInfo);
+  return d->signature;
+}
+
+QList<qint64> SignatureValidationInfo::signedRangeBounds() const
+{
+  Q_D(const SignatureValidationInfo);
+  return d->range_bounds;
+}
+
+bool SignatureValidationInfo::signsTotalDocument() const
+{
+  Q_D(const SignatureValidationInfo);
+  if (d->range_bounds.size() == 4 && d->range_bounds.value(0) == 0 &&
+      d->range_bounds.value(1) >= 0 &&
+      d->range_bounds.value(2) > d->range_bounds.value(1) &&
+      d->range_bounds.value(3) >= d->range_bounds.value(2))
+  {
+    // The range from d->range_bounds.value(1) to d->range_bounds.value(2) is
+    // not authenticated by the signature and should only contain the signature
+    // itself padded with 0 bytes. This has been checked in readSignature().
+    // If it failed, d->signature is empty.
+    // A potential range after d->range_bounds.value(3) would be also not
+    // authenticated. Therefore d->range_bounds.value(3) should coincide with
+    // the end of the document.
+    if (d->docLength == d->range_bounds.value(3) && !d->signature.isEmpty())
+      return true;
+  }
+  return false;
+}
+
 SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValidationInfo &other)
 {
   if ( this != &other )
@@ -512,9 +582,33 @@ FormField::FormType FormFieldSignature::type() const
 {
   return FormField::FormSignature;
 }
+ 
+FormFieldSignature::SignatureType FormFieldSignature::signatureType() const
+{
+  SignatureType sigType = AdbePkcs7detached;
+  FormWidgetSignature* fws = static_cast<FormWidgetSignature*>(m_formData->fm);
+  switch (fws->signatureType())
+  {
+    case adbe_pkcs7_sha1:
+      sigType = AdbePkcs7sha1;
+      break;
+    case adbe_pkcs7_detached:
+      sigType = AdbePkcs7detached;
+      break;
+    case ETSI_CAdES_detached:
+      sigType = EtsiCAdESdetached;
+      break;
+  }
+  return sigType;
+}
 
 SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const
 {
+  return validate(opt, QDateTime());
+}
+
+SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime& validationTime) const
+{
   FormWidgetSignature* fws = static_cast<FormWidgetSignature*>(m_formData->fm);
   SignatureInfo* si = fws->validateSignature(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation);
   SignatureValidationInfoPrivate* priv = new SignatureValidationInfoPrivate;
@@ -567,7 +661,24 @@ SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const
       break;
   }
   priv->signer_name = si->getSignerName();
+  priv->signer_subject_dn = si->getSubjectDN();
+  priv->hash_algorithm = si->getHashAlgorithm();
+
   priv->signing_time = si->getSigningTime();
+  GooList* list = fws->getSignedRangeBounds();
+  if (list)
+  {
+    for (int i = 0; i < list->getLength(); ++i)
+    {
+      priv->range_bounds.append(*static_cast<long long*>(list->get(i)));
+    }
+    deleteGooList(list, Goffset);
+  }
+  if (priv->range_bounds.size() == 4)
+  {
+    priv->signature = QByteArray::fromHex(fws->getCheckedSignature()->getCString());
+    priv->docLength = fws->getCheckedFileSize();
+  }
 
   return SignatureValidationInfo(priv);
 }
diff --git a/qt5/src/poppler-form.h b/qt5/src/poppler-form.h
index 44928b3..7a8d575 100644
--- a/qt5/src/poppler-form.h
+++ b/qt5/src/poppler-form.h
@@ -3,6 +3,7 @@
  * Copyright (C) 2008, 2011, 2016, 2017, Albert Astals Cid <aacid@kde.org>
  * Copyright (C) 2012, Adam Reichold <adamreichold@myopera.com>
  * Copyright (C) 2016, Hanno Meyer-Thurow <h.mth@web.de>
+ * Copyright (C) 2017, Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,6 +23,8 @@
 #ifndef _POPPLER_QT5_FORM_H_
 #define _POPPLER_QT5_FORM_H_
 
+#include <QtCore/QDateTime>
+#include <QtCore/QList>
 #include <QtCore/QRectF>
 #include <QtCore/QStringList>
 #include <QtCore/QSharedPointer>
@@ -401,6 +404,22 @@ namespace Poppler {
 	    CertificateNotVerified      ///< The certificate is not yet verified.
 	};
 
+	/**
+	 * The hash algorithme of the signature
+	 */
+	enum HashAlgorithm
+	{
+	    HashAlgNULL = 0,
+	    HashAlgMD2 = 1,
+	    HashAlgMD5 = 2,
+	    HashAlgSHA1 = 3,
+	    HashAlgSHA256 = 4,
+	    HashAlgSHA384 = 5,
+	    HashAlgSHA512 = 6,
+	    HashAlgSHA224 = 7,
+	    HashAlgTOTAL
+	};
+
 	/// \cond PRIVATE
 	SignatureValidationInfo(SignatureValidationInfoPrivate *priv);
 	/// \endcond
@@ -422,9 +441,36 @@ namespace Poppler {
 	QString signerName() const;
 
 	/**
+	  The signer subject distinguished name associated with the signature.
+	 */
+	QString signerSubjectDN() const;
+
+	/**
+	  The the hash algorithm used for the signature.
+	 */
+	HashAlgorithm hashAlgorithm() const;
+
+	/**
 	  The signing time associated with the signature.
 	 */
 	time_t signingTime() const;
+	QDateTime signingDateTime() const;
+
+	/**
+	  Get the signature binary data.
+	 */
+        QByteArray signature() const;
+
+	/**
+	  Get the bounds of the ranges of the document which are signed.
+	 */
+        QList<qint64> signedRangeBounds() const;
+
+	/**
+	  Checks whether the signature authenticates the total document
+          except for the signature itself.
+	 */
+        bool signsTotalDocument() const;
 
 	SignatureValidationInfo(const SignatureValidationInfo &other);
 	SignatureValidationInfo &operator=(const SignatureValidationInfo &other);
@@ -443,6 +489,15 @@ namespace Poppler {
     class POPPLER_QT5_EXPORT FormFieldSignature : public FormField {
     public:
 
+        /**
+           The types of signature fields.
+         */
+        enum SignatureType {
+            AdbePkcs7sha1,
+            AdbePkcs7detached,
+            EtsiCAdESdetached
+        };
+
 	/**
 	   The validation options of this signature.
 	*/
@@ -458,12 +513,15 @@ namespace Poppler {
 
 	FormType type() const override;
 
+        SignatureType signatureType() const;
+
 	/**
 	  Validate the signature.
 
 	  Reset signature validatation info of scoped instance.
 	 */
 	SignatureValidationInfo validate(ValidateOptions opt) const;
+	SignatureValidationInfo validate(int opt, const QDateTime& validationTime) const;
 
 	private:
 	Q_DISABLE_COPY(FormFieldSignature)
diff --git a/utils/pdfsig.1 b/utils/pdfsig.1
index 8029ff0..99ca056 100644
--- a/utils/pdfsig.1
+++ b/utils/pdfsig.1
@@ -9,7 +9,11 @@ pdfsig \- Portable Document Format (PDF) digital signatures tool
 .SH DESCRIPTION
 .B pdfsig
 verifies the digital signatures in a PDF document.
-It also displays the identity of each signer (commonName field of the signer certificate) and the time and date of the signature.
+It also displays the identity of each signer
+(commonName field and full distinguished name of the signer certificate),
+the time and date of the signature, the hash algorithm used for signing,
+the type of the signature as stated in the PDF and
+the signed ranges with a statement wether the total document is signed.
 .PP
 The signer certificate validation uses the trusted certificates stored in the following locations:
 .IP \(bu
diff --git a/utils/pdfsig.cc b/utils/pdfsig.cc
index 2190fea..88c94f4 100644
--- a/utils/pdfsig.cc
+++ b/utils/pdfsig.cc
@@ -8,6 +8,7 @@
 // Copyright 2015 André Esser <bepandre@hotmail.com>
 // Copyright 2015 Albert Astals Cid <aacid@kde.org>
 // Copyright 2016 Markus Kilås <digital@markuspage.com>
+// Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
 //
 //========================================================================
 
@@ -18,6 +19,7 @@
 #include <stddef.h>
 #include <string.h>
 #include <time.h>
+#include "goo/GooList.h"
 #include "parseargs.h"
 #include "Object.h"
 #include "Array.h"
@@ -29,6 +31,19 @@
 #include "SignatureInfo.h"
 
 
+enum HASH_HashType
+{
+    HASH_AlgNULL = 0,
+    HASH_AlgMD2 = 1,
+    HASH_AlgMD5 = 2,
+    HASH_AlgSHA1 = 3,
+    HASH_AlgSHA256 = 4,
+    HASH_AlgSHA384 = 5,
+    HASH_AlgSHA512 = 6,
+    HASH_AlgSHA224 = 7,
+    HASH_AlgTOTAL
+};
+
 const char * getReadableSigState(SignatureValidationStatus sig_vs)
 {
   switch(sig_vs) {
@@ -157,7 +172,77 @@ int main(int argc, char *argv[])
     sig_info = sig_widgets.at(i)->validateSignature(!dontVerifyCert, false);
     printf("Signature #%u:\n", i+1);
     printf("  - Signer Certificate Common Name: %s\n", sig_info->getSignerName());
+    printf("  - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN());
     printf("  - Signing Time: %s\n", time_str = getReadableTime(sig_info->getSigningTime()));
+    printf("  - Signing Hash Algorithm: ");
+    switch (sig_info->getHashAlgorithm())
+    {
+      case HASH_AlgMD2:
+        printf("MD2\n");
+        break;
+      case HASH_AlgMD5:
+        printf("MD5\n");
+        break;
+      case HASH_AlgSHA1:
+        printf("SHA1\n");
+        break;
+      case HASH_AlgSHA256:
+        printf("SHA-256\n");
+        break;
+      case HASH_AlgSHA384:
+        printf("SHA-384\n");
+        break;
+      case HASH_AlgSHA512:
+        printf("SHA-512\n");
+        break;
+      case HASH_AlgSHA224:
+        printf("SHA-224\n");
+        break;
+      default:
+        printf("unknown\n");
+    }
+    printf("  - Signature Type: ");
+    switch (sig_widgets.at(i)->signatureType())
+    {
+      case adbe_pkcs7_sha1:
+        printf("adbe.pkcs7.sha1\n");
+        break;
+      case adbe_pkcs7_detached:
+        printf("adbe.pkcs7.detached\n");
+        break;
+      case ETSI_CAdES_detached:
+        printf("ETSI.CAdES.detached\n");
+        break;
+      default:
+        printf("unknown\n");
+    }
+    GooList* range_list = sig_widgets.at(i)->getSignedRangeBounds();
+    if (range_list)
+    {
+      long long bound = 0;
+      printf("  - Signed Ranges: ");
+      for (int i = 0; i < range_list->getLength(); ++i)
+      {
+        bound = *static_cast<long long*>(range_list->get(i));
+        if (i%2 == 0)
+        {
+          if (i > 0)
+            printf(", ");
+          printf("[%lld - ", bound);
+        }
+        else
+          printf("%lld]", bound);
+      }
+      deleteGooList(range_list, Goffset);
+      GooString* signature = sig_widgets.at(i)->getCheckedSignature();
+      if (signature && sig_widgets.at(i)->getCheckedFileSize() == bound)
+      {
+        printf("\n  - Total document signed\n");
+        delete signature;
+      }
+      else
+        printf("\n  - Not total document signed\n");
+    }
     printf("  - Signature Validation: %s\n", getReadableSigState(sig_info->getSignatureValStatus()));
     gfree(time_str);
     if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) {
-- 
1.9.1