OpenPGP.js says elgamal keys are considered too weak - node.js

We are using PGP encryption to encrypt files before transfer. We are using the npm package OpenPGP.js to encrypt the files using a public key from the recipient. I have exported the public key in armored format to use with openpgp.encrypt function.
Here is the code to encrypt the file:
const publicKey = await openpgp.readKey({ armoredKey: key.publicKey });
const encrypted = await openpgp.encrypt({
message: await openpgp.createMessage({ text: readStream }),
encryptionKeys: publicKey
});
However the function call produces this error:
Error: Error encrypting message: Could not find valid encryption key
packet in key ea8be7d9f2fd53a7: elgamal keys are considered too weak.
The output of gpg --list-keys gives the following information
pub dsa1024 2010-07-23 [SCA]
ABCDEFGHIJK
uid [ unknown] my recipient <my.recipient#email.com>
sub elg2048 2010-07-23 [E]
I'm able to encrypt a file using GnuPG, but OpenPGP does not seem to like the public key. Is this error message valid? Do I need to request another key from the client, or is there a way to bypass this error message?
*Edit: After some research I have found that DSA-1024/(ElGamal-anything) is not safe anymore, so I'll probably have to request new keys be made.

OpenPGP implementations have different security considerations, and OpenPGP.js seems decided to reject DSA/ElGamal by default via this PR: https://github.com/openpgpjs/openpgpjs/pull/1264/files#
However it is possible to override this behaviour via config, examples are available in tests.

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

How can I create a signed JWT using npm Jose and then verify this token?

I am struggling to understand how to use the npm jose module (https://www.npmjs.com/package/jose) to create and verify signed JWT tokens in my Node application. My scenario is this: I want to sign an authenticated request to access a resource. I can successfully create a JWT claim for this request grant token that respects the properties of “its” and “aud”, “exp”, etc. but I want to sign it (to wit, using the SignJWT object and the ‘sign’ method) so that when it gets passed back to my server as a request I can validate it and grant or reject access.
The “sign” method doesn’t seem to like anything I pass it for the ‘key’ parameter (I am not passing any options — maybe I should be, but what?).
I am attempting to use RSA key pairs. I want to sign with the private key and verify with the public key. For my immediate need, I suppose I could use a symmetric key instead, but I am thinking of some other future scenarios where I will want this classic PKCS relationship of certificate keys. And at any rate, I don’t think this choice has anything to do with the current roadblock to my progress.
I first tried to use jose/util/generate_key_pair to create my public/private pair. But when I went to use the key, the error informed me this was not supported by my implementation. So I switched to trying to create a ‘pem’ cert outside of my app and applying that (as text), but that also failed. The ‘sign’ method reports that the key must be a ‘KeyLike’, ‘CryptoKey’, or ‘Uint8Array’ type. Well, the UInt8Array (node buffer) is not enough type information: it doesn’t speak to what is in that buffer, and “KeyLike” is such a vague definition that it’s ignorable. After beseeching the oracles of the search engines, I found I could create a key pair in CryptoKey format using the following from Node APIs:
crypto.webcrypto.subtle.generateKey(
{
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
[‘sign’, ‘verify’]
).then((pair:any) => {
serverInstance.keyPair = pair
})
But, still, when I get to the signing part:
siaToken.sign(serverInstance.keyPair.privateKey).then(signature => {
I get an exception that reports
“TypeError: CryptoKey does not support this operation”
Thinking this might have to do with the ‘usages’ parameter of generateKey, I tried various values there, but with no success.
So, I am flummoxed. Can anyone tell me how to (a) best produce a pair of keys for this purpose and (b) how to apply these for JWT signing?
I have also struggled with signing and verifying JWT using jose but was finally able to succeed with HS256 symmetric key encryption. I produced it by following steps (I am using jose-node-cjs-runtime for Node.js only use case. Feel free to replace with desired package. Also please note that I have found that these codes are working for Node.js version 16.7.0, 16.9.0 so please ensure that any of them is installed. If you want to deploy these changes to production environment, then also you have to ensure the deploy environment has the same Node.js version. One way this can be achieved is by mentioning Node.js version in engines key in package.json):
Add Required imports
// library for generating symmetric key for jwt
const { createSecretKey } = require('crypto');
// library for signing jwt
const { SignJWT } = require('jose-node-cjs-runtime/jwt/sign');
// library for verifying jwt
const { jwtVerify } = require('jose-node-cjs-runtime/jwt/verify');
Create Secret key of type KeyObject
KeyObject is recommended by Node.js for using when generating symmetric, asymmetric keys. Use following code to generate and store symmetric key object of type KeyObject in secretKey.
const secretKey = createSecretKey(process.env.JWT_SECRET, 'utf-8');
Replace process.env.JWT_SECRET with a sufficiently long string. It needs to be sufficiently long (use strings of length at least 32) otherwise there will be following error thrown when signing the JWT: HS256 requires symmetric keys to be 256 bits or larger
Sign the JWT
(async () => {
const token = await new SignJWT({ id: '12345' }) // details to encode in the token
.setProtectedHeader({ alg: 'HS256' }) // algorithm
.setIssuedAt()
.setIssuer(process.env.JWT_ISSUER) // issuer
.setAudience(process.env.JWT_AUDIENCE) // audience
.setExpirationTime(process.env.JWT_EXPIRATION_TIME) // token expiration time, e.g., "1 day"
.sign(secretKey); // secretKey generated from previous step
console.log(token); // log token to console
})();
Verify the JWT
We will use the same symmetric key stored in secretKey for verification purpose as well. Following code can be used to extract token from request header (in an Express app) and validate the token:
(async () => {
// extract token from request
const token = req.header('Authorization').replace('Bearer ', '');
try {
// verify token
const { payload, protectedHeader } = await jwtVerify(token, secretKey, {
issuer: process.env.JWT_ISSUER, // issuer
audience: process.env.JWT_AUDIENCE, // audience
});
// log values to console
console.log(payload);
console.log(protectedHeader);
} catch (e) {
// token verification failed
console.log("Token is invalid");
}
})();
By far the easiest way to generate the key material is to use generateKeyPair. The method is runtime agnostic and only requires a single argument - the Algorithm Identifier you wish to use the target key pair with. If you're bringing your own keys tho you must be aware of the different requirements for the key in order to be usable by the algorithm.
Not every runtime's crypto capabilities can support every algorithm, the list of available algorithms per runtime is available here.
Furthermore - importing SPKI/PKCS8 encoded key material is platform-specific and done through platform-specific APIs. The ways one can end up with KeyLike (type alias for CryptoKey (web), KeyObject (node), or Uint8Array (symmetric secrets) is documented in, well, KeyLike alias documentation linked from every function doc that uses it.
If you were to provide any actual reproduction code for your steps I would be happy to help.
The ‘sign’ method reports that the key must be a ‘KeyLike’, ‘CryptoKey’, or ‘Uint8Array’ type.
I'm fairly sure it says KeyObject at runtime, KeyLike is merely a type alias covering all the different types of inputs applicable for the different algorithms and runtimes.

How to decrypt pubnub messages in pubnub blocks

Im using pubnub encrypted messages https://www.pubnub.com/docs/javascript/api-reference-sdk-v4#init via cipher key between two clientes, now i start intercepting those messages with PubNub BLOCKS but can't find a way to decrypt them, i receive a long Base64 string and there is no tool to decrypt it via either the provided crypto module or the provided pubnub module, the block is super simple
export default (request) => {
console.log(request); // Log the request envelope passed when tested with a payload its shown when a real message goes through is a base64 string of an encrypted message
return request.ok();
}
Publishing Unencrypted Meta Data with Encrypted Message Paylaod
Currently, if you are using AES encryption the assumption is that you would want end to end encryption and not be able to decrypt the message in-flight by a BLOCK.
However if there is information you want to act on, you pass the information using the Meta Argument. This meta information is outside the message payload and not encrypted and therefore accessible by a BLOCK.
The PubNub PM team would love to hear more about your use case and why you would want to decrypt this message in flight to see if this is something we need to add to the roadmap. Please send a message to PubNub Support with more details.
Here is some sample code you can use to see the meta data in action with a block:
Publishing a message with meta data
The meta data portion of your message is never encrypted and it meant for data that you would use for filtering messages (and other use cases). If you are using an cipher key when you init PubNub, the message portion of the payload would be encrypted from end to end (not decrypted within PubNub since we do not know your cipher key). But the meta portion would remain clear text so that you can perform condition logic in a block based on these keys/values or for stream filtering on a per client basis.
pubnub.publish(
{
channel : "chmeta",
message : {"text": "hello"},
meta: {
"cool": "beans"
}
},
function(status, response) {
console.log(status, response);
}
);
Accessing meta data with PubNub BLOCKS
In your block code (Before or After Publish event handlers), you can access the meta key as follows:
export default (request) => {
console.log(JSON.parse(request.params.meta));
return request.ok();
}
The output of the entire request parameter would be quite verbose and I encourage you to review it as it will have lots of gems in there you might want to take advantage of, but just honing in on the meta key (request.params.meta) will give access to the meta data you provided in the publish. The JSON.parse is needed because the data will be stringified (escaped), {\"cool\": \"beans\"}, and this will transform it back into a real live JSON object, {"cool": "beans"}.
Okay, I did it myself. The code is ugly and I am open to any help with refactoring, but it works - it lets you decrypt messages in PubNub functions (blocks)
Here's the Gist - https://gist.github.com/DataGreed/f0007e7b5b8dcfadd8a44a5d3514b6dc
Don't forget to change the encryption key in getKey function.
PubNub Functions (formerly called BLOCKS) now has Crypto Module
I believe at the time you asked this question, the crypto module was not included with BLOCKS (we rebranded this as PubNub Functions). Now it is:
PubNub Functions Crypto Module Docs

Argon - Decryption Issues of Vuforia License

I am using the Argon 3 web browser with Vuforia's image tracking. I've taken the license key of my Vuforia application and encrypted it with the GPG Keychain. I've included relevant snippets of my index.html and app.js files here.
Whenever I load up my application, I get the error:
Vuforia: could not decrypt vuforia license JSON
Running it in the debugger shows:
Unexpected token VSyntaxError: Unexpected token V
at Object.parse(native)
...
index.html
<script id="license" type="text/plain">-----BEGIN PGP MESSAGE-----
Comment: GPGTools - https://gpgtools.org
hQIMA3/IreB2WlL1AQ//WZ4evbtnP39ycrb5Z8fa1U0ugbjGOfVA1h0nhgye3IhF
UMRcDF4nJ/+LKYzcePI7orjwjfedTKfX3oVrqb8focLoQvBKwG6bgRhAIr9oTwtO
uXXOVeZFo9iCnEicwGvGtdgUVv/EQHl/VIstLg3+aEtV3Xpjnlx7r2VbUp9L7iEp
mhaTLVpAcgWyqyPYN3QoEbBEtjdRKnHAogb268ZKEWSVjtcDo9NQCI/Lfb+3ghZS
mBVxr3d4Jtb8mcpOYeUfMilD5BuzelhciJb0PPFVdj/JmcVpDsFNNX/FvFNIlm0c
qH64s+ByHGiCGcQeAJx/ZAF9cTjBYDk3HZLlPWHOD7rA1roHLujN/yf3UpLkBFFn
jEj/MMO3KSWJUAXVs+vNpThgqwgIDPeuV9nKH5QeQORpKp3zOVVsXGGYxbVY7sDl
3sbXTYghhE5XM21t4/A/iFwJxB1ndqbhfiA/kdQwsKAb17OdBVzldQ1Wh/JouTFB
m1ETnviMDKZYw7a9yiavvCjjxJHedmQNPWJVJBiOeHvGZLOpdV47TZwiXLs6dsqJ
NxB0352AOw4v66nk6RMUwclAhiz4ll0xQIPTWQpjIjOhf7COK1jFXUs+PDS21MVq
1nwjDDAjKsfj4dxPbzJUGuQwaNqI/Jg7BqhVxo/uZxtvw9c+ERcHdMY9EnK6aMLS
wSABq1kaId5VF5ccHO999AKWrB9IIhpahlFRi3asU62Cz4DZ6XqbiTDTFpmX1ZG0
6JynBv4+H/SH45TPgsBMs6IMgWPrGxTmpipte6W2X98lf5ogzWnSOGv5J7BDYtLz
0GruiAjcIOpneDx4x+i5gh/0GjBIM1ZlaHhW3Gl+zxRj8X2vhoGXFg/qB5YKk05T
womuhvDCGbO0fO1oSlZP+1kjqfsyN8k67LyWwjVvuoGfwrv3WS4dwVTB6YlCKM6l
LH1GcHmkdBVTFHQsltzbeIllcJ2QLSqWRIfPeB0eIAMh9G8P8397I9fWznBGGIz6
lXlH3AF50+cwruWTpv0Jnjyd+n3wGc14UFgaEgjWFCK04OOaZVUuRuJG900VT2qV
Nh8y9KsdTMGkVfU25BA8k04Vi6IKDnl2vbQWnYCs5wsFfj/e2/5B4Ixx7ekjgi5/
vylNv8t12ollHapJ5zSx7KzyV77N3Gam6PrORovx6evIEe+fXnPJoZkMrNRbKv/Z
uH5DMZQU3CW+Dy4hS4VEmRSM5hFa5SM7GrDAAtxsBoY2oX0AJYKs1RyhzokQCN9O
bV1qkcfW+6kKLq3HbNsMJ45ya2mcXZNaXa+JDeRE22U0csrJSsU4M5us9wZc6XiM
DuQ=
=qaTl
-----END PGP MESSAGE-----
</script>
app.js
// Tell Argon that we need Vuforia for image tracking
Argon.immersiveContext.setRequiredCapabilities('Vuforia');
var encryptedData = document.getElementById("license").text;
// initialize Vuforia with our license key
Argon.Vuforia.initialize({
encryptedLicenseData: encryptedData,
startCamera: true,
}).then(function(api)
{
// load, activate, and use our dataSet
api.loadDataSetFromURL('./auburn_map.xml').then(function (dataSet)
{
dataSet.activate();
setupStreetCar(dataSet.trackables.streetcar);
}).then(api.startObjectTracker)
.then(api.hintMaxSimultaneousImageTargets.bind(api, 2));
});
I am unsure of what to do from here. Any help would be greatly appreciated.
Edit:
I encrypted the JSON file using the GPG keychain. I created a new keypair with the email "secure#argonjs.io", went to my JSON file, highlighted the text, selected that I wanted to encrypt and did NOT sign or encrypt with password.
How did you encrypt this JSON file? You need to encrypt with the secure#argonjs.io public key, and do not sign it or encrypt with a password.

Resources