forge.js Replicate what I do with rsa but with aes - javascript

Im using the JavaScript library forge.js (https://github.com/digitalbazaar/forge)
rsa publicKey if 896 bits in length lets me encrypt a fare bit of text but, the length of the publicKey it's self is too long for my needs.
If I shorten it to a key of 460 bits then the length of the key is almost ok but, I then am limited to only encrypting a short amount.
I like the features of RSA (encrypt/decrypt & sign/verify) but I don't like the length of the key it's self and the limit on size.
Is there another form of encryption that would be better suited for my use?
needs:
A public key about 20 characters long
to be able to encrypt around 140 characters
same or similar features as RSA
I have been playing around with forge AES but it looks like the encryption is sort of a shared thing - You have your one key (that is shared?). then you can create a cypher and decypher. But I don't see how this would work similar to rsa as; with rsa I can share my publicKey and be safe to sign and decrypt but I don't see how I can do the same with my current understanding of aes.
How I currently would opperate:
//make sure that what user has said both hasn't been tampered with and is for you
var kp=forge.pki.rsa.generateKeyPair({bits: 896,e:0x10001});
var m=['Hi!'];
m[1]=kp.privateKey.sign(forge.md.sha1.create().update(m[0],'utf8'));
console.log(m);
console.log(kp.publicKey.verify(forge.md.sha1.create().update(m[0],'utf8').digest().bytes(),m[1]));
var asked='did you read this?';
var ask=kp.publicKey.encrypt(asked);
var read=kp.privateKey.decrypt(ask);
var r=['yes!'];
console.log('yes?');
r[1]=kp.privateKey.sign(forge.md.sha1.create().update(r[0],'utf8'));
r[2]=kp.privateKey.sign(forge.md.sha1.create().update(read,'utf8'));
console.log(kp.publicKey.verify(forge.md.sha1.create().update(asked,'utf8').digest().bytes(),r[2]));
Note: You would need do the decrypt and verify with try{}catch(e){} in production just incase the messages we're for a different users publicKey.
So far I can encrypt and decrypt REALLY basicly but I don't understand how to turn this form of cyrptography into what I am used to
//encypher the letter i then decypher it
var aes=forge.pkcs5.pbkdf2('k9','kr',1000,32);
var ci=forge.cipher.createCipher('AES-CBC',aes);
ci.start({'iv':'k5'});
ci.update(forge.util.createBuffer('i','utf8'));
ci.finish();
console.log(ci.output.toHex());
var ci=forge.cipher.createDecipher('AES-CBC',aes);
ci.start({'iv':'k5'});
ci.update(forge.util.createBuffer(forge.util.hexToBytes('7276131d61a323c37b5e451c3acc983e')));
ci.finish();
ci.output.toString('utf8')
//7276131d61a323c37b5e451c3acc983e
//"i"
// k9 kr k5 might as well just be me mashing the keyboard with my head btw

No, you can't use AES as a replacement of RSA, because as you already noted, then AES key must be shared.
A 460 bit RSA key doesn't provide (any) security nowadays. It can be brute-forced with a little bit of EC2 time. The recommendation would be to use at least 2048 bit RSA keys.
Since you want to sign stuff, you can use ECDSA which is based on Elliptic Curve Cryptography (ECC) and enables you to use much smaller keys for similar security (Some numbers). Forge doesn't provide ECC support yet.
SJCL provides an implementation of ECDSA
Add sjcl.js, core/bn.js, core/ecc.js
Generate keys var ecdsaKeys = sjcl.ecc.ecdsa.generateKeys(256);
ecdsaKeys.pub provides the verify() function
ecdsaKeys.sec provides the sign() function
If you need to send the public or secret key, then you need to serialize it yourself.
If you need encryption instead of signature, use ElGamal instead of ECDSA. SJCL also provides that. Since it is impossible to encrypt 140 characters with any size of ElGamal keys, you would need to opt in for a hybrid encryption with AES. SJCL also provides that with some authenticated modes like GCM or CCM.

Related

AES encryption option for offline usage

My objective is to perform data encryption/decryption locally for some data such as personal information and so on, instead of password, within a mobile app. Stumbled upon this library, and now considering two options which I can have
Option 1
Using user's password as secret passphrase, instead of hardcoding a passphrase, encryption and decryption key are "customized"
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
​
var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
Option 2 Generating a key based on user's password such as below
const CryptoJS = require("crypto-js");
const salt = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.PBKDF2("password", salt, {
keySize: 512 / 32,
iterations: 10000,
});
var encrypted = CryptoJS.AES.encrypt("Message", key.toString());
var decrypted = CryptoJS.AES.decrypt(encrypted, key.toString());
console.log(decrypted.toString(CryptoJS.enc.Utf8));
Given my use case, I'm wondering if there is any advantages of one option over another?
Neither of these two options can be recommended:
The first option should definitely not be used unless compatibility reasons enforce this.
The main reason for this is that the built-in key derivation applies the deprecated key derivation function EVP_BytesToKey(), which derives a 32 bytes key (AES-256) and a 16 bytes IV.
EVP_BytesToKey() and the pararmeters used for key derivation (MD5, iteration count of 1) are considered insecure today, s. e.g. here.
In addition, as already mentioned in the comment, EVP_BytesToKey() is a proprietary and non-standard implementation of OpenSSL, and is therefore not available on many platforms, which can be a problem in a cross-platform architecture.
The clearly more secure alternative is to apply a standardized key derivation function such as PBKDF2 to derive a key that is then used for encryption/decryption.
The second option uses PBKDF2 as key derivation function though, but performs a hexadecimal encoding of the key with key.toString(), i.e. converts the key to a string, with far-reaching consequences:
CryptoJS just uses the data type to interpret the second parameter as password or key. In case of a string the data is interpreted as password and the key derivation with EVP_BytesTokey() is performed (as for the first option), in case of a WordArray the data is interpreted as key which is directly applied.
The current implementation passes the key as string, i.e. the key derived with PBKDF2 is interpreted as password and the built-in key derivation with EVP_BytesTokey() is unnecessarily performed in addition to the key derivation with PBKDF2.
The correct approach:
Change the second option so that the key is passed as WordArray, i.e. key instead of key.toString().
This change has the consequence that salt and IV must be handled explicitly (in contrast to the built-in key derivation with EVP_BytesToKey(), where this happens under the hood):
The current code already generates a random salt, which is perfectly correct. Similarly, a random IV must be generated (CryptoJS uses CBC by default) and passed (s. here).
As an alternative to explicit generation with random(), the IV can be derived together with the key via PBKDF2.
Since salt and IV are needed for decryption and are not secret, both are usually concatenated with the ciphertext: salt|IV|ciphertext (if the IV is derived together with the key, of course only the salt needs to be concatenated). On the decryption side, the portions can be separated based on the known lengths of salt and IV.
Regarding the key size mentioned in the comment: All AES variants are considered secure today, even the smallest key size AES-128.
Whether AES-128, AES-192 or AES-256 should be used depends on the respective requirements. If the highest possible security is needed e.g. with regard to future quantum computers, AES-256 is probably the better choice. However, this requirement may not always exist. See e.g. also the post Why most people use 256 bit encryption instead of 128 bit?.
You should never hard code a passphrase, it turns cryptography into encoding.
The use of hashing instead of standard human readable text stings is preferable, especially as it adds what i assume are iteration hashing which increase workload against brute force attacks. 1000 iterations adds quite some time, 10k even more, but everything that consume CPU cycles need to be balanced against performance.
Also make sure that any encoding you use (like Utf8) when reading/writing data is explicitly set so you don't mix and match types, that can cause some problems.

Encrypting and decrypting data on client side using cryptojs

I am working on chrome extension to save some user data to local storage. I encode data with crypto-js.
However I am facing one minor issue/dilemma - if I encode user data using secret key abc123 I get something like this 2FsdGVkX19v0LNG0FKFv1SxAQj4AqdNvWWMGyi9yVI=. However if I decrypt it using another secret key like abc I get empty string. Only the correct secret key return non empty string.
So my question how do I need to encode data, if decoding with wrong key it would still return some string? Is there some configuration for this or different encoding?
If I am a hacker and I am using brute force to crack data it looks pretty obvious, what secret key user is using to encode data.
Fidller to fiddle with configuration.
Since you're using Crypto.js you're limited to popular and battle-tested algorithms. Let's say that you're using AES-256-CBC with a key derived from a password ("abc123").
If you encrypt something that a human uses then it likely has low entropy and therefore some structure to it. Think about some JSON string that you're encrypting. If the attacker tries to decrypt the ciphertext with some random key they might get random bytes. If they know they have to get JSON back, they have an oracle whether the decryption worked. They try again with a different key and get different random bytes. They repeat this until they find a plaintext that has a valid JSON structure. Even when they don't know that it is JSON, they might utilize statistical methods in order to deduce whether they got the right key.
You might need to use gzip before encryption in order to make that deduction harder but then the attacker might just incorporate an ungzip procedure in their bruteforcer and just do the statistical analysis.
AES is a block cipher where changing a single bit in the key changes roughly half the bits in the ciphertext with a constant plaintext. That means the attacker will not be able to see that they are getting closer to the correct key. They will have to try all of them.
The usual way of making it harder for an attacker is to increase the work factor. Crypto.js uses by default EVP_bytestokey with a single iteration of a MD5 hash of the password in order to produce the key. This operation is quite fast. You could change that to something like PBKDF2 with a million iterations but today's computers are so fast that this doesn't solve your problem...
The main issue is that your password is too short. "abc123" is a password that can be bruteforced in milliseconds when using Crypto.js defaults (1xMD5) and maybe minutes when using PBKDF2 with a million iterations. Adding a single character to a password multiplies the bruteforce effort by at least 50 (depending on class: upper, lower, digits, special).
Now you can calculate how long your password should be in order interfere with an attacker that corresponds to your risk appetite.
Note that just having encryption doesn't solve all your problems. Usually, the threat still exists.
You only can decrypt your data with the encryption key.
I personally wouldn't encrypt data in a frontend application.
Maybe you should have a look at the documentation:
https://cryptojs.gitbook.io/docs/

Performing AES Encryption is Ruby compared to Javascript

I am struggling at this one part of code where I need to decrypt AES on my server side (Ruby) from a client. I know all the information, but I am struggling with re-producing the encryption.
I am currently using CryptoJS from https://github.com/brix/crypto-js. I am not sure which version of AES it is using which might be my first problem.
An example of how I currently encrypt my data in Javascript is:
encodeURIComponent(CryptoJS.AES.encrypt("Message","Key").toString())
Right now I am currently using openssl and cgi in Ruby to try to decrypt. This is wrong and not working, but I wanted to show what I am trying as I believe it is close. I don't understand how the key is used in the encryption, but I am following the example I found here
require "openssl"
require "cgi"
cipher = OpenSSL::Cipher.new('AES-128-CBC')
cipher.encrypt
key = "Key"
iv = cipher.random_iv
encrypted = cipher.update("Message") + cipher.final
puts CGI::escape(encrypted.to_s)
I have just put "Message" and "Key" to not share my information, I am an amateur when it comes to security and cryptography, but I have done these things in lower level languages without problems. I believe the problem happens in two main areas
My lack of knowledge of how these high level languages work, and the libraries I am using
The strings are sometimes UTF-8 vs UTF-16 in these langauges, so passing the "Message" as a string might be causing problems
FULL EXAMPLE OF ENCRYPTION AND DECRYPTION IN JAVASCRIPT:
Encrypting and URL encoding with input 1:
encodeURIComponent(CryptoJS.AES.encrypt("1","Key").toString())
Result:
"U2FsdGVkX19Lp8ItQaO5h6Lj68sheHeYrIkJAfqt1Tw%3D"
Decoding URL and Decryption:
CryptoJS.AES.decrypt(decodeURIComponent("U2FsdGVkX19Lp8ItQaO5h6Lj68sheHeYrIkJAfqt1Tw%3D"), "Key").toString(CryptoJS.enc.Utf8)
Result:
"1"
At least one of your problems is noted by Artjom B noted in the comment above, and this is a frequent problem with trying to get crypto-js to interoperate with other libraries: crypto-js is not taking in a "Key" the way you are using it but instead a password. Passwords are not keys!!!
Internally, crypto-js uses a very poor algorithm for converting the password into a key. The algorithm is poor for two reasons: (1) it is based upon the insecure MD5, and (2) converting passwords to keys should be a slow process to deter brute force guessing of passwords. In crypto-js, the process is not slow.
To get you headed the right direction, do not call:
CryptoJS.AES.encrypt("Message","Password")
Instead, call
CryptoJS.AES.encrypt("Message", key, { iv: iv });
You might also need to explore padding to get it to interoperate with Ruby.

Web Crypto API: how to add passphrase to private key

We use window.crypto.subtle.generateKey to generate an RSA key pair.
How can we use the Web Crypto API to add a passphrase to the private key?
Keys generated with WebCrypto are not password-protected.
You could export the key and build a format that supports encryption, like pkcs8 in a PEM file, but to do this it would be needed to use an additional library.
I'm a bit late to the party, but isn't wrapKey() what you're looking for? It's on the same API and allows you to wrap the key up in a password-protected form, e.g. pkcs8.
#pedrofb is right. If you wanted to do this for better or worse PKCS8 is the right format, here is a link to code that can be used to create such a bag - https://github.com/PeculiarVentures/PKI.js/blob/5b9c35c154c48b232b45cc2a908c88e2f56a8447/src/PKCS8ShroudedKeyBag.js
You should probably also read this: http://unmitigatedrisk.com/?p=543 which describes some concepts related to key bags that are material.
You may also want to look into using webcrypto in a service worker and exposing an interface over PostMessage to get the operation to take place. This, combined with a non-exportable key, provides some protection from arbitrary use of the key, e.g the attacker can only do what the interface allows.

How to use public/private keys for short data without a symmetric cipher

I'd like to encrypt and decrypt data with private and public keys, respectively.
The data is between 1 and 32 bytes.
I do not want to use a symmetric cipher for the data - I want to use the keys on the data directly. (This is not normally done for performance reasons, but my data is very small.)
Is this possible and Where should I start?
I'd like to encrypt and decrypt data with private and public keys
Pretty much anyone who's worked with crypto will tell you that you write crypto at your own risk. Use the standard libraries, they're made with all the things that (if you are not aware of these techniques) leave your traffic unsecured.
That said, here's some Python libraries (but you can get this in most languages from PHP to Java or whatever floats your boat):
https://pypi.python.org/pypi/pycrypto
https://www.dlitz.net/software/pycrypto/
Crypto RSA in Python
https://pypi.python.org/pypi/PyECC
You'll see pycrypto because it's popular if you're looking to use RSA. Another package is PyECC which includes functionality for elliptic curve crypto. This is important if you're looking to reduce your key size or have less resources to devote to crypto, usually in mobile systems or something small. The difference in key sizes (link) can get ridiculous as the keys grow (for increased security)
Maybe keyczar is what you're looking for:
http://www.keyczar.org/

Categories

Resources