Define a key to generate keys - java

I'm making a project about security in Java. The Server receives a password and with this password is supposed to insert in MAC to verify the consistency of one specific file.
Mac m;
SecretKey sk;
sk = KeyGenerator.getInstance( "AES" ).keygenerator();/* what i don't want to use */
byte[]mac=null;
Mac m = Mac.getInstance("HmacSHA1");
m.init( password ); /* it's wrong */
m.update("work of security".getBytes());
mac = m.doFinal();
What I can understand is how to define a SecretKey to MAC init...

The two typical methods use to derive an AES key from a password are:
Using the raw bytes of the password. This option is not very strong (subject to trivial dictionary attacks) and relies on the password being exactly 128/192/256 bits.
Deriving the key using a function, such as PBKDF2.
You need to find out how the key is derived. The two options in code are:
Raw Bytes
SecretKey aesKey = new SecretKeySpec(password.getBytes(someCharset), "AES");
Derivation Function
Example PBKDF2:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey aesKey = factory.generateSecret(new PBEKeySpec(password, salt,
iterations, 256));
The salt value is a random byte array (i.e. perhaps eight bytes). The iterations can be increased to improve security at the expense of performance.

Related

Can the salt in the encryption key can be different for encryption and decryption?

I have a java application that encrypts and decrypts strings. It generates one key for encryption and one for decryption.
Key Generation:
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
char[] passwordChars = password.toCharArray();
KeySpec spec = new PBEKeySpec(passwordChars, salt.getBytes(), iterations, 256);
SecretKey key = keyFactory.generateSecret(spec);
byte[] passwordHash = key.getEncoded();
SecretKey secretKey = new SecretKeySpec(key.getEncoded(), "AES");
My goal is to have the password stay the same for both the encryption and decryption key. But have a different salt for each of them. Is this possible or do I need to have the same salt for both keys?
Whenever I try to change the salt for the decryption key, I get this error:
javax.crypto.AEADBadTagException: Tag mismatch!
at java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:623)
at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1118)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1055)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:855)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2207)
https://crackstation.net/hashing-security.htm - the salt should be different for each piece of encrypted information. I believe its often derivable from the user's other details... so dob + last name (for example) are used as the salt for encrypting passwords so that each password is encrypted to a different value, even when two users share the same password.
Edit: the salt should be the same for the encryption and decryption key, but unique for the individual passwords being encrypted/decrypted. Please read the information at the URL for a better understanding

AES GCM key derivation swift

I'm trying to implement in swift the equivalent of my code in java. Basically is an AES implementation with GCM padding and I'm using a key derivation for that. In swift I'm using the CryptoSwift library.
My issue is that I cannot get the same encrypted text in swift.
After a very long research I couldn't find any solutions for my problem, I even saw the test code of the CryptoSwift library repository to get any ideas but with no luck
This is my java code:
GCMParameterSpec ivParameterSpec = new GCMParameterSpec(128, "ivVector".getBytes());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec("myPassword".toCharArray(), "salt".getBytes(), 1000, 256);
SecretKey tmp = secretKeyFactory.generateSecret(keySpec);
key = new SecretKeySpec(tmp.getEncoded(), "AES");
encryptCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptCipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
byte[] encryptedWord = Base64.encode(encryptCipher.doFinal("example".getBytes("UTF-8")));
And this is my swift code:
do{
keyDerivation = try PKCS5.PBKDF2(password: "myPassword".bytes, salt: "salt".bytes, iterations: 1000, keyLength: 32, variant: .sha1).calculate()
let gcm = GCM(iv: keyDerivation, mode: .combined)
let aes = try AES(key: keyDerivation, blockMode: gcm, padding: .noPadding)
let encryptedText = try aes.encrypt("example".bytes)
}catch{
print(error)
}
Any help would be appreciated.
Your IV doesn't match in both cases. In Java you use a string, and in Swift you use the derived key in keyDerivation.
Furthermore, you should make sure that you use the same character encoding. I'd not use getBytes or similar for either language. Explicitly specifying UTF-8 is probably best.
Note that the Java PBKDF2WithHmacSHA1 may handle password encoding in a rather peculiar way, so some kind of input validation on the password is probably in order.
Needless to say, the salt should be random for each call to encrypt, not static. I presume this is just test code though.

How to convert crypto from java to nodejs

I have following code in Java.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(password.getBytes());
kgen.init(INIT_LENGTH, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(byteContent);
return result;
I need to implement it in JavaScript/Node.js
I don't know how to implement it in JavaScript from secretString to key generated by KeyGenerator
from the comment section:
This is my code:
function encodeDesCBC( textToEncode, keyString, ivString ) {
const key = Buffer.from(keyString.substring(0, 8), 'utf8')
const iv = Buffer.from(ivString.substring(0, 8), 'utf8')
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv)
let c = cipher.update(textToEncode, 'utf8', 'base64')
c += cipher.final('base64')
return base64url.escape(c)
}
My problem is secureRandom and KeyGenerator. I do not know how to implement it in nodejs
I don't know Java, but your usage looks somewhat weak, there are algorithms like PBKDF2 (which is old and discouraged now) and scrypt which do a much better job at turning human passwords into keying material. I'm also not sure where your IV is coming from in your Java code. Exactly replicating the Java code would be somewhat difficult as you'd need to know how your version of Java was implemented, and hence how the bytes passed to setSeed actually get turned into a key.
Node's Crypto module, as far as I can tell, assumes you know how long the keys are supposed to be. In the case of AES 128 in CBC mode, this would be 128 bits (i.e. 16 bytes) for both the key and IV.
Assuming you wanted to use things built into the Crypto module (argon2 would be recommended if you could relax this restriction) then you'd do something like:
const crypto = require('crypto');
const password = 'passw0rd';
const scrypt_salt = crypto.randomBytes(16);
const key = crypto.scryptSync(password, scrypt_salt, 16);
which would leave you with a suitable value in key, then you'd encrypt with:
const plaintext = 'the plain text to encode';
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
const ciphertext = Buffer.concat([
cipher.update(plaintext),
cipher.final(),
]);
and you could encode to Base64 with ciphertext.toString('base64').
Note that to be able to decrypt this, you'd need the scrypt_salt, iv, and ciphertext.
When you encrypt something using AES, there is always a "mode" in play. In your Java code you don't specify a mode explicitly - Cipher.getInstance("AES"). When you do not specify a mode, the default mode in Java is "ECB" which is the most insecured mode anyway.
In your NodeJs code, you're using "CBC" mode which is a altogether different mode.
Neither "ECB", nor "CBC" are considered secured enough. As of today, usually, the recommended mode is the GCM mode.
To generate a key from a password, ideally a "key derivation function" should be used. The 4 key derivation functions recommended by OWASP are: PBKDF2, Scrypt, Bcrypt and Argon2.
In your Java code, the password is used as a seed for the pseudo random number generator class SecureRandom. That's a little bit bizarre because even if you give the same password to your function, it will produce different key in different run. Yes, SecureRandom is also used to generate key. But if the requirement is to generate a key from a password, a key derivation function, as mentioned above, shoul be used. Both the approaches are shown in the following StackOverflow answer with detailed explanation. However, it uses "GCM" mode. But as long as you understand the concepts, you can use any mode of your choice.
https://stackoverflow.com/a/53015144/1235935
Similarly, you'll find the same implementation in NodeJs in the following StackOverflow answer:
https://stackoverflow.com/a/53573115/1235935
To further understand AES in general, you may want to go through the following StackOverflow answer:
https://stackoverflow.com/a/43779197/1235935

Secure key for android database

Is there any algorithm to generate an encrypted key in android to secure a database?
I tried this PBE algorithm:
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
NUM_OF_ITERATIONS, KEY_SIZE);
SecretKeyFactory factoryKey = SecretKeyFactory.getInstance(PBE_ALGORITHM);
SecretKey tempKey = factoryKey.generateSecret(pbeKeySpec);
SecretKey secretKey = new SecretKeySpec(tempKey.getEncoded(), "AES");
But it generates the same key every time. Any other good algorithms for generating a secure key?
To generate a random secret key, use the KeyGenerator class, with code something like this:
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(KEY_SIZE);
SecretKey skey = kgen.generateKey();
Note that you will obviously have to store this key securely somewhere if you wish to decrypt your database later, hence it may be worthwhile to pursue the PBE-based solution proposed in your question.
Typically to achieve what you want you use your PBE key to encrypt/decrypt a random key (that you must store, keep it separate from your data as best you can) which you use to encrypt/decrypt your data. Then your data ciphertext, by itself, has no direct relation to your password without the encrypted keys.

How to generate the same AES key in Java(Android) as in .Net?

I need to generate an AES key in Java (Android) from salt and password given from .Net WebService. I need to have the same key as the key generated in .net with the same password and salt (using Rfc2898DeriveBytes and AesManaged()).
Here is my code in Android:
char[] passwordAsCharArray = password.toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwordAsCharArray, salt, 1000, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKeySpec secretKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
Here is code in .net:
byte[] keyBytes = Encoding.Unicode.GetBytes(key);
Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(key, keyBytes);
AesManaged rijndaelCSP = new AesManaged();
rijndaelCSP.BlockSize = 128;
rijndaelCSP.KeySize = 256;
rijndaelCSP.Key = derivedKey.GetBytes(rijndaelCSP.KeySize / 8);
rijndaelCSP.IV = derivedKey.GetBytes(rijndaelCSP.BlockSize / 8);
ICryptoTransform decryptor = rijndaelCSP.CreateDecryptor();
When I compare both keys they are different. Any ideas how to generate on Android the same key as in .Net? (I know that the key which have been generated in .net is correct).
Number of iterations in .Net is 1000, salt and password are also the same as in Android.
Ok, it turned out that I dont need exactly the same key (as a byte array). I needed this to decrypt a file (in Java) which have been encrypted in .Net - with this key it gaves me Bad Padding Exception so I think the key was different and that causes the problem, but all I needed to do was to generate IV like a key - that solved my problem. Thanks for response!
It looks like you used the "key" (which should be a password) as a salt in your .NET code, whereas the Java part uses a specified salt. Furthermore, you specified the Unicode character set for decoding your salt, which is weird, the salt should be a random octet string (== byte array) from the beginning.
I would recommend you transform your password and random salt to byte arrays first, compare both using a hexadecimal representation (on your console, or in your debugger) and only then use those as input parameters for the PBKDF2 function in each. I would recommend an UTF-8 encoding for your password.
Always specify all parameters in cryptography, try not to use default, e.g. for the iteration count. If your input is off by a single bit, the output will be completely incorrect, and there is no way to tell which parameter was responsible.
It looks like the Java and .NET PBKDF2 "primitive" is identical on both platforms, there is working code out on the internet.

Categories