I am trying to communicate with a web service. In order to do that, I need to encrypt a message using the public key that I received from the web service. The doc says the following about the public key format:
Format: X.509 encoded key in ANS.1 (sic!)
(ANS.1 is ASN.1 I guess).
The public key is:
-----BEGIN CERTIFICATE-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDE+ApyETIF1cXzKnU144P6lg/FcilmuQS2wBvaWp6t9OovthGmrsszd7eo4rL6Nitj1YOKETTtnwm4T+1EEyBrgwcfXAlm3FasTC/HIzhRRa+F8Yuz+UZkGvgP8Qa6B0vRob2BjhWx1PfwuWHQxGvAjiqUJ/dEMjocFuCrY5NZqwIDAQAB-----END CERTIFICATE-----
I tried to use this is a NodeJS code with the following:
const key = crypto.createPublicKey({
key: Buffer.from(publicKey),
format: 'der',
type: 'pkcs1'
});
But I received the following error:
node:internal/crypto/keys:607
handle.init(kKeyTypePublic, data, format, type, passphrase);
^
Error: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
at Object.createPublicKey (node:internal/crypto/keys:607:12)
at Object.<anonymous> (/XXXXXXXX/wsClient.js:16:20)
at Module._compile (node:internal/modules/cjs/loader:1149:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1203:10)
at Module.load (node:internal/modules/cjs/loader:1027:32)
at Module._load (node:internal/modules/cjs/loader:868:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:23:47 {
opensslErrorStack: [
'error:0D09B00D:asn1 encoding routines:d2i_PublicKey:ASN1 lib',
'error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error'
],
library: 'asn1 encoding routines',
function: 'asn1_check_tlen',
reason: 'wrong tag',
code: 'ERR_OSSL_ASN1_WRONG_TAG'
}
I can't even convert this public key using openssl into any usable format. The only way to see inside it for me was to use the following online tool:
https://lapo.it/asn1js/
Here I can at least see that the public key is valid, but I don't know how to use it in NodeJS. Converting it is also an accaptable solution for me.
After some painful hours it turns out that two things had to be done:
replace "BEGIN CERTIFICATE" with "BEGIN PUBLIC KEY" and the same for the end
They also needed to be in a separate line
After that NodeJS Crypto is able to parse the key.
Interestingly phpseclib was able to parse the key in the original format and then output it in the correct one, that's how I realized the solution.
Related
I'm trying out the subtle crypto methods in Node.js 18. I want to import a private RSA Key.
I copied the example straight from the MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#pkcs_8_import) and rewrote it to be able to run it in Node.js:
const { subtle } = require('crypto').webcrypto;
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
// Example 2048 bit Key generated from https://travistidwell.com/jsencrypt/demo/
const pemEncodedKey = `-----BEGIN PRIVATE KEY-----
MIIEowIBAAKCAQBux5llZPW8B9b3n7lvw6wqeNUX/GKUB3iWN6FWWXRRlSdqvNkI2nGiiKE5lJv+ca507P/xPsj/ThmntnVPrwiN98FWlY8h7GPKAqPzzG+C+kOPpqvkZm10RHn/pjcdAi5QqBauMrZCis77x5O1ynJFu6DcDH/QM+0O4FCL+KHcahDhp/3Oc5O4cGDbGJcc8AsnD7d2Dv1yiFMDidKg2OuQIb/YvdxgO+DKB3Pjali3escD7b/Tj9iYQIhV3wEcXBFnenQGqz227bmtuRatyDgcOB5HL8k/YoxzTm7+1Qh2rhzJBe5zdZ86Ms7P7RPCg+od+wehzUowxWkxB372vWWfAgMBAAECggEAUxqRTKssXV5UOXctGVbk9QeodFH1ca8ZGzeoZKq+w+TsqPn6ptWYoaF1sUh2ra6CfVy9tDCxgDUKsfICl0BrXnUaKOYRdhVr1sOcUuxuSweLX1xdXv4n5izoiIwclDpqnD88pHmOmOSg2eiiOqIgj4dt6SXHTF1n3N0SD675XeuGmUVmNvw3V//FjpQqgAgVRsFoVI8rd3yxEeZRy/T8/lZWWQxt9232Ipcd91zHXFEvj+gQ8pAtyhLJ34dDJdT7Fs7eoDAkGqson1TedblGPbq+pieB59xGqciZgBQpcZNsfCuyzRA3skgd1Fu2NRnzgdDklKHnjEcUfib12nqTAQKBgQCyu77dAZezADejbCDxyRpHnGfp1Jz5p/Y/j9hvGEPl72aiFFZpD3Rbz3kPn9SayYVq5Zxjlt+KwuljNFppYeK0ZVZ2zWi1VmhrHwl+3yemf4ufltvCDCutDNLjjkt1rlwiz+eDmHSJiOFSEc5uJcFrwcAWDBKBQP1vn4VFqJ43fwKBgQCeq3tzAd/9W3ph7Dgv2ungimQ9vSZIfWawFnKyhfF5P7y8wJ6mgxnNv/BiuwqdqS5DY72EVq0tjwVvpHA6xCiWKYQ/9lhb9OJZsADbAaMRgKzuKCmpxJA2c6EKDQ2TK0/3WdLLnpuAXKcHP38sKJmKxm5wgRjC0pZLpWcK/xjh4QKBgQCEuxIxlAYxAz9OWHVauUqP1aIBr0fnywj/CPblAbMipZelU88b9EMoDzpLFRnQ3Uj8KonqF1fo93hUmMNvsSanav47+a0BxaqDqqfllRkf92Yb3O9T+q/Qsk5GeRymxxZbL+QxAN3CaWlTBjAz8kvilx7sAIkZfcb3xxI0udTNRwKBgD664SWI2jtaTToln9kbnVdOn27hNx91pIF9fn8iAWPEVSPyq0Z9klgLyEfgVsQaPNYburN1aSYX4zhONKinILytUUHQbQJ+AHcg5FWxgfzLeJL3gfFCaxl8AXDt1C4Y85aBBpvF6wiGmOp+qhKVQo7hAIyuHVH4276wd9qbHAVBAoGBAIqjXYfmtHRONXVtMCt5rlt3Y/Pe0KkA6vvfjGMnRKrmvEsHXh8Aj9ZPiTDxBKweJggg9BoJ/bs3juENIx5cJ76aiBYsdhw+4IGF79IfZm73k2ZC9exy9m69yp7GszmdZVJuiFlRoJFBMQm6oDH1+tUPW9M/76Ah8GpjkWx6dAcr
-----END PRIVATE KEY-----`;
const importPrivateKey = async () => {
const pemHeader = "-----BEGIN PRIVATE KEY-----";
const pemFooter = "-----END PRIVATE KEY-----";
const pemContents = pemEncodedKey.substring(pemHeader.length, pemEncodedKey.length - pemFooter.length);
const binaryDerString = Buffer.from(pemContents, 'base64').toString('binary');
const binaryDer = str2ab(binaryDerString);
const key = await subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSA-OAEP",
hash: "SHA-256",
},
true,
["decrypt"]
);
}
importPrivateKey();
Now when I run this file, I get:
node:internal/crypto/keys:618
handle.init(kKeyTypePrivate, data, format, type, passphrase);
^
Error: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag
at createPrivateKey (node:internal/crypto/keys:618:12)
at Object.rsaImportKey (node:internal/crypto/rsa:270:19)
at SubtleCrypto.importKey (node:internal/crypto/webcrypto:513:10)
at importPrivateKey (/Users/felix/Desktop/fts/js-api/bin/test.js:23:28)
at Object.<anonymous> (/Users/felix/Desktop/fts/js-api/bin/test.js:35:1)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Module._load (node:internal/modules/cjs/loader:827:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12) {
opensslErrorStack: [
'error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error',
'error:0D07803A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error'
],
library: 'asn1 encoding routines',
function: 'asn1_check_tlen',
reason: 'wrong tag',
code: 'ERR_OSSL_ASN1_WRONG_TAG'
}
This example does work when I use the key from the linked MDN docs. But not if I use a freshly generated one.
What is going wrong here?
Your private key is inconsistent: You use the header and footer of a PKCS#8 key, but the body of a PKCS#1 formatted key. This can be verified e.g. in an ASN.1 parser like https://lapo.it/asn1js/.
Note that the JSEncrypt demo generates a PKCS#1 formatted private key (inclusive PKCS#1 header and footer).
Since WebCrypto API does not support PKCS#1 (see here), you need to convert the key to a PKCS8 formatted key, e.g. with OpenSSL.
Your key in PKCS#8 format (without line breaks in the body, so it can be used directly in JavaScript code) is:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAG7HmWVk9bwH1vefuW/DrCp41Rf8YpQHeJY3oVZZdFGVJ2q82QjacaKIoTmUm/5xrnTs//E+yP9OGae2dU+vCI33wVaVjyHsY8oCo/PMb4L6Q4+mq+RmbXREef+mNx0CLlCoFq4ytkKKzvvHk7XKckW7oNwMf9Az7Q7gUIv4odxqEOGn/c5zk7hwYNsYlxzwCycPt3YO/XKIUwOJ0qDY65Ahv9i93GA74MoHc+NqWLd6xwPtv9OP2JhAiFXfARxcEWd6dAarPbbtua25Fq3IOBw4HkcvyT9ijHNObv7VCHauHMkF7nN1nzoyzs/tE8KD6h37B6HNSjDFaTEHfva9ZZ8CAwEAAQKCAQBTGpFMqyxdXlQ5dy0ZVuT1B6h0UfVxrxkbN6hkqr7D5Oyo+fqm1ZihoXWxSHatroJ9XL20MLGANQqx8gKXQGtedRoo5hF2FWvWw5xS7G5LB4tfXF1e/ifmLOiIjByUOmqcPzykeY6Y5KDZ6KI6oiCPh23pJcdMXWfc3RIPrvld64aZRWY2/DdX/8WOlCqACBVGwWhUjyt3fLER5lHL9Pz+VlZZDG33bfYilx33XMdcUS+P6BDykC3KEsnfh0Ml1PsWzt6gMCQaqyifVN51uUY9ur6mJ4Hn3EapyJmAFClxk2x8K7LNEDeySB3UW7Y1GfOB0OSUoeeMRxR+JvXaepMBAoGBALK7vt0Bl7MAN6NsIPHJGkecZ+nUnPmn9j+P2G8YQ+XvZqIUVmkPdFvPeQ+f1JrJhWrlnGOW34rC6WM0Wmlh4rRlVnbNaLVWaGsfCX7fJ6Z/i5+W28IMK60M0uOOS3WuXCLP54OYdImI4VIRzm4lwWvBwBYMEoFA/W+fhUWonjd/AoGBAJ6re3MB3/1bemHsOC/a6eCKZD29Jkh9ZrAWcrKF8Xk/vLzAnqaDGc2/8GK7Cp2pLkNjvYRWrS2PBW+kcDrEKJYphD/2WFv04lmwANsBoxGArO4oKanEkDZzoQoNDZMrT/dZ0suem4Bcpwc/fywomYrGbnCBGMLSlkulZwr/GOHhAoGBAIS7EjGUBjEDP05YdVq5So/VogGvR+fLCP8I9uUBsyKll6VTzxv0QygPOksVGdDdSPwqieoXV+j3eFSYw2+xJqdq/jv5rQHFqoOqp+WVGR/3Zhvc71P6r9CyTkZ5HKbHFlsv5DEA3cJpaVMGMDPyS+KXHuwAiRl9xvfHEjS51M1HAoGAPrrhJYjaO1pNOiWf2RudV06fbuE3H3WkgX1+fyIBY8RVI/KrRn2SWAvIR+BWxBo81hu6s3VpJhfjOE40qKcgvK1RQdBtAn4AdyDkVbGB/Mt4kveB8UJrGXwBcO3ULhjzloEGm8XrCIaY6n6qEpVCjuEAjK4dUfjbvrB32pscBUECgYEAiqNdh+a0dE41dW0wK3muW3dj897QqQDq+9+MYydEqua8SwdeHwCP1k+JMPEErB4mCCD0Ggn9uzeO4Q0jHlwnvpqIFix2HD7ggYXv0h9mbveTZkL17HL2br3KnsazOZ1lUm6IWVGgkUExCbqgMfX61Q9b0z/voCHwamORbHp0Bys=
-----END PRIVATE KEY-----
With this key format the JavaScript code works and the key is imported correctly.
By the way, under NodeJS you can use Buffer directly, i.e. the conversion via a binary string into an ArrayBuffer and thus also str2ab() are not needed:
...
const binaryDer = Buffer.from(pemContents, 'base64');
const key = await subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSA-OAEP",
hash: "SHA-256",
},
true,
["decrypt"]
);
...
Note that even under v18.1.0, the WebCrypto API is marked Stability: 1 - Experimental (here).
I need to access to the API of a service provider (for my company)
So, they gave me a 'doc' and a SSL certificate in multiple form (.jks, .p12, .pem)
I work with NodeJS so I took the .pem, inside there is 2 certificates and 1 encrypted private key.
I split the .pem in 3 files, mycert.crt.pem, mycert.key.pem, mycert2.crt.pem
(I checked on https://www.sslshopper.com/certificate-key-matcher.html to know which cert use with the key)
So my NodeJS, I used the least possible module to avoid module problem :
const cert = fs.readFileSync(path.resolve('cert', 'mycert.crt.pem'))
const key = fs.readFileSync(path.resolve('cert', 'mycert.key.pem'))
let options = {
hostname: 'https://serviceproviderurl.com',
path: 'v1/api/example',
method: 'POST',
key: key,
cert: cert
}
let req = https.request(options, function (res) {
console.log(res.statusCode)
res.on('data', function (d) {
process.stdout.write(d)
})
})
req.end()
And the error message :
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
at Object.createSecureContext (_tls_common.js:151:17)
at Object.connect (_tls_wrap.js:1407:48)
at Agent.createConnection (https.js:125:22)
at Agent.createSocket (_http_agent.js:234:26)
at Agent.addRequest (_http_agent.js:193:10)
at new ClientRequest (_http_client.js:276:16)
at Object.request (https.js:309:10)
at Object.<anonymous> (/mnt/c/Project/test.js:74:17)
at Module._compile (internal/modules/cjs/loader.js:959:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10) {
opensslErrorStack: [
'error:0907B00D:PEM routines:PEM_read_bio_PrivateKey:ASN1 lib',
'error:2306A075:PKCS12 routines:PKCS12_item_decrypt_d2i:pkcs12 pbe crypt error',
'error:23077074:PKCS12 routines:PKCS12_pbe_crypt:pkcs12 cipherfinal error'
],
library: 'digital envelope routines',
function: 'EVP_DecryptFinal_ex',
reason: 'bad decrypt',
code: 'ERR_OSSL_EVP_BAD_DECRYPT'
I think I missed something with the 3 certificates. Also they didn't gave me a passphrase, is it normal ?
I don't have a good knowledge about SSL certificate use and I hope you can help me.
Thank you
I am getting following error while trying to update the entity in google cloud datastore:
InvalidKey: A key should contain at least a kind.
at keyToKeyProto (/Volumes/Drive B/dev/zapi/node_modules/#google-cloud/datastore/src/entity.js:696:11)
at Array.map (<anonymous>)
at DatastoreRequest.createReadStream (/Volumes/Drive B/dev/zapi/node_modules/#google-cloud/datastore/src/request.js:226:23)
at DatastoreRequest.get (/Volumes/Drive B/dev/zapi/node_modules/#google-cloud/datastore/src/request.js:461:8)
at /Volumes/Drive B/dev/zapi/node_modules/#google-cloud/common/build/src/util.js:681:32
at new Promise (<anonymous>)
at Datastore.wrapper [as get] (/Volumes/Drive B/dev/zapi/node_modules/#google-cloud/common/build/src/util.js:662:20)
at fetchEntity (/Volumes/Drive B/dev/zapi/node_modules/gstore-node/lib/model.js:204:36)
at Function.get (/Volumes/Drive B/dev/zapi/node_modules/gstore-node/lib/model.js:174:16)
at Promise (/Volumes/Drive B/dev/zapi/node_modules/gstore-node/lib/utils.js:39:35)
at new Promise (<anonymous>)
at Function.wrapper (/Volumes/Drive B/dev/zapi/node_modules/gstore-node/lib/utils.js:27:16)
at resolve (/Volumes/Drive B/dev/zapi/graphql/mutations/user/linkConsult.js:101:44)
I don't know why is this coming for.
Thanks in advance.
When you trying to access key and key is not available when no data available, it gives the error
"Error Message : InvalidKey: A key should contain at least a kind"
To avoid this error first make sure that [datastrore.KEY] is available.
Thanks
I want to add to this answer with a little more detail. Here are some other helpful points to investigate if you run into this error when using the datastore.save method. The entity needs the correct key property like the example below.
// remember to use the key method on the google data store instance
const entity = {
key: dataStore.key('Name of your Kind') // this is Kind property you see on the GCP dashboard,
data: {
example: 'this is an example',
...
}
};
// then save the entity
const dbResult = await dataStore.save(entity)
a friend of mine lost her password for a BitGo account. her "Key Card" contains her private key in the form (actual info redacted for security reasons):
User Key:
{"iv":"IDMkr...UH4g0TBCofAcIg","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"lI8k...vhX0","ct":"Kf...LOmgbn67w2CRYBhcXtX
0wdPF3D7ThCKaeZhTymin9hcMD5eL...AosmmvfA8npiDIHWgvdbHAk"}
and it's clear that the "ct" (ciphertext) was encrypted using AES 256 CCM. I've found a module (https://github.com/spark/node-aes-ccm) that will help me decrypt the private key ("ct") but the docs for the module are not very helpful:
decrypt(key, iv, ciphertext, aad, auth_tag)
key, iv, plaintext, aad, and auth_tag are all Buffer objects. decrypt will return an object like the following:
{
plaintext: Buffer,
auth_ok: Boolean
}
so other than that the parameters are buffers, there's no description for them... however, it seems I've got everything I need except the key. from reading the docs on a related module (https://github.com/xorbit/node-aes-gcm):
key is a 16, 24 or 32-byte Buffer object containing the AES key used
for encryption
I gather this is the key with which the private key was encrypted, but where would I get that and how does it relate to the lost password?
any help greatly appreciated
* Addendum I *
my friend figured out the password so following Ben's advice below I thought to try it. the code is straightforward:
var sjcl = require('sjcl-all');
var s = sjcl.decrypt("ThePassword", {
"iv":"IDMkrTa5UH4g0TBCofAcIg",
"v":"1",
"iter":"10000",
"ks":"256",
"ts":"64",
"mode":"ccm",
"adata":"",
"cipher":"aes",
"salt":"lI8kABgvhX0",
"ct":"KfJUrLOmgbn67w2CRYBhcXtX0wdPF3D7ThCKaeZhTymin9hcMD5eLHIUAosmmvfA8npiDIHWgvdbHAk"
}
);
console.log(s);
but when I run it, it pukes with the exception below:
/Users/ekkis/Development/decrypt/node_modules/sjcl-all/sjcl.js:66
c="{",d="";for(b in
a)if(a.hasOwnProperty(b))switch(b.match(/^[a-z0-9]+$/i)||p(new
sjcl.exception.invalid("json encode: invalid property
name")),c+=d+'"'+b+'":',d=",",typeof a[b]){case "number":case
"boolean":c+=a[b];break;case
"string":c+='"'+escape(a[b])+'"';break;case
"object":c+='"'+sjcl.codec.base64.fromBits(a[b],0)+'"';break;default:p(new
sjcl.exception.bug("json encode: unsupported type"))}return
c+"}"},decode:function(a){a=a.replace(/\s/g,"");a.match(/^{.*}$/)||p(new
sjcl.exception.invalid("json decode: this isn't json!"));
TypeError: a.replace is not a function
at Object.decode (/Users/ekkis/Development/decrypt/node_modules/sjcl-all/sjcl.js:66:438)
at Object.decrypt (/Users/ekkis/Development/decrypt/node_modules/sjcl-all/sjcl.js:65:473)
at Object. (/Users/ekkis/Development/decrypt/buff.js:3:14)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.runMain (module.js:605:10)
at run (bootstrap_node.js:425:7)
so it complains that the json isn't json. but it looks fine to me! any help greatly appreciated
This encrypted JSON blob is produced by the SJCL library (https://www.npmjs.com/package/sjcl). A decryption would be done using sjcl.decrypt("password", json_blob). However, if your friend hasn't already been in touch with BitGo [support at bitgo.com], I recommend that route first.
I am following the tutorial Windows Azure SDK for Node.js - Compute Management to manage virtual machines.
I downloaded the pem file using azure account cert export to <Subscription GUID>.pem.
The script currently contains:
var subscriptionId ="<Subscription GUID>";
var pem = "<Subscription GUID>.pem";
var computeManagementClient = computeManagement.createComputeManagementClient(computeManagement.createCertificateCloudCredentials({
subscriptionId: subscriptionId,
pem: fs.readFileSync(pem)
}));
And when I run it from Node.js it produces the error:
C:\Apps\azure\node_modules\azure-mgmt-compute\node_modules\azure-common\lib\util\validate.js:416
throw new Error('Required argument ' + name + ' for function ' + func + ' is
^
Error: Required argument credentials.pem for function CertificateCloudCredentials is not defined
at throwMissingArgument (C:\Apps\azure\node_modules\azure-mgmt-compute\node_modules\azure-common\lib\util\validate.js:416:9)
at ArgumentValidator._.extend.string (C:\Apps\azure\node_modules\azure-mgmt-compute\node_modules\azure-common\lib\util\validate.js:426:7)
at C:\Apps\azure\node_modules\azure-mgmt-compute\node_modules\azure-common\lib\services\credentials\certificateCloudCredentials.js:35:9
at Object.validateArgs (C:\Apps\azure\node_modules\azure-mgmt-compute\node_modules\azure-common\lib\util\validate.js:478:3)
at new CertificateCloudCredentials (C:\Apps\azure\node_modules\azure-mgmt-compute\node_modules\azure-common\lib\services\credentials\certificateCloudCredentials.js:32:14)
at Object.exports.createCertificateCloudCredentials (C:\Apps\azure\node_modules\azure-mgmt-compute\lib\compute.js:54:10)
at Object.<anonymous> (C:\Apps\azure\setup.js:14:97)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
The issue is with pem: fs.readFileSync(pem). The SDK is not converting the node buffer to a string. There is an issue on Github.
Until this is fixed use toString on the buffer:
pem: fs.readFileSync(pem).toString()