what's spkac in crypto module of nodejs? - node.js

I found spkac from this document:
https://nodejs.org/api/crypto.html#crypto_certificate_exportchallenge_spkac
const spkac = getSpkacSomehow();
The docs says getSpkacSomehow.
How can I get spkac? What's the detail?

SPKAC represents Netscape signed public key and challenge.
As stated in the doc, in this scenario, the SPKAC should come from an HTML form's <keygen> element.
Though you can also use openssl to generate a SPKAC manually: openssl spkac -key key.pem -challenge hello -out spkac.cnf, see openssl doc
Anyway, since the <keygen> element has been deprecated, this should not be used anymore.
If you're interested, here's a little history about SPKAC.

Related

Node TLS Error: ca md too weak, when making request with Axios

I have a problem when I am making a request with Axios in node getting Error: ca md too weak.
I am sending .pfx file and password for the pfx certificate file.
I can easily access API using Postman and sending pfx certificate and password, but making a request with Axios in node js (v. 18.0) I get Error: ca md too weak.
I don't want to downgrade Node version and using: process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; also doesn't help.
Also to connect to the server I have to use VPN and this error pops up even if I haven't turned on VPN.
Is it possible to bypass this check, so I can send a request as I do in the Postman?
Here is a code example:
agent = new https.Agent({
pfx: fs.readFileSync((process.cwd() + "\\src\\sources\\KIISWSClient.pfx")),
passphrase: 'password',
rejectUnauthorized: false,
});
userKiisData = axios.post('https://ws-kiis.hlk.hr/AdeoMembersPublicService.svc/WSPublic/BasicData', {
Username: "myusername",
Role: "role"
}, {
auth: {
username: "user",
password: "mypassword"
},
httpsAgent: agent
}
return userKiisData
Here is the error:
Error: ca md too weak
at configSecureContext (node:internal/tls/secure-context:278:15)
at Object.createSecureContext (node:_tls_common:113:3)
at Object.connect (node:_tls_wrap:1622:48)
at Agent.createConnection (node:https:142:22)
at Agent.createSocket (node:_http_agent:343:26)
at Agent.addRequest (node:_http_agent:294:10)
at new ClientRequest (node:_http_client:311:16)
at Object.request (node:https:352:10)
TLDR: jump to end if you don't care about understanding
rejectUnauthorized directs nodejs to accept (some) invalid certs used by the server instead of aborting the connection, but this error is occurring on your own client cert/chain not that of the server. (That's why it can occur even without the VPN you need for an actual connection.) The "CA_MD_TOO_WEAK" check occurs in modern versions of OpenSSL (1.1.0 up) but can vary depending on how the OpenSSL was built, thus for nodejs it also depends whether your nodejs was built to use its own embedded OpenSSL (usual on Windows) or one already provided on the system where it is installed (usual on Linux).
You can look at the signature algorithms(s) used in your cert chain with openssl if you have (or get) it on this machine or you (may, can, and do) copy the pfx to a machine where ditto. First do
openssl pkcs12 -in yourpfxfile -nokeys >temp0
and look at temp0; it should contain one or more blocks each consisting of a subject= line, an issuer= line, a -----BEGIN CERTIFICATE----- line, several lines of base64, and an -----END CERTIFICATE----- line. (Likely there are also Bag Attributes: lines optionally followed by indented lines; ignore those.)
If there is only one block and it has the same value for subject and issuer, then your error should not have happened, because the cert is selfsigned and OpenSSL should not be checking signature strength on selfsigned cert. Otherwise, do
openssl x509 -in temp0 -noout -text -certopt no_pubkey,no_sigdump,no_extensions
and you should get a few lines of output that includes signatureAlgorithm: x where x is of the form {hash}withRSAEncryption ecdsa_with_{hash} dsa-with-{hash} or dsaWith{hash}. If {hash} is MD5, the signature on your certificate is weak and doesn't provide the security it claims -- although in your situation, if the server accepts it in spite of this weakness, it's probably not your problem.
If the first (leaf) cert is not selfsigned (subject not same as issuer) and its signature algorithm does not use MD5, either
the OpenSSL used by your nodejs is set to a higher than usual 'security level' -- this is not likely for a nodejs that has embedded OpenSSL but more plausible for a system-provided one, especially on security-focussed systems like RedHat 8; or
the problem is with a cert other than the first one (i.e. a CA cert), but this shouldn't occur if your cert(s) are from a properly-run CA because such a CA should never have a higher CA cert signed with a weaker key or signature algorithm than the leaf cert, except the selfsigned root whose signature doesn't matter, and isn't checked so it wouldn't cause your error. However, if you want, break each block other than the first into a separate file, and repeat the openssl x509 procedure above on each except if the last one has subject and issuer the same don't check it because it is the selfsigned root cert.
Anyway, workaround: add to your https-Agent options ciphers: "DEFAULT:#SECLEVEL=0". This should turn off several checks designed to prevent insecure SSL/TLS connections, including the one relevant here; as a result, if you use this code to process data of any importance, it may be at higher risk depending on what the server does.

AES-CBC Decryption

I am trying to resolve this problem:
Using the following endpoint, obtain and decrypt the encrypted message. You may use any language and library of your choice to implement the solution.
POST https://challenge-b375.szechuen.workers.dev
Request
Body (Plain): Your X25519 public key (Base64)
Response
Body (JSON)
publicKey: Our ephemeral X25519 public key (Base64)
encryptedMsg: Message encrypted with AES-CBC using the X25519 shared key (Base64)
iv: IV for AES-CBC encryption (Base64)
Sample Request
$ curl https://challenge-b375.szechuen.workers.dev -d 'fjUfGYyHBYBRVMJp+P2aSHghq1K2s47ytzXLLhKWzQ4='
Sample Response
{"publicKey":"1ObUUf0ktnLKFGqbbuD63L6kQWS9YzUffucZzLJRJRE=","encryptedMsg":"22RhujlT7rGpgs1bZ4TshPYhHUvsjSVsuHOtBjC+rdEq1S4r+ozfdhMXU2tVdFtoSP2l7d1WkLZfioMSnNW20kt8s7+r9z72Ona7GWMg3q2C77jS6LOvh62FG1+uX5yea6YHdU7RceKqwiI00wyj7Q==","iv":"XWYiWHaw0m73nRKVvTPKXg=="}
I used openssl, but received "Bad magic number" in the output. Below is my openssl command where I've transformed the private key and the iv in binaries. encryptedMsg contains the message opbtained with
curl https://challenge-b375.szechuen.workers.dev -d 'fjUfGYyHBYBRVMJp+P2aSHghq1K2s47ytzXLLhKWzQ4='
openssl enc -base64 -aes-256-cbc -in encryptedMsg -d -k 666A55664759794842594252564D4A702B5032615348676871314B3273343779747A584C4C684B577A51343D -iv 65397530355659736452677571462B616C794E7965673D3D
bad magic number
I tried without transforming them in binaries, but I received errors. I also couldn't find a website that would decrypt it. Any help would be appreciated :)
Thanks

Using .p12 file to send signature and validate using Node Crypto verify

I am trying to validate calls to my API from an IOS app. I have generated keys for backend like so:
openssl genrsa -aes256\
-passout pass:pass\
-out signature_private_key.pem 2048
openssl rsa \
-in signature_private_key.pem\
-passin pass:pass\
-out signature_public_key.pem\
-outform PEM\
-pubout
In the node backend I am validating the call like this:
static createSignatureString(bodyString, apiKey, timestamp) {
const bodyHash = crypto
.createHash("sha256")
.update(bodyString)
.digest("base64");
return `${bodyHash}:${apiKey}:${timestamp}`;
}
static createSignature(string, privateKey) {
const sign = crypto.createSign("RSA-SHA256");
sign.write(string);
sign.end();
return sign.sign(privateKey, "base64");
}
static verifySignature(xMessageSignatureString, signature, publicKey) {
const verify = crypto.createVerify("RSA-SHA256");
verify.update(xMessageSignatureString);
verify.end();
return verify.verify(publicKey, new Buffer(signature, "base64"));
}
The mobile app uses a .p12 file to generate a signature and send to the rest API. Given the above how would I generate a .p12 file using openssl that would send a valid signature?
To create a PKCS12 file you need the private key (which you have) and an (X.509) certificate containing the matching publickey, or if applicable a series of certificates starting with the matching one called a 'chain'. Either you can get this certificate/chain from a CA, or OpenSSL can create a self-signed certificate which uses no chain and which is acceptable to many applications but not all:
openssl req -new -x509 -key privatekeyfile -out certfile [-days N] [-$hash]
-days N specifies how long you want the cert to be valid; for dummy certs it is common to choose a large value like 3650 for about 10 years, or more, although you can as easily generate a replacement in the future if you need to. It's safest not to go beyond 2037 because some C programs, though fewer than in the past, are still affected by the Y2038 bug. You can specify the hash used in the self-signature; this doesn't really matter, but some people prefer to avoid the default (SHA1) and use e.g. -sha256 instead. You can specify the 'subject' name on the commandline if you wish with -subj; see the man page. There are also many extensions which can be placed in an X.509 certificate, but whether you want any of them and if so which ones and what values varies depending on the application(s) that will use it.
Once you have the cert and privatekey in files:
openssl pkcs12 -export -in certfile -inkey privatekeyfile -out p12file
On both of these commands you can specify passwords on the commandline, as you did in the commands you posted, if you prefer; see the man pages and/or usage messages. Remember that this practice makes your passwords temporarily visible to other processes, often including other users if on a shared system.
For clarity: on CA-issued certs the signature hash does matter and should not be SHA1 or weaker, but on root or self-signed certs it normally does not matter.

Making a unique extension id and key for Chrome extension?

I have a Chrome extension I made, but based it on some example found online. Its not in a crx file. The extension ID is the one used in the example. I would like to change it before I upload my extension to the chrome store. My question is how do I change this? Do I just manually change the letters in my manifest.json file? Or does the extension id have to be generated from something because its in a fixed format? Same for the key, can I just randomly change these two before I do anything else now that I'm ready?
{
// Extension ID: rnldjzfmornpzlahmmmgbagdohdnhdic
"key": "MIGfMA3GCSqGSIb3DFEBAQUAA4GNADCBiQKBgQDcBHwzDvyBQ6bDppkIs9MP4ksKqCMyXQ/A52JivHZKh4YO/9vJsT3oaZhSpDCE9RCocOEQvwsHsFReW2nUEc6OLLyoCFFxIb7KkLGsmfakkut/fFdNJYh0xOTbSN8YvLWcqph09XAY2Y/f0AL7vfO1cuCqtkMt8hFrBGWxDdf9CQIDAQAB",
"name": "Name of extension",
...
Note that extension signing consists of two "keys":
The private key file, .pem, that is used to sign CRX files and must remain the same for future updates.
The resulting public key in the manifest - can't be used to sign future updates (used to verify the signature instead), but can be used to force a particular ID for unpacked extensions since the ID is derived as a hash of the public key. (For those curious, if key is not present an unpacked extension falls back to hashing the path).
You have 2 options:
Let Google handle it.
Remove the key field from the manifest completely; then submit it to the store.
CWS will generate a new keypair for your extension (and, consequently, a new ID), which will be preserved between updates. If you need to maintain the ID for your development version (not always a good idea, as Chrome will get confused with autoupdates, but a good idea during storage.sync testing), you can extract the new public key from your Developer Dashboard using "More info" link on your item.
However, there is no way to get the .pem key from CWS. You are forever locked in CWS as auto-update source. That shouldn't matter though as Chrome disallows extension installs from elsewhere.
Retain control of the private key.
Note: this apporoach may be deprecated by now, and there's not much practical reason to use it.
You can generate a CRX file of your extension from chrome://extensions using "Pack extension" function.
If you don't provide an existing .pem file, Chrome will generate a new keypair (and thus, ID) for your extension.
Guard the resulting .pem key with your life carefully. It can be used to impersonate you as a developer when it comes to updates.
Then, when you submit the extension to CWS, include the .pem in the archive's root as key.pem (Note: removed from documentation; links to an archived version). This instructs CWS to use it instead of generating a new keypair. Note that you have to provide your private key to Google, since it modifies the extensions before signing.
Since the ID is a hash of a (randomly-generated) public key, there is a tiny chance of collision with an existing extension. If that happens, just re-generate the .pem file for a different one.
In either case: do not include the key field in the manifest when uploading, or CWS may reject it.
Also, do not hardcode the extension ID in your extension anywhere. It's accessible using one of those functions:
chrome.runtime.getManifest().id // gives "youridehere"
chrome.runtime.getURL("something") // gives "chrome-extension://youridhere/something"
And in CSS files, you can use __MSG_##extension_id__ as a macro:
background-image:url('chrome-extension://__MSG_##extension_id__/background.png');
You can create you own key and extension ID for the manifest:
openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -out key.pem
Key:
openssl rsa -in key.pem -pubout -outform DER | openssl base64 -A
Extension ID:
openssl rsa -in key.pem -pubout -outform DER | shasum -a 256 | head
-c32 | tr 0-9a-f a-p

Easy secure connection over unsafe network (both endpoints fully controlled)

I need to connect client & server over untrusted network. I've considered using TLS (crypto/tls), but from what I understand, I first need to create a create a crypto/x509.Certificate. But I feel overwhelmed by all the parameters I need to pass to the x509.CreateCertificate() function - it says it needs all of the following fields:
SerialNumber, Subject, NotBefore, NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains.
I have full control over both endpoints, so I believe I don't need any expiration or invalidation support/parameters (I can change keys both on client and server at any time I want) - so I can probably skip NotBefore and NotAfter (? or do I have to set them anyway?). What should I put in all the other fields, and why, to avoid any vulnerabilities? Also, can I use the same private/public key pair for both ways authentication (client to server, and server to client), or do I have to use 2 pairs?
Or, is there something simpler than TLS that I could use? Note however, that I need to two way authentication.
EDIT:
I created a simple library based on suggestions from the accepted answer, plus key generation code from generate_cert.go - see:
github.com/akavel/tunnel
Owlstead is partly correct. Your best bet is creating self signed certificates using OpenSSL. However, I would then use the Go TLS library for encryption. Below is some code that may help you.
Creating an x509 key pair
I normally follow the instructions here. Summary of commands (do for both client and server):
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
Using Go's TLS library
First, create a tls.Config. One TLS config will work on both client and server but some of the options only need to be set on one or the other:
cert, err := tls.LoadX509KeyPair(cert, key)
config := &tls.Config{
Certificates: []Certificates{cert},
ClientAuth: tls.RequireAnyClientCert, // Must be done on server
InsecureSkipVerify: true, // Must be done on client
}
On the server, you need to setup a TLS listener. This sets it up on port 4443:
listener, err := tls.Listen("tcp", ":4443", config)
for {
conn, err := listener.Accept()
acceptConn(conn) // your code
}
On the client:
conn, err := tls.Dial("tcp", serverAddr, config)
This will create an encrypted connection, but it will not verify the other side is who they say they are. The easiest way to do that is to give each server the public key of the other server and compare it to the server that has just connected. To find the public key on the other server, you need to:
c := conn.(*tls.Conn) // convert net.Conn from listener to tls conn
err := c.Handshake() // ensure handshake is completed without error
state := c.ConnectionState()
pubKey, err := x509.MarshalPKIXPublicKey(state.PeerCertificates[0])
bytes.Equal(pubKey, knownKey) // compare to known value
As owlstead stated, TLS is still your best bet. Creating your own certificates can be done by following some online guide.
The following article lets you create your own SSL root certificate (You effectively become your own Root CA, like Verisign and such). With this, you can create and sign your own application certificates and distribute them. If, as you say, you have full control over both end points, this is probably worth checking in to.
http://www.eclectica.ca/howto/ssl-cert-howto.php/

Resources