Generate AES key on node - java

I'm dealing with a legacy application that uses a custom protocol to cipher communication. Random AES keys are generated in legacy Java app like this:
keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
keygen.generateKey().getEncoded();
I've been looking for solutions on crypto with no luck. How can I generate this key on nodejs?

That code probably does not do as much as you think. It simply generates 16 (128 / 8) secure random bytes, then wraps a key object around it. So with nodejs, you simply generate 16 bytes and feed the algorithm the raw key data.
If you want to use the generated key, then make sure you create a binary encoded string or buffer from the bytes returned by the getEncoded() method. You could use hexadecimal encoding/decoding if you require the key to be a textual string somewhere in the process.
See randomBytes() and createCipheriv() for information.
AES keys are just cryptographically strong random bytes, DES (parity bits) and RSA (prime number calculation) keys are not.

Related

Encryption with PBEWithHmacSHA256AndAES_128 in java

I use PBEWithHmacSHA256AndAES_128 in java for encryption and my question is about the algorithm internals.
I understand the roles of PBE, Hmac, and aes in encryption process.
My question is: does the Hmac digest the plaintext and then gets appended to the plaintext and get encrypted by aes (as in SSL)? because the result is always a multiple of aes 16 bytes (if i encrypt only 1 character i get 16 bytes as the final result) but Hmac is 32 bytes.
When i read about Hmac i found that the most used way is to digest the ciphertext and append the result to the ciphertext and save it, but it seems not the case in PBEWithHmacSHA256AndAES_128.
I am asking this because i don't want to save the password and rely on Hmac's result to check for password's validity, and also to understand better the algorithm.
(I sometimes calculate Hmac separately again by using PBKDF2WithHmacSHA1 with different salt and count for secret key generation and append the result to the final ciphertext for more security.)

How to derive an AES 256 bit key from an arbitary byte array it Java?

Given an arbitrary Java byte array for example 1024 byte array I would like to derive an AES-256 bit key. The array is generated from ECHD via javax.crypto.KeyAgreement using byte[] secret = keyAgreement.generateSecret()
My current solution is to treat the input byte array as a password. Use the PBKDF2 key derivation function the input array as both the password and the salt as shown below.
UPDATE: I have set UTF-8 as the encoding to address issues pointed out in comments and answers.
private byte[] deriveAes256bitKey(byte[] secret)
throws NoSuchAlgorithmException, InvalidKeySpecException {
var secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
var password = new String(secret, UTF_8).toCharArray();
var keySpec = new PBEKeySpec(password, secret, 1024, 256);
return secretKeyFactory.generateSecret(keySpec).getEncoded();
}
Is there a better way to take a byte array in Java and turn it into an AES-256 bit key?
I would be wary of using new String(input).toCharArray() to create the password. It's not portable (it uses the platform default encoding), and its behaviour is undefined if there are invalid character sequences in the input.
Consider this:
System.out.println(new String(new byte[] {(byte) 0xf0, (byte) 0x82, (byte) 0x82, (byte) 0xac}, StandardCharsets.UTF_8));
f08282ac is an over long encoding of the Euro sign (€). It's decoded to the replacement character (�; 0xfffd) because it's an illegal sequence. All illegal UTF-8 sequences will end up as the replacement char, which is not what you want.
You could avoid decoding problems by serialising the byte array before passing it to the SecretKeyFactory (base64 encode it, or simply new BigInteger(input).toString(Character.MAX_RADIX)). However, this can be avoided if you don't use the SecretKeyFactory. It's unnecessary.
PBKDF2 (Password-Based Key Derivation Function 2) is designed to make brute force attacks against user supplied passwords harder by being computationally expensive and adding salt.
You don't need that here (your input is large and random; nobody will be mounting dictionary attacks against it). Your problem is just that the input length doesn't match the required key length.
You can just hash the input to the correct length:
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] keyBytes = md.digest(input);
What is required here is a KBKDF or Key Based Key Derivation Function. A KBKDF converts a secret value that contains enough entropy into a different key of a specific size. A PBKDF is used when you have a passphrase with potentially too little entropy into a key using key strenghtening (using the salt and work factor or iteration count). The work factor / iteration count doesn't need to be used if the input value is already strong enough not to be guessed / brute forced.
SHA-256 in general suffices if you only want a resulting 128 bit value. However, using a key derivation function may still offer benefits. First of all, it is a function that is explicitly defined for the function, so it is easier to prove that it is secure. Furthermore, it is generally possible to add additional data to the key derivation function so that you can e.g. derive more keys or a key and an IV. Or you can expand the configurable output size to output enough data for different keys or key / IV.
That said, most cryptographers won't frown too much if you use SHA-256 (or SHA-512 in case you require more bits for key / IV). The output is still supposed to be randomized using all possible bits from the input, and it is impossible to inverse the function.

Signature RSA for data with length as key length

I have to create a signature from given data, but the problem is that data has length as key length. All implementation of RSA Signers in JAVA allow to put data of max. key length minus one length. How to create signature for data with length as key length? Algorithm is basic RSA without padding and without digest.
Firstly, please don't use RSA without padding - it's a terrible idea and diminishes the security of your system.
If you need to sign data that is equal in size to your key length (or larger), I'm afraid you'll need to use a digest. There's not really a choice here. I'm not sure why you wouldn't want to use a digest anyway.
RSA is only able to encrypt data to a maximum amount of your key size (2048 bits = 256 bytes) minus padding / header data (11 bytes for PKCS#1 v1.5 padding).
As a result it is often not possible to encrypt files with RSA directly. RSA is also not meant for this purpose. If you want to encrypt more data, you can use something like:
Generate a 256-bit random keystring K
Encrypt your data with AES-CBC with K
Encrypt K with RSA
Send both to the other side

Encrypted data length is very large with RSA

I want that encrypted data have the same length as my original text, because I must respect the constraint of length. I am using BouncyCastle.
Here's my code to generate the keys pair:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(512);
KeyPair keypair = keyGen.genKeyPair();
PublicKey pub = keypair.getPublic();
byte[] pubs = pub.getEncoded();
And here's my code to encrypt:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
PublicKey pk = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(pubs));
cipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] cipherBytes = cipher.doFinal(plainArray);
The encoded data is very large, what can I do to make as small as original data ?
Nothing, at least not regarding the RSA. RSA requires a certain amount of padding to be secure, and as the data will be seen as random data by any compression method, you cannot compress it either.
Of course, you should not directly encrypt data directly using RSA, you should encrypt a random session/data key instead. But even then, you will have the encrypted session key as overhead.
You can remove some bits for the asymmetric encrypted data/key by using Elliptic Curve Cryptography (the output of which is double the key size minimum, but key sizes are much smaller to achieve the same level of security). EC cryptography is not for the meek though, it has lots of complexity.
512 bit RSA is considered insecure by the way. Use 1024 as minimum or keep to the ECRYPT II or NIST recommendations listed at http://www.keylength.com/ .
If you can keep the key secret, you can use a symmetric cryptosystem like AES. If used in CFB mode, it can be adapted to any bit length. I.e. #bits input & output is identical.
RSA and ElGamal -- the main public-key cryptosystems-- can be broken in a matter of hours even at 512 bits as you are selecting. 1024 - 4096 bits is normal. Less than 512 bits is worthless unless your opponent is limited to using 1990s hardware. :-)
The nature of RSA is such that you use exponentiation in encryption and decryption.
Basically, for public key e and private key d, you take a message m and then you get a compressed message equal to c=m^e and mod it by n. Then you decrypt with m=c^d and again mod it by n (mod means take the remainder).
If you think about it, a message to the power of something results in a larger number. Because you do a mod n, you end up with a number that is at most n-1. n is the key length.
So basically, whatever message you take, you encrypt to something as large as n. The message has to be less than n.
But the message has to be turned into an integer using a padding scheme (so you can do the exponentiation). This padding scheme may require less than n bits. And so, you end up with larger files than the size of the data being encrypted. Also, the last chunk may be less than n bits, but will encrypt into size n.

continued encryption with diffie-hellman shared key

I'm currently working on a protocol, which uses Diffie-Hellman for an key exchange.
I receive a packet, which consists of an aes-128 encrypted part and a 128 Bit DH Public Key.
In a very last step in the protocol, the aes key is sent to another peer in the network.
This aes-key should be encrypted with a cipher using a 128 bit strong secretkey.
I plan to use Blowfish (can also be another cipher, doesn't really matter for the problem)
Now to encrypt the aes-key, with lets say blowfish, I have to build a secretkey for the encryption with a class called SecretKeySpec (I'm using javax.crypto stuff), which takes an byteArray in the constructor to build the secretKey.
The sharedkey from DH is a 128 Bit BigInteger.
Well, now I can interpret my shared-key as a byteArray (wich gives me still 128 Bit in 16Bytes [where the numbers are interpreted as frames of 8 Bit data])
So my question is, how strong is my key really?
Is there any impact because I only use numbers as input for the byteArray (so does this limit the keyspace in any way?)
I think this is not the case, but I'm not 100% sure.
Maybe someone can do the math and proof me right or wrong.
If I'm wrong what keysize for the shared key give me piece of mind to finally get to the 128Bit SecretKey for the encryption?
The Crypto++ website suggests using a minimum p of 3072 bits (or 256 bits for an ECC implementation) to transport a 128 bit AES key.
You might wish to study the references provided at http://www.keylength.com/en/compare/ for further information about comparing key lengths among different algorithms.
Not an expert in DH here, but to me it seems that DH's keyspace for the shared key represented in n bits is somewhat smaller than 2^n.

Categories

Resources