I am trying to call a web service that requires authetication via X509 certificate. I'm using Node.js and node-soap module, getting successfully the WSDL.
But when I try to build the SOAP envelope with the Signature tag inside, I get the following error from the server:
The value of the attribute "prefix="xmlns",localpart="wsse",rawname="xmlns:wsse"" is invalid. Prefixed namespace bindings may not be empty.
I've tried to modify the existingPrefixes that are passed to the underlying library (xml-crypto).
This is my code:
var privateKey = fs.readFileSync(process.env.KEYFILE);
var publicKey = fs.readFileSync(process.env.CERTFILE);
var password = ''; // optional password
var options = {
mustUnderstand: 1,
signerOptions: {
existingPrefixes: {
'wsse': "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
}
},
};
var wsSecurity = new soap.WSSecurityCert(privateKey, publicKey, password, options);
const endpoint = process.env.ENDPOINT;
soap.createClientAsync(endpoint).then(client => {
client.setSecurity(wsSecurity);
return client.wiOperationAsync({ ... })
})
However, the error persists and I get a SOAP request body with something like this:
<KeyInfo>
<wsse:SecurityTokenReference
xmlns:wsse="">
<wsse:Reference URI="#x509-1639ca80191641b9bd804497cfcef0b5" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</KeyInfo>
Any idea to avoid that empty mlns:wsse attribute?
Problem
We are seeing this error returned from the DocumentDB REST API whenever we request a list or query, but not when we fetch objects by name/id:
The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used.
Background
We have been successfully using the node.js sdk with DocumentDB for over a year now, but as we want to migrate our back-end restful API code from a node.js App Service to Azure Functions we are seeing 10-30 second lag times come into play as the DocumentDB sdk loads slowly when the Function hasn't been called in a while. We know that the Function instance is hot, and this isn't a cold instance issue based on previous communication with the Azure Functions team.
To work around this we want to test the DocumentDB REST API which requires zero external libraries to run in a node.js Function and should execute as quickly as possible.
Code
This is the test harness running in local node.js. We'll move this to an Azure Function once it's working.
var express = require('express');
var router = express.Router();
var crypto = require("crypto");
var request = require('request');
router.get('/', function (req, res, next) {
var key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
var uri = "https://xxxxxx.documents.azure.com";
var verb = 'GET';
var type = 'dbs';
var link = 'dbs';
var url = `${uri}/${link}`;
var headers = getDefaultRequestHeaders();
// var body = `{"query":"SELECT * FROM c", "parameters": []}`;
var body = '';
headers['content-length'] = body.length;
headers['authorization'] = getAuthorizationTokenUsingMasterKey(verb, type, link, headers['x-ms-date'], key);
request[verb.toLowerCase()]({"url": url, "headers": headers, "body": body}, function (error, response, body) {
// console.log(`error is ${error}`);
// console.log(`response is ${JSON.stringify(response, null, 2)}`);
console.log(`body is ${body}`);
res.status(response.statusCode).json(body);
});
});
function getDefaultRequestHeaders(isQuery, date) {
var headers = {
"content-type": "application/json",
"x-ms-date": new Date().toUTCString(),
"x-ms-version": "2017-02-22",
"accept": "application/json",
"cache-control": "no-cache",
"user-agent": "xxxxxx/1.0"
};
if(isQuery) {
headers["x-ms-documentdb-isquery"] = true;
headers["content-type"] = "application/query+json";
}
if(date) {
headers["x-ms-date"] = date;
}
return headers;
}
function getAuthorizationTokenUsingMasterKey(verb, resourceType, resourceLink, date, masterKey) {
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceLink || "") + "\n" +
date.toLowerCase() + "\n" +
"" + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
module.exports = router;
We are using the getAuthorizationTokenFromMasterKey function verbatim from the Access control in the DocumentDB API page.
The key, app name, and user-agent have been replaced with x's for privacy/security.
Test Results
List Databases
When I try the most basic call to list dbs the server returns the token error:
var verb = 'GET';
var type = 'dbs';
var link = 'dbs';
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndbs\n\nsat, 12 aug 2017 12:28:41 gmt\n\n'\r\nActivityId: acbf19d9-6485-45c5-9c30-6aa21f14d5b3\"}"
Get Database
However, when I perform the get database request it works fine:
var verb = 'GET';
var type = 'dbs';
var link = 'dbs/00001';
Response:
"{\"id\":\"00001\",\"_rid\":\"0eUiAA==\",\"_ts\":1441256154,\"_self\":\"dbs\/0eUiAA==\/\",\"_etag\":\"\\"00007d4a-0000-0000-0000-55e7d2da0000\\"\",\"_colls\":\"colls\/\",\"_users\":\"users\/\"}"
List Collections
Similarly, requesting the list of collections from this database returns a token error:
var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls';
Respose:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ncolls\ndbs/00001\nsat, 12 aug 2017 12:32:19 gmt\n\n'\r\nActivityId: 8a9d4ff8-24ef-4fd2-b400-f9f8aa743572\"}"
Get Collection
But when I call get collection I get a valid response:
var verb = 'GET';
var type = 'colls';
var link = 'dbs/00001/colls/00001';
Response:
"{\"id\":\"00001\",\"indexingPolicy\":{\"indexingMode\":\"consistent\",\"automatic\":true,\"includedPaths\":[{\"path\":\"\/*\",\"indexes\":[{\"kind\":\"Range\",\"dataType\":\"Number\",\"precision\":-1},{\"kind\":\"Range\",\"dataType\":\"String\",\"precision\":-1},{\"kind\":\"Spatial\",\"dataType\":\"Point\"}]}],\"excludedPaths\":[]},\"_rid\":\"0eUiAJMAdQA=\",\"_ts\":1454200014,\"_self\":\"dbs\/0eUiAA==\/colls\/0eUiAJMAdQA=\/\",\"_etag\":\"\\"00000100-0000-0000-0000-56ad54ce0000\\"\",\"_docs\":\"docs\/\",\"_sprocs\":\"sprocs\/\",\"_triggers\":\"triggers\/\",\"_udfs\":\"udfs\/\",\"_conflicts\":\"conflicts\/\"}"
List Documents
Requesting list documents on that collection gives me this error:
var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndocs\ndbs/00001/colls/00001\nsat, 12 aug 2017 12:34:48 gmt\n\n'\r\nActivityId: 57097e95-c41b-4770-b91a-370418ef2cce\"}"
Get Document
Not surprisingly, fetching a single document works fine:
var verb = 'GET';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs/e7fe638d-2152-2097-f9c6-9801d7cf5cdd';
Response:
"{\"name\":\"test rest api\",\"id\":\"e7fe638d-2152-2097-f9c6-9801d7cf5cdd\",\"_rid\":\"0eUiAJMAdQCbHgAAAAAAAA==\",\"_self\":\"dbs\/0eUiAA==\/colls\/0eUiAJMAdQA=\/docs\/0eUiAJMAdQCbHgAAAAAAAA==\/\",\"_etag\":\"\\"0d00d1ee-0000-0000-0000-598ef7d40000\\"\",\"_attachments\":\"attachments\/\",\"_ts\":1502541779}"
Query Documents
Finally, sending a query also results in a token error:
var verb = 'POST';
var type = 'docs';
var link = 'dbs/00001/colls/00001/docs';
var body = `{"query":"SELECT * FROM c", "parameters": []}`;
Response:
"{\"code\":\"Unauthorized\",\"message\":\"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'post\ndocs\ndbs/00001/colls/00001\nsat, 12 aug 2017 12:35:42 gmt\n\n'\r\nActivityId: b8b95f8c-1339-423e-b0e7-0d15d3056180\"}"
I believe the documentation is incorrect. Where they say resourceLink, they should actually say resource id. If you look at the Node SDK code, this is how they are calculating the authorization header (notice the use of resourceId):
getAuthorizationTokenUsingMasterKey: function (verb, resourceId, resourceType, headers, masterKey) {
var key = new Buffer(masterKey, "base64");
var text = (verb || "").toLowerCase() + "\n" +
(resourceType || "").toLowerCase() + "\n" +
(resourceId || "") + "\n" +
(headers["x-ms-date"] || "").toLowerCase() + "\n" +
(headers["date"] || "").toLowerCase() + "\n";
var body = new Buffer(text, "utf8");
var signature = crypto.createHmac("sha256", key).update(body).digest("base64");
var MasterToken = "master";
var TokenVersion = "1.0";
return "type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature;
},
So if you want to list the databases, because there is no resource id you will need to use an empty string for your link variable. Similarly, if you want to list collections in the database, the link should actually be the id of the database (e.g. dbs/00001 and not dbs/00001/colls).
I was getting the same issue. For querying documents I was getting authorization token error. It was due to wrong ResourceId/ResourceLink
var verb = 'POST';
var type = 'docs';
var link = 'dbs/{db-id}/colls/{coll-id}/docs';
var url = `${uri}/${link}`;
var resourceLink = "dbs/{db-id}/colls/{coll-id}"
getAuthorizationTokenUsingMasterKey(verb, type, resourceLink, headers['x-ms-date'], key)
the only correction is required from the given question data is to change the appropriate resourceLink while generating AuthorizationToken. For querying Documents the resourceLink is <dbs/{db-id}/colls/{coll-id}> instead of <dbs/{db-id}/colls/{coll-id}/docs>
I want to provide another thing to consider in addressing this issue. In my case I had to add this header: x-ms-documentdb-query-enablecrosspartition: true, because I created my container with a partitionKey.
I also want to confirm how my main parameters were setup to calculate the authorization header for querying over Documents:
resourceType: docs
resourceLink: dbs/<databaseId>/colls/<containerId>
I had thought, up to this point, that the resourceLInk had to match with the request URL but this is showing me I was wrong. Similarly notice that the resourceType is not present in the resourceLink.
POST /dbs/<databaseId>/colls/<containerId>/docs HTTP/1.1
accept: application/json
x-ms-documentdb-isquery: true
x-ms-version: 2018-12-31
authorization: type%3Dmaster%26ver%3D1.0%26sig%***********************
x-ms-date: Sat, 03 Apr 2021 22:34:24 GMT
x-ms-documentdb-query-enablecrosspartition: true
x-correlation-id: be1b1fe1-94cc-11eb-a0a4-38f9d3924940
Host: <host>.documents.azure.com
User-Agent: AHC/1.0
Connection: keep-alive
Content-Type: application/query+json
Content-Length: 72
{
"query": "SELECT * FROM <containerId>",
"parameters": [
]
}
I got the same error while making an update to document DB but in my case I realized I was using the Read-Only keys. After changing connection string to use it to Read-Write Keys, I was able to update the records.
I'm building a simple, STARTTLS capable POP3 Proxy in Node.JS and I'm having quite a hard time.
The proxy serves as a front-end for many back-end servers, so it must load their certificates dynamically, depending on the Client's connection.
I'm trying to use the SNICallback, which brings me the servername the client uses, but I can't set the right certificate after this, because I need one certificate before I have this call, when I create the secure context.
The code is as bellow:
// Load libraries
var net = require('net');
var tls = require('tls');
var fs = require('fs');
// Load certificates (created with openssl)
var certs = [];
for (var i = 1; i <= 8; i++) {
var hostName = 'localhost' + i;
certs[hostName] = {
key : fs.readFileSync('./private-key.pem'),
cert : fs.readFileSync('./public-cert' + i + '.pem'),
}
}
var server = net.createServer(function(socket) {
socket.write('+OK localhost POP3 Proxy Ready\r\n');
socket.on('data', function(data) {
if (data == "STLS\r\n") {
socket.write("+OK begin TLS negotiation\r\n");
upgradeSocket(socket);
} else if (data == "QUIT\r\n") {
socket.write("+OK Logging out.\r\n");
socket.end();
} else {
socket.write("-ERR unknown command.\r\n");
}
});
}).listen(10110);
and upgradeSocket() is as follows:
function upgradeSocket(socket) {
// I need this 'details' or handshake will fail with a message:
// SSL routines:ssl3_get_client_hello:no shared cipher
var details = {
key : fs.readFileSync('./private-key.pem'),
cert : fs.readFileSync('./public-cert1.pem'),
}
var options = {
isServer : true,
server : server,
SNICallback : function(serverName) {
return tls.createSecureContext(certs[serverName]);
},
}
sslcontext = tls.createSecureContext(details);
pair = tls.createSecurePair(sslcontext, true, false, false, options);
pair.encrypted.pipe(socket);
socket.pipe(pair.encrypted);
pair.fd = socket.fd;
pair.on("secure", function() {
console.log("TLS connection secured");
});
}
It handshakes correctly but the certificate I use is the static one in 'details', not the one I get in the SNICallback.
To test it I'm running the server and using gnutls-cli as a Client:
~$ gnutls-cli -V -s -p 10110 --crlf --insecure -d 5 localhost3
STLS
^D (Control+D)
The above command is supposed to get me the 'localhost3' certificate but it's getting the 'localhost1' because it's defined in 'details' var;
There are just too many examples throughout the internet with HTTPS or for TLS Clients, which it's a lot different from what I have here, and even for Servers as well but they're not using SNI. Any help will be appreciated.
Thanks in advance.
The answer is quite simple using tls.TLSSocket, though there is a gotcha with the listeners.
You have to remove all the listeners from the regular net.Socket you have, instantiate a new tls.TLSSocket using your net.Socket and put the listeners back on the tls.TLSSocket.
To achieve this easily, use a wrapper like Haraka's tls_socket pluggableStream over the regular net.Socket and replace the "upgrade"
function to something like:
pluggableStream.prototype.upgrade = function(options) {
var self = this;
var socket = self;
var netSocket = self.targetsocket;
socket.clean();
var secureContext = tls.createSecureContext(options)
var tlsSocket = new tls.TLSSocket(netSocket, {
// ...
secureContext : secureContext,
SNICallback : options.SNICallback
// ...
});
self.attach(tlsSocket);
}
and your options object would have the SNICallback defined as:
var options {
// ...
SNICallback : function(serverName, callback){
callback(null, tls.createSecureContext(getCertificateFor(serverName));
// ...
}
}
I use the following library to work with the amazon product api, which is actually pretty cool
(-> https://github.com/livelycode ) but I'm stuck on a certain point.
I manage to retrieve a response, but somehow I'm searching in the wrong region
How can I set the api to the current region?
I'm already having a associate Id with EU region I guess, so I think I missed some config
Here's what I have:
var region = {
host: "ec2.eu-west-1.amazonaws.com", // use a different region to the default
};
var prodAdv = aws.createProdAdvClient('xxx',
'xxx', 'xxx-21', region);
var options = {SearchIndex: "Books", Keywords: "Javascript"}
prodAdv.call("ItemSearch", options, function(err, result) {
//console.log(JSON.stringify(result.Items.Item[0]));
console.log(result.Items.Item[0]);
});
Currently it runs this service http://webservices.amazon.com/AWSECommerceService/2011-08-01, but I'd really like to change it to amazon.de
thank you
For anyone who might be interested in this later on.
I switched to https://github.com/dmcquay/node-apac and did just overwrite the defaultEndPoint
var util = require('util'),
OperationHelper = require('apac').OperationHelper;
OperationHelper.defaultEndPoint = 'ecs.amazonaws.de'
var opHelper = new OperationHelper({
awsId: 'xxx',
awsSecret: 'xxx',
assocId: 'xxx-'
});
opHelper.execute('ItemSearch', {
'SearchIndex': 'Books',
'Keywords': 'harry potter',
'ResponseGroup': 'ItemAttributes,Offers'
}, function(results) {
console.log(results.ItemSearchResponse.Items[0].Item);
});
I'm pretty sure, something like this is also possible in the other library.
Best
just got the same problem.
My solution is the overriting the options while creating the
var prodAdvOptions = {host: "ecs.amazonaws.de", region: "DE"};
var prodAdv = aws.createProdAdvClient('yourAccessKeyId', 'yourSecretAccessKey', 'yourAssociateTag', prodAdvOptions );
with the prodAdvOptions you can change the region and tne host.
I'm trying to make a connection to APNs. It simply won't connect. I get variations of:
apn Socket error occurred +609ms { [Error: socket hang up] code: 'ECONNRESET' }
and
apn Connection error occurred before TLS Handshake +0ms
This is for a Passbook pass. Not an app. I'm using Passbook certificates.
My code is:
var apns = require('apn');
var root = process.cwd();
var fs = require('fs');
var options = {
cert: root + '/certs/new/cert.pem', /* Certificate file path */
certData: null, /* String or Buffer containing certificate data, if supplied uses this instead of cert file path */
key: root + '/certs/new/key.pem', /* Key file path */
keyData: null, /* String or Buffer containing key data, as certData */
passphrase: 'secret', /* A passphrase for the Key file */
ca: null, /* String or Buffer of CA data to use for the TLS connection */
gateway: 'gateway.sandbox.push.apple.com',/* gateway address */
port: 2195, /* gateway port */
enhanced: true, /* enable enhanced format */
errorCallback: undefined, /* Callback when error occurs function(err,notification) */
cacheLength: 100 /* Number of notifications to cache for error purposes */
};
var apnsConnection = new apns.Connection(options);
var myDevice = new apns.Device('token');
var note = new apns.Notification();
note.payload = {};
note.device = myDevice;
apnsConnection.sendNotification(note);
It appears that I mixed up my certificates. I'm sure I tried swapping them earlier but obviously didn't.
cert: Your app cert.
key: Apple's WWDR
are you behind a proxy? that could be the issue (at least it is often in my case)
Try the following structure : Read the .cert and .key files manually and set them as certData and keyData property, respectivelly. Here is the core :
var key = root + '/certs/new/key.pem'
var cert = root + '/certs/new/cert.pem';
var certData = fs.readFileSync(cert, encoding='ascii');
var keyData = fs.readFileSync(key, encoding='ascii');
var apnsConnection = new apns.Connection({
certData: certData,
keyData: keyData,
gateway: 'gateway.sandbox.push.apple.com',
port: 2195,
... /* other configs of course */
});