diff --git a/javascript/src/iframe/viamapi-iframe.js b/javascript/src/iframe/viamapi-iframe.js
index aec4b16e3ef7deb60d5d35927e56c967b261bc4b..1df7b53b1e79009ecb58efda7ad7f7f4ca658f41 100644
--- a/javascript/src/iframe/viamapi-iframe.js
+++ b/javascript/src/iframe/viamapi-iframe.js
@@ -20,10 +20,11 @@ import {
 } from "../utilities/appUtility";
 import { LOGIN_MODES } from "../constants/authentication";
 import {
+  CertificateData,
   createOneTimePassportCertificate,
   createPassportCertificate,
   decryptMessage,
-  encryptMessage,
+  encryptMessage, parseCertificate,
   signEmail,
   verifySMIME
 } from "../utilities/signingUtilities";
@@ -1095,6 +1096,43 @@ const connection = Penpal.connectToParent({
 
       return encodeResponse("200", verificationResult.verified, verificationResult.message);
     },
+    validateDocument: async (documentUUID, contentType) => {
+      const authenticationPublicKey = localStorage.getItem("authenticatedIdentity");
+
+      if (
+        !authenticationPublicKey ||
+        !window.loadedIdentities[authenticationPublicKey] ||
+        !extendPinCodeTtl(authenticationPublicKey)
+      ) {
+        return encodeResponse("400", "", "Identity not authenticated");
+      }
+
+      const validateDocumentResponse = await executeRestfulFunction(
+        "private",
+        window.viamApi,
+        window.viamApi.documentValidateDocumentByUUID,
+        null,
+        documentUUID,
+        contentType);
+
+      if (validateDocumentResponse.code !== "200") {
+        return encodeResponse("400", "", validateDocumentResponse.status);
+      }
+
+      const signatures = validateDocumentResponse.data;
+      if (signatures) {
+        for (const signature of signatures) {
+          const certificateChain = signature.certificateChainPEM.map((certificatePEM) => {
+            const certificate = parseCertificate(certificatePEM);
+            const certificateData = new CertificateData(certificate);
+            return certificateData;
+          });
+          signature.certificateChain = certificateChain;
+        }
+      }
+
+      return validateDocumentResponse;
+    },
     signEmail: async (passportUUID, emailArg, emailMessage) => {
       const authenticationPublicKey = localStorage.getItem(
         "authenticatedIdentity"
diff --git a/javascript/src/utilities/signingUtilities.js b/javascript/src/utilities/signingUtilities.js
index bb6027a04133449d4044806027472b419798a70e..3fad4cbc4c74e05a39c3eb93f39a5c16ec5d406e 100644
--- a/javascript/src/utilities/signingUtilities.js
+++ b/javascript/src/utilities/signingUtilities.js
@@ -10,7 +10,7 @@ import {
   isEqualBuffer
 } from "pvutils";
 import { fromBER } from "asn1js";
-import { ContentInfo, SignedData } from "pkijs";
+import { ContentInfo, SignedData, Certificate } from "pkijs";
 import { algomap, rdnmap } from "../constants/certificates";
 import {
   fixNewLines,
@@ -85,6 +85,182 @@ const encryptionAlgorithm = {
   length: 128
 };
 
+// Convert a hex string to a byte array
+function hexStringToBytes(hex) {
+  let bytes, c;
+  if (hex.length % 2 === 1) {
+    hex = "0" + hex;
+  }
+  for (bytes = [], c = 0; c < hex.length; c += 2) {
+    bytes.push(parseInt(hex.substr(c, 2), 16));
+  }
+  return bytes;
+}
+
+export class CertificateData {
+  //**********************************************************************************
+  /**
+   * Constructor for SignedData class
+   * @param {pkijs.Certificate} [certificate]
+   * @param {Object} [parameters]
+   */
+  constructor(parameters = {}) {
+    this.serialNumber = null; //string || ArrayBuffer || Uint8Array
+
+    this.keyPair = null; // write only; {publicKey, privateKey}
+
+    this.signatureAlgorithm = null; //read-only, initialized form pkijs.Certificate object
+
+    this.algorithms = null; // write only; {hashAlg: "SHA-256", signAlg: "RSASSA-PKCS1-v1_5", keyLength: 2048};
+
+    this.issuer = null; //same as subject
+
+    this.subject = {
+      commonName: null, //string
+      country: null, //string
+      locality: null, //string
+      state: null, //string
+      organization: null, //string
+      organizationUnit: null, //string
+      email: null, //string
+      url: null //string
+    };
+
+    this.validity = {
+      notBefore: null, //new Date()
+      notAfter: null, //new Date()
+      validYears: null //int
+    };
+
+    this.isCA = false;
+
+    if (parameters) {
+      if (parameters instanceof Certificate) {
+        this.fromCertificate(parameters);
+      } else {
+        this.fromParameters(parameters);
+      }
+    }
+  }
+
+  fromCertificate(certificate) {
+    this.serialNumber = bufferToHexCodes(
+      certificate.serialNumber.valueBlock.valueHex
+    );
+
+    let signatureAlgorithm =
+      algomap[certificate.signatureAlgorithm.algorithmId];
+    if (typeof signatureAlgorithm === "undefined") {
+      signatureAlgorithm = certificate.signatureAlgorithm.algorithmId;
+    } else {
+      signatureAlgorithm = `${signatureAlgorithm}`;
+    }
+    this.signatureAlgorithm = signatureAlgorithm;
+
+    this.issuer = {};
+    const issuer = certificate.issuer.typesAndValues;
+    for (const typeAndValue of issuer) {
+      let typeVal = rdnmap[typeAndValue.type];
+      if (typeof typeVal === "undefined") {
+        typeVal = typeAndValue.type;
+      }
+      const subjVal = typeAndValue.value.valueBlock.value;
+      this.issuer[typeVal] = subjVal;
+    }
+
+    const subject = certificate.subject.typesAndValues;
+    for (const typeAndValue of subject) {
+      let typeVal = rdnmap[typeAndValue.type];
+      if (typeof typeVal === "undefined") {
+        typeVal = typeAndValue.type;
+      }
+      const subjVal = typeAndValue.value.valueBlock.value;
+      this.subject[typeVal] = subjVal;
+    }
+
+    this.validity.notBefore = certificate.notBefore.value;
+    this.validity.notAfter = certificate.notAfter.value;
+
+    this.isCA = certificate.issuer.isEqual(certificate.subject);
+  }
+
+  fromParameters(parameters) {
+    if ("serialNumber" in parameters) {
+      this.serialNumber = parameters.serialNumber;
+    }
+
+    if ("keyPair" in parameters) {
+      this.keyPair = {};
+      if ("publicKey" in parameters.keyPair) {
+        this.keyPair.publicKey = parameters.keyPair.publicKey;
+      }
+      if ("privateKey" in parameters.keyPair) {
+        this.keyPair.privateKey = parameters.keyPair.privateKey;
+      }
+    }
+
+    if ("signatureAlgorithm" in parameters) {
+      this.signatureAlgorithm = parameters.signatureAlgorithm;
+    }
+
+    if ("algorithms" in parameters) {
+      this.algorithms = {};
+      if ("hashAlg" in parameters.algorithms) {
+        this.algorithms.hashAlg = parameters.algorithms.hashAlg;
+      }
+      if ("signAlg" in parameters.algorithms) {
+        this.algorithms.signAlg = parameters.algorithms.signAlg;
+      }
+      if ("keyLength" in parameters.algorithms) {
+        this.algorithms.keyLength = parameters.algorithms.keyLength;
+      }
+    }
+
+    if ("subject" in parameters) {
+      if ("commonName" in parameters.subject) {
+        this.subject.commonName = parameters.subject.commonName;
+      }
+      if ("country" in parameters.subject) {
+        this.subject.country = parameters.subject.country;
+      }
+      if ("locality" in parameters.subject) {
+        this.subject.locality = parameters.subject.locality;
+      }
+      if ("state" in parameters.subject) {
+        this.subject.state = parameters.subject.state;
+      }
+      if ("organization" in parameters.subject) {
+        this.subject.organization = parameters.subject.organization;
+      }
+      if ("organizationUnit" in parameters.subject) {
+        this.subject.organizationUnit = parameters.subject.organizationUnit;
+      }
+      if ("email" in parameters.subject) {
+        this.subject.email = parameters.subject.email;
+      }
+      if ("url" in parameters.subject) {
+        this.subject.url = parameters.subject.url;
+      }
+    }
+
+    if ("validity" in parameters) {
+      if ("notBefore" in parameters.validity) {
+        this.validity.notBefore = parameters.validity.notBefore;
+      }
+      if ("notAfter" in parameters.validity) {
+        this.validity.notAfter = parameters.validity.notAfter;
+      }
+      if ("validYears" in parameters.validity) {
+        this.validity.validYears = parameters.validity.validYears;
+      }
+    }
+
+    if ("isCA" in parameters) {
+      this.isCA = parameters.isCA;
+    }
+  }
+}
+
 //*********************************************************************************
 // Returns promise, resolved to keyPair object {publicKey, privateKey}
 //*********************************************************************************
@@ -146,14 +322,18 @@ function fixPkijsRDN() {
 //*********************************************************************************
 function createCertificate(certData, issuerData = null) {
 
-  if (typeof certData === "undefined") {
+  if (typeof certData === "undefined" || certData === null) {
     return Promise.reject("No Certificate data provided");
   }
 
-  if (typeof certData.subject === "undefined") {
+  if (typeof certData.subject === "undefined" || certData.subject === null) {
     return Promise.reject("No Certificate subject data provided");
   }
 
+  if (typeof certData.subject.commonName === "undefined" || certData.subject.commonName === null) {
+    return Promise.reject("No Certificate common name provided");
+  }
+
   //region Get a "crypto" extension
   const crypto = pkijs.getCrypto();
 
@@ -219,14 +399,40 @@ function createCertificate(certData, issuerData = null) {
     //region Put a static values
     certificate.version = CERTIFIATE_Version_3;
 
-    const serialNumberBuffer = new ArrayBuffer(20);
-    const serialNumberView = new Uint8Array(serialNumberBuffer);
-    pkijs.getRandomValues(serialNumberView);
-    serialNumberView[0] &= 0x7f;
-    // noinspection JSUnresolvedFunction
-    certificate.serialNumber = new asn1js.Integer({
-      valueHex: serialNumberView
-    });
+    certificate.serialNumber = null;
+    if (certData.serialNumber) {
+      let serialNumberView = null;
+
+      if (certData.serialNumber instanceof Buffer) {
+        serialNumberView = new Uint8Array(certData.serialNumber);
+      } else if (certData.serialNumber instanceof ArrayBuffer) {
+        serialNumberView = new Uint8Array(certData.serialNumber);
+      } else if (certData.serialNumber instanceof Uint8Array) {
+        serialNumberView = certData.serialNumber;
+      } else if (certData.serialNumber instanceof String) {
+        try {
+          serialNumberView = new Uint8Array(hexStringToBytes(certData.serialNumber));
+        } catch (ignore) {
+          serialNumberView = null;
+        }
+      }
+      if (serialNumberView !== null) {
+        certificate.serialNumber = new asn1js.Integer({
+          valueHex: serialNumberView
+        });
+      }
+    }
+
+    if (certificate.serialNumber === null) {
+      const serialNumberBuffer = new ArrayBuffer(20);
+      const serialNumberView = new Uint8Array(serialNumberBuffer);
+      pkijs.getRandomValues(serialNumberView);
+      serialNumberView[0] &= 0x7f;
+      // noinspection JSUnresolvedFunction
+      certificate.serialNumber = new asn1js.Integer({
+        valueHex: serialNumberView
+      });
+    }
     //endregion Put a static values
 
     //region Subject
@@ -680,7 +886,7 @@ export function parsePrivateKey(privateKeyPEM) {
 }
 
 export function createPassportCertificate(commonNameArg) {
-  const certData = {
+  const certDataParams = {
     algorithms: {
       hashAlg: "SHA-256",
       signAlg: "RSASSA-PKCS1-v1_5",
@@ -705,7 +911,9 @@ export function createPassportCertificate(commonNameArg) {
     isCA: true // optional flag denoting if this is CA certificate or leaf certificate, defaults to false
   };
 
-  return createCertificate(certData, null);
+  const certificateData = new CertificateData(certDataParams);
+
+  return createCertificate(certificateData, null);
 }
 
 export function createOneTimePassportCertificate(
@@ -1066,48 +1274,8 @@ export const parseSignedData = signatureBase64 => {
 
 export const parseCertificates = signedData => {
   try {
-    return signedData.certificates.map((certificate, index) => {
-      const certificateData = { issuer: {}, subject: {}, validity: {} };
-      const serialNumber = bufferToHexCodes(
-        certificate.serialNumber.valueBlock.valueHex
-      );
-      const issuer = certificate.issuer.typesAndValues;
-      const subject = certificate.subject.typesAndValues;
-      const notAfter = certificate.notAfter.value;
-      const notBefore = certificate.notBefore.value;
-      let signatureAlgorithm =
-        algomap[certificate.signatureAlgorithm.algorithmId];
-      if (typeof signatureAlgorithm === "undefined") {
-        signatureAlgorithm = certificate.signatureAlgorithm.algorithmId;
-      } else {
-        signatureAlgorithm = `${signatureAlgorithm}`;
-      }
-
-      for (const typeAndValue of issuer) {
-        let typeVal = rdnmap[typeAndValue.type];
-        if (typeof typeVal === "undefined") {
-          typeVal = typeAndValue.type;
-        }
-        const subjVal = typeAndValue.value.valueBlock.value;
-        certificateData.issuer[typeVal] = subjVal;
-      }
-
-      for (const typeAndValue of subject) {
-        let typeVal = rdnmap[typeAndValue.type];
-        if (typeof typeVal === "undefined") {
-          typeVal = typeAndValue.type;
-        }
-        const subjVal = typeAndValue.value.valueBlock.value;
-        certificateData.subject[typeVal] = subjVal;
-      }
-
-      certificateData.signatureAlgorithm = signatureAlgorithm;
-      certificateData.serialNumber = serialNumber;
-      certificateData.validity = {
-        notAfter,
-        notBefore
-      };
-
+    return signedData.certificates.map((certificate) => {
+      const certificateData = new CertificateData(certificate);
       return certificateData;
     });
   } catch (e) {