node.jssslsoapws-security

SOAP request in node.js with two securities: TLS and WSS


I am trying to create SOAP client and send a request to external server. This is the code:

const options = {
  endpoint: endpoint,
  pfx: fs.readFileSync(
    "[cert-tls.p12]"
  ),
  passphrase: "[password]",
};
soap.createClient(url, options, function (err, client) {
  if (err) {
    console.log("Error Occurred!!");
    console.log(err);
  } else {
    var WssSecurity = new soap.WSSecurityCert(
      fs.readFileSync(
        "[privateKey.pem]"
      ),
      fs.readFileSync(
        "[publickey.pem]"
      ),
      "[password]"
    );
    client.setSecurity(WssSecurity);
    client.ObslugaRecepty.ObslugaReceptyWS.zapisPakietuRecept(
      recepta_podpisana,
      function (err, result) {
        if (err) {
          console.log("Error Occurred!!");
          console.log(err);
        }
      }
    );
  }
});

I need to add two different securities to request: SSL Security and WS Security. If I will run the code which i provide here, I am getting error:

Error: write EPROTO 6512:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:c:\ws\deps\openssl\openssl\ssl\record\rec_layer_s3.c:1546:SSL alert number 40

So the first thing is, my client doesn't apply pfx cert which is in options. To get rid of this problem I need to add this inside the soap.createClient(){}:

    var tlsSecurity = new soap.ClientSSLSecurityPFX(
      fs.readFileSync(
        "[cert-tls.p12]"
      ),
      "[password]"
    );
    client.setSecurity(tlsSecurity);

And I see these values in response error message which wasn't there before adding client.setSecurity(tlsSecurity):

      transitional: [Object],
      pfx: <Buffer 30 82 18 51 02 01 03 30 82 18 0b 06 09 2a 86 48 86 f7 0d 01 07 01 a0 82 17 fc 04 82 17 f8 30 82 17 f4 30 82 05 b9 06 09 2a 86 48 86 f7 0d 01 07 01 a0 ... 6179 more bytes>,
      passphrase: '[password]'
    },
    request: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,

But this function is overwriting my WsSecurity and the new error is:

Error: wsse:InvalidSecurity: Missing wsse:Security header in request

Is there any possibility to use client.setSecurity(); with two different certificates? Or provide tls certificate correctly without using client.setSecurity(tlsSecurity) or do something with WsSecurity? Do you have any ideas?


Solution

  • I found a solution for my problem. Here is a security.js file with class which can stack different types of security:

    "use strict";
    exports.__esModule = true;
    var SecurityStack = /** @class */ (function () {
      function SecurityStack() {
        var security = [];
        for (var _i = 0; _i < arguments.length; _i++) {
          security[_i] = arguments[_i];
        }
        this.stack = security;
        var hasPostProcessMethod = this.stack.some(function (s) {
          return s["postProcess"];
        });
        var hasToXmlMethod = this.stack.some(function (s) {
          return s["toXML"];
        });
        if (!hasPostProcessMethod) this.postProcess = undefined;
      }
      SecurityStack.prototype.addOptions = function (options) {
        this.stack.forEach(function (security) {
          if (security["addOptions"]) security.addOptions(options);
        });
      };
      SecurityStack.prototype.toXML = function () {
        var result = "";
        this.stack.forEach(function (security) {
          if (security["toXML"]) result += security.toXML();
        });
        return result;
      };
      SecurityStack.prototype.addHeaders = function (headers) {
        this.stack.forEach(function (security) {
          if (security["addHeaders"]) security.addHeaders(headers);
        });
      };
      SecurityStack.prototype.postProcess = function (xml, envelopeKey) {
        var result = xml;
        this.stack.forEach(function (security) {
          if (security["postProcess"])
            result = security.postProcess(xml, envelopeKey);
        });
        return result;
      };
      return SecurityStack;
    })();
    
    module.exports = SecurityStack;
    

    and the usage in soap.createClient():

    const securityStack = new SecurityStack(tlsSecurity, WssSecurity);
    client.setSecurity(securityStack);