title: Web signature with X509 certificate + smartcard
body: <p>I'm trying to add signature functionality to my web app.</p>
  <p>The signature must be made with a smartcard. So, after searching in a lot of places I found <em>webcrypto-socket</em> (<a href="https://peculiarventures.github.io/webcrypto-local/docs/" rel="nofollow noreferrer">https://peculiarventures.github.io/webcrypto-local/docs/</a>) - Webcrypto socket module implements Crypto interface and uses <em>Fortify</em> application for crypto implementation.</p>
  <p>Then, I could sign in to my app but the signature that it produces was not a valid PKCS signature, it was returning only the signature hash without the public certificate.</p>
  <p>My code is based on this example: <a href="https://peculiarventures.github.io/fortify-examples/example5.html" rel="nofollow noreferrer">https://peculiarventures.github.io/fortify-examples/example5.html</a></p>
  <p>In addition, comments for webcrypto-socket and fortify are welcome!</p>
  <p>I'm doing something like this, where providers and certificates are filled as fortify recommends:</p>
  <pre><code>function sign() {
      var provider;
      var cert;
      Promise.resolve()
          .then(function () {
              var providerID = document.getElementById(&quot;providers&quot;).value;
              return ws.getCrypto(providerID)
          })
          .then(function (crypto) {
              provider = crypto;
  </code></pre>
  <blockquote>
  <pre><code>          setEngine('subtle', crypto, 
  </code></pre>
  </blockquote>
  <pre><code>    new CryptoEngine({ name: 'subtle', crypto: crypto, subtle: crypto.subtle }));
  
              return GetCertificate(provider,
                  document.getElementById(&quot;certificates&quot;).value);
          })
          .then(function (certificate) {
              cert = certificate;
              return GetCertificateKey(&quot;private&quot;, provider,
                  document.getElementById(&quot;certificates&quot;).value);
          })
          .then(function (key) {
              if (!key) {
                  throw new Error(&quot;Certificate doesn't have private key&quot;);
              }
  
              var textToSignElement = document.getElementById(&quot;textToSign&quot;);
              var signatureResultElement = 
                  document.getElementById(&quot;signatureResult&quot;);
              signDataElement(textToSignElement, signatureResultElement, 
                  cert, key, provider);
  
          })
  }
  
  function signDataElement(textToSignElement, signatureResultElement, 
      key, cert, provider) {
      var message = pvtsutils.Convert.FromBase64(hashElement.value);
      var hashAlg = key.algorithm.hash.name;
      var sequence = Promise.resolve();
      sequence = sequence.then(() =&gt; provider.subtle.digest({ name: hashAlg },
          new Uint8Array(message)));
      var cmsSignedSimpl = null;
      var messHex = null;
      var certRaw = null;
  
      sequence = sequence.then(function (res) {
          messHex = res;
          return GetCertificateAsPEM(provider, cert);
      });
  
      //region Combine all signed extensions
      sequence = sequence.then(result =&gt; {
          certRaw = result;
          var signedAttr = [];
  
          signedAttr.push(new Attribute({
              type: &quot;1.2.840.113549.1.9.3&quot;,
              values: [new ObjectIdentifier({ value: &quot;1.2.840.113549.1.7.1&quot; })]
          })); // contentType
  
          signedAttr.push(new Attribute({
              type: &quot;1.2.840.113549.1.9.5&quot;,
              values: [new UTCTime({ valueDate: new Date() })]
          })); // signingTime
  
          signedAttr.push(new Attribute({
              type: &quot;1.2.840.113549.1.9.4&quot;,
              values: [new OctetString({ valueHex: messHex})]
          })); // messageDigest
  
          return signedAttr;
      });
  
      sequence = sequence.then(signedAttr =&gt; {
          var asn1 = fromBER(PemToDer(certRaw));
          var newCert = new Certificate({ schema: asn1.result });
  
          newCert.issuer.typesAndValues.push(new AttributeTypeAndValue({
              type: &quot;2.5.4.3&quot;, // Common name
              value: new BmpString({ value: cert.issuerName })
          }));
          cmsSignedSimpl = new SignedData({
              version: 1,
              encapContentInfo: new EncapsulatedContentInfo({
                  eContentType: &quot;1.2.840.113549.1.7.1&quot; // &quot;data&quot; content type
              }),
              signerInfos: [new SignerInfo({
                  version: 1,
                  sid: new IssuerAndSerialNumber({
                      issuer: newCert.issuer,
                      serialNumber: newCert.serialNumber
                  }),
                  signedAttrs: new SignedAndUnsignedAttributes({
                      type: 0,
                      attributes: signedAttr
                  })
              })],
              certificates: [newCert]
          });
          return cmsSignedSimpl.sign(key, 0, hashAlg, message)
      });
  
      sequence = sequence.then(() =&gt; {
          var cmsSignedSchema = cmsSignedSimpl.toSchema(true);
  
          var cmsContentSimp = new ContentInfo({
              contentType: &quot;1.2.840.113549.1.7.2&quot;,
              content: cmsSignedSchema
          });
  
          var _cmsSignedSchema = cmsContentSimp.toSchema();
  
          //region Make length of some elements in &quot;indefinite form&quot;
          _cmsSignedSchema.lenBlock.isIndefiniteForm = true;
  
          var block1 = _cmsSignedSchema.valueBlock.value[1];
          block1.lenBlock.isIndefiniteForm = true;
  
          var block2 = block1.valueBlock.value[0];
          block2.lenBlock.isIndefiniteForm = true;
  
          //endregion
  
         return _cmsSignedSchema.toBER(false);
      }, error =&gt; Promise.reject(`Erorr during signing of CMS Signed Data: ${error}`));
  
      sequence.then((certificateBuffer) =&gt; {
          var certSimplString = String.fromCharCode.apply(null, 
              new Uint8Array(certificateBuffer));
  
          signatureElement.value = formatPEM(window.btoa(certSimplString));
  
          alert(&quot;Certificate created successfully!&quot;);
      })
  
      return sequence;
  }
  
  function GetCertificate(provider, certID) {
      var certID;
      return provider.certStorage.getItem(certID)
          .then(function (cert) {
              return cert;
          });
  }
  
  function GetCertificateAsPEM(provider, cert) {
      return provider.certStorage.exportCert('PEM', cert);
  }
  </code></pre>
  <p>The <em>signDataElement</em> is based on <a href="https://pkijs.org/examples/CMSSigned_complex_example.html" rel="nofollow noreferrer">https://pkijs.org/examples/CMSSigned_complex_example.html</a></p>