I have written some (functional) AES encryption code using Java's built in encryption libraries, as follows, but I'd like to use a 256-bit key. However, I'd like to do this without the user having to install to Unlimited Strength Cryptography Policy files.
Now, I've heard that using the BouncyCastle Lightweight API can allow me to do this, but unfortunately I'm having a great deal of trouble getting my head around it, and am struggling to fit any documentation that helps me.
Here is a my current code, in which 'content' is the byte array to be encrypted:
KeyGenerator kgen = KeyGenerator.getInstance("AES");
int keySize = 128;
kgen.init(keySize);
SecretKey key = kgen.generateKey();
byte[] aesKey = key.getEncoded();
SecretKeySpec aesKeySpec = new SecretKeySpec(aesKey, "AES");
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, aesKeySpec);
byte[] encryptedContent = aesCipher.doFinal(content);
How would I go about re-implementing this with the BouncyCastle Lightweight API? Can anyone help me out and/or point me in the direction of some simple example code?
I'm also interesting in any other solutions that allow 256-bit key AES encryption without the need for the user to install the unlimited strength policy files.
Many thanks!
This question and answer is a useful starting point.
256bit AES/CBC/PKCS5Padding with Bouncy Castle
The next best place to look is the test code for the LW APIs and then the JCE Provider code. The JCE Provider code is a wrapper around the LW libraries - so if you want to know how to do it, that's the best place to see it.
By the JCE Provider code, I mean the BC implementation.
Related
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
I want to generate Random key using AES/ECB/PKCS7PADDING but java does not support PKCS7 padding and in my algo I have to use the same padding, bouncy castle does support PKCS7PADDING but I am unable to understand how to generate key using that
My code :
Security.addProvider(new BouncyCastleProvider());
private byte[] generateSessionKey() throws NoSuchAlgorithmException, NoSuchProviderException {
KeyGenerator kgen = KeyGenerator.getInstance("AES/ECB/PKCS7PADDING", "BC");
kgen.init(SYMMETRIC_KEY_SIZE);
SecretKey key = kgen.generateKey();
return key.getEncoded();
}
I am receiving error of no such algorithm
Key generation for AES does not have to do anything with padding. Padding is required for some modes of operation such as ECB and CBC. But the key itself doesn't rely on the mode or the padding. So you should just use:
KeyGenerator kgen = KeyGenerator.getInstance("AES", "BC");
which will undoubtedly work.
Do not use PKCS#7 padding, substitute "PKCS5Padding" for "PKCS7Padding".
Please take a look here to understand the difference between the padding mechanisms and here for the difference in Java.
I guess Android is using a stripped down Bouncy Castle provider (it was one of the reasons that SpongyCastle was lounged), which would explain the differences between Android and the Java SE provider.
I'm migrating my native Android game to libGDX. So I can't access the Android libraries anymore and I'm trying to replace android.util.Base64 by org.apache.commons.codec.binary.Base64. (I need Base64's encodeToString and decode methods.)
Unfortunately, with the new package I get this error:
java.security.InvalidKeyException: Illegal key size (using the same 24-character-key as I did before).
Here at stackoverflow they say it's probably because "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7" are missing. But if I use them, the users of my app have to install them, too.
Is there any easier solution? Why did it work before?
EDIT:
This is the code that leads to the InvalidKeyException:
javax.crypto.Cipher writer = Cipher.getInstance("AES/CBC/PKCS5Padding");
String keyOf24Chars = "abcdefghijklmnopqrstuvwx";
IvParameterSpec ivSpec = getIv();
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.reset();
byte[] keyBytes = md.digest(keyOf24Chars.getBytes("UTF-8"));
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES/CBC/PKCS5Padding");
// secretKey.getAlgorithm(): "AES/CBC/PKCS5Padding"
// secretKey.getFormat(): "RAW"
// secretKey.getEncoded().length: 32
writer.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); // java.security.InvalidKeyException: Illegal key size
EDIT 2:
As explained in Maarten Bodewes' comment, Android has it's own implementation of the java and javax classes which apparently have no problem with 32 byte keys. After I have installed the "JCE Unlimited Strength Jurisdiction Policy Files 7" we are coming to the code that uses Base64 and causes this error: java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.encodeToString:
String valueToEncode = "xyz";
byte[] secureValue;
try {
secureValue = writer.doFinal(valueToEncode.getBytes("UTF-8"));
} catch (Exception e) {
throw new SecurePreferencesException(e);
}
Base64 base64 = new Base64();
String secureValueEncoded = base64.encodeToString(secureValue);
But this method does exist (in BaseNCodec which Base64 extends):
public String encodeToString(final byte[] pArray) {
return StringUtils.newStringUtf8(encode(pArray));
}
How can I make Android use this method?
EDIT 3:
Finally I solved my problem by writing an interface and then using my old Android code (when compiling for Android). Check this example for libGDX: Interfacing with platform specific code.
No, there isn't an easier solution. You could use 3DES instead of AES (which I presume you are using) but you would be downgrading your security, and still be incompatible with the previous code. Downgrading security of AES to 128 is a better idea, but the incompatibility issue won't go away.
If you are not using the encryption/decryption in a third party library (e.g. JSSE for SSL or XML encryption) then you could directly use the Bouncy Castle or Spongy Castle API's. So that means directly using AESBlockCipher + a mode of encryption. Bouncy Castle doesn't have these kind of limitations - they are part of the Oracle Cipher implementation.
It was working before because Android doesn't have these kind of restrictions while Java 7/8 SE does.
I'm implementing encryption code in Java/Android to match iOS encryption. In iOS there are encrypting with RSA using the following padding scheme: PKCS1-OAEP
However when I try to create Cipher with PKCS1-OAEP.
Cipher c = Cipher.getInstance("RSA/None/PKCS1-OAEP", "BC");
Below is the stacktrace
javax.crypto.NoSuchPaddingException: PKCS1-OAEP unavailable with RSA.
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineSetPadding(CipherSpi.java:240)
at javax.crypto.Cipher.getCipher(Cipher.java:324)
at javax.crypto.Cipher.getInstance(Cipher.java:237)
Maybe this RSA/None/PKCS1-OAEP is incorrect? but can't find any definitive answer to say either PKCS1-OAEP is unsupported or the correct way to define it.
I'm using the spongycastle library so have full bouncycastle implementation.
The code in the first answer does work, but it's not recommended as it uses BouncyCastle internal classes, instead of JCA generic interfaces, making the code BouncyCastle specific. For example, it will make it difficult to switch to SunJCE provider.
Bouncy Castle as of version 1.50 supports following OAEP padding names.
RSA/NONE/OAEPWithMD5AndMGF1Padding
RSA/NONE/OAEPWithSHA1AndMGF1Padding
RSA/NONE/OAEPWithSHA224AndMGF1Padding
RSA/NONE/OAEPWithSHA256AndMGF1Padding
RSA/NONE/OAEPWithSHA384AndMGF1Padding
RSA/NONE/OAEPWithSHA512AndMGF1Padding
Then proper RSA-OAEP cipher initializations would look like
Cipher c = Cipher.getInstance("RSA/NONE/OAEPWithSHA1AndMGF1Padding", "BC");
The following code works, if anyone else is stuck with similar encryption encoding/padding issues
SubjectPublicKeyInfo publicKeyInfo = new SubjectPublicKeyInfo(
ASN1Sequence.getInstance(rsaPublicKey.getEncoded()));
AsymmetricKeyParameter param = PublicKeyFactory
.createKey(publicKeyInfo);
AsymmetricBlockCipher cipher = new OAEPEncoding(new RSAEngine(),
new SHA1Digest());
cipher.init(true, param);
return cipher.processBlock(stuffIWantEncrypted, 0, 32);
I am looking for any Android java AES 256 or AES 512 crypto library which is compatible with NDK openSSL library in C/C++ source code example. I want to encrypt in Java and pass it to C and decrypt there and vice versa depending on our application scenario. Please help me out with any example . I tried myself with the following code in java and c .but it is not working .
my java side is :
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES/CBC/PKCS5Padding");
Cipher cipher = Cipher.getInstance("AES")
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
On NDK side i am using
EVP_BytesToKey(
EVP_aes_256_cbc(),
EVP_sha1(), // SHA1
Salt, // 0
KeyData,
KeyLength,
Rounds, // 0
aes_key, aes_iv); // return buffers
Appreciate any responses ....
Bouncy castle is the option. Used it working fine.