Skip to content
Snippets Groups Projects
signingUtilities.js 37 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
      return string.charAt(0).toUpperCase() + string.slice(1);
    }
    
    function capitalizeHeader(string) {
      let result = "";
      const tokens = string.split("-");
      for (let i = 0; i < tokens.length; i++) {
        result += capitalizeFirstLetter(tokens[i]);
        if (i !== tokens.length - 1) {
          result += "-";
        }
      }
    
      return result;
    }
    
    function makeBoundary() {
      let len = 20 + Math.random() * 20;
    
    Alexey Lunin's avatar
    Alexey Lunin committed
      return "W0RyLiBEYW15YW4gTWl0ZXZd--" + makeid(len);
    
    Damyan Mitev's avatar
    Damyan Mitev committed
    function hexString(buffer) { //TODO remove
      const byteArray = new Uint8Array(buffer);
    
      const hexCodes = [...byteArray].map(value => {
        const hexCode = value.toString(16);
        const paddedHexCode = hexCode.padStart(2, '0');
        return paddedHexCode;
      });
    
      return hexCodes.join('');
    }
    
    export const parseSignedData = signatureBase64 => {
    
      try {
        const certificateDecoded = atob(signatureBase64);
        const buffer = stringToArrayBuffer(certificateDecoded);
        const asn1 = fromBER(buffer);
    
        const contentInfo = new ContentInfo({ schema: asn1.result });
        const signedData = new SignedData({ schema: contentInfo.content });
    
    Damyan Mitev's avatar
    Damyan Mitev committed
        return signedData;
      } catch (e) {
        console.error("Error parsing signed data", e);
        return null; //TODO implement proper error handling
      }
    };
    
    export const parseCertificates = signedData => {
      try {
        //TODO remove block
        console.log("ParseCerts", signedData.signerInfos);
        console.log("DigestAttrib",signedData.signerInfos[0].signedAttrs.attributes[2]);
        const digest = signedData.signerInfos[0].signedAttrs.attributes[2].values[0].valueBlock.valueHex;
        const digestStr = hexString(digest);
        console.log("DigestAttrib", digestStr);
    
    
        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 certificateData;
        });
      } catch (e) {
        console.error("Error parsing certificate", e);
      }
    };
    
    
    Damyan Mitev's avatar
    Damyan Mitev committed
    export const getCertificateChain = signedData => {
    
      const certificateChain = [];
    
      try {
    
    Damyan Mitev's avatar
    Damyan Mitev committed
        const certificates = parseCertificates(signedData);
    
    
        // Add first certificate in the chain
        certificateChain.push(certificates[0]);
    
        // Go through all certificates to build a chain from first certificate to the root
        certificates.forEach(certificate => {
    
    Alexey Lunin's avatar
    Alexey Lunin committed
          if (
            certificateChain[0].issuer.commonName === certificate.subject.commonName
          ) {
    
            certificateChain.unshift(certificate);
          }
        });
      } catch (e) {
        console.warn("Error getting certificate data", e);
      }
    
      return certificateChain;
    };
    
    Damyan Mitev's avatar
    Damyan Mitev committed
    
    export const verifySMIME = (smimeString, rootCaPem) => {
      return new Promise(resolve => {
        setTimeout(async () => {
          const emailString = fixNewLines(smimeString);
          const parts = parseMIME(emailString);
          //const rawAttachments = getAttachments(emailString, parts);
    
          //const attachments = [];
          let signatureBase64;
          let signatureBoundary;
    
          for (const part of parts) {
            let contentType = getHeaderValue("content-type", part);
            if (contentType === null || contentType === undefined) {
              continue;
            }
            contentType = contentType[0];
    
            if (contentType.startsWith(SIGNATURE_CONTENT_TYPE)) {
              signatureBase64 = getAttachment(emailString, part).base64;
              signatureBoundary = part.boundary;
              break;
            }
          }
    
          let verificationResult = false;
          let signedData;
    
          if (signatureBase64) {
            const dataPart = parts[0];
            if (dataPart.boundary !== signatureBoundary) {
              throw new Error(`Invalid SMIME format: wrong boundary on first MIME part`);
            }
    
            const data = emailString.slice(
              dataPart.indices.from,
              dataPart.indices.to
            );
            const dataBuffer = stringToArrayBuffer(data);
    
            const rootCa = parseCertificate(rootCaPem);
    
            signedData = parseSignedData(signatureBase64);
            for (let i = 0; i < signedData.signerInfos.length; i++) {
              console.log(`Validating message for signer ${i}`);
              const result = await signedData.verify({
                signer: i,
                data: dataBuffer,
                trustedCerts: [rootCa],
                checkDate: new Date(),
                checkChain: true,
                extendedMode: true,
                passedWhenNotRevValues: false
              });
              console.log(`Result for signer ${i}:`,result);
              if (!result) {
                verificationResult = false;
                resolve(verificationResult);
                return;
              }
            }
          }
    
          verificationResult = true;
          resolve(verificationResult);
        }, 50);
      });
    };