I've implemented AES encryption with certain task-specific parameters using standard Java tools and BouncyCastle provider for specific AES algorithm.
Here is the code:
private byte[] aesEncryptedInfo(String info) throws UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidParameterSpecException, InvalidAlgorithmParameterException, NoSuchProviderException {
Security.addProvider(new BouncyCastleProvider());
SecretKey secret = new SecretKeySpec(CUSTOMLONGSECRETKEY.substring(0, 32).getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(VECTOR_SECRET_KEY.getBytes()));
return cipher.doFinal(info.getBytes("UTF-8"));
}
In some environments this code requires special policy files. See related question: InvalidKeyException Illegal key size
My goal is to reimplement it using third-party library, ideally I would use bouncy castle which is already used as provider. The library should have no restictions of standard java policy files. In other words there should be no restrictions.
Please suggest in your answers how to reimplement it using BouncyCastle or other third-party library which can work without restrictions mentioned. Ideally I would see the code :-)
Thank you very much for reading!
After a delay I now happy to post a solution. Hope that someone can benefit from it because Bouncy Castle documentation is not filled with a lot of examples :-)
private byte[] aesEncryptedInfo(String info)
// Creating AES/CBC/PKCS7Padding cipher with specified Secret Key and Initial Vector
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding());
cipher.init(true, new ParametersWithIV(new KeyParameter(CUSTOMLONGSECRETKEY.getBytes()), VECTOR_SECRET_KEY.getBytes()));
byte[] inputData = info.getBytes("UTF-8");
int outBlockSize = cipher.getOutputSize(inputData.length);
byte[] outputData = new byte[outBlockSize];
int outLength = cipher.processBytes(inputData, 0, inputData.length, outputData, 0);
outLength += cipher.doFinal(outputData, outLength);
if (outLength != outBlockSize) {
return Arrays.copyOf(outputData, outLength);
}
else {
return outputData;
}
}
By the way I found two differences between Java API and Bouncy Castle API:
1. Bouncy Castle uses composition of objects to create needed cipher. While Java API uses string to identify needed cipher.
2. BC encryption code slightly bigger, while Java API code is more compact.
The solution is full replacement for original Java API implementation - the proof is a custom unit test that I made.
Use the Bouncycastle lightweight crypto API directly, rather than through Java JCE interface. Bouncycastle includes its own crypto API accessible through various classes in org.bouncycastle.* packages. It also implements the JCE provider interface to make some of its crypto implementations available through standard JCE classes like Cipher, KeyGenerator, etc.
The cryptography policy restrictions are enforced by the JCE classes, not by bouncycastle. Therefore if you do not use these classes you'll will not encounter any restrictions. On the downside you will sacrifice some portability. To get started, take a look at the javadocs for the AESEngine class, and the rest of the javadocs for the bouncycastle.
Why isn't it possible to just add the necessary policy files?
That would be the easiest thing to do.
If you live in the US and you export your software to other (maybe "unallowed") countries, you will (theoretically) get trouble either way (including policy files/doing the encryption yourself).
If you live outside the US, why even bother about it, just include the policy files, no one cares.
No option for buying a toolkit? RSA BSAFE
Related
In my project I need to verify PGP clear signed signatures using a corresponding public key. While I did manage to find a code which does that (For example: https://github.com/cjmalloy/openbitpub/blob/64485d64a699eb6096f01b27d5f7e51dd726602f/src/main/java/com/cjmalloy/obp/server/pgp/PgpUtil.java), it operates on a low level and looks pretty horrible.
I was thinking, perhaps there exist some specialized parsers that can consume -----BEGIN PGP PUBLIC KEY BLOCK-----xxx-----END PGP PUBLIC KEY BLOCK----- and -----BEGIN PGP SIGNED MESSAGE-----xxx-----BEGIN PGP SIGNATURE-----xxx-----END PGP SIGNATURE----- blocks so I can check signatures in a more declarative way?
I've found related PEMReader class from bouncycastle.openssl package but nothing PGP-related so far.
I was thinking, perhaps there exist some specialized parsers that can consume -----BEGIN PGP PUBLIC KEY BLOCK-----xxx-----END PGP PUBLIC KEY BLOCK----- and -----BEGIN PGP SIGNED MESSAGE-----xxx-----BEGIN PGP SIGNATURE-----xxx-----END PGP SIGNATURE----- blocks so I can check signatures in a more declarative way?
A parser will not be enough at all -- you will need to implement lots of OpenPGP-specific functions like symmetric key derivation from strings (for encrypted keys), handling of different types of assymetric cryptography algorithms, hash sums, different kinds of packet nesting, ... -- at least you're not required to implement the OpenPGP CBC mode deriate as you don't require encryption (only signatures).
OpenPGP is much to complicated to write your own parser and crypto code, rely on existing libraries instead. In the end, with Java you've got two possible roads to follow:
Using GnuPG through GPGME's Java interface, which requires a local GnuPG installation.
Using Bouncy Castle for Java which has a pretty much complete OpenPGP implementation in native Java code, but will require you to perform all the crypto operations in Java. The documentation pretty much consists of the JavaDoc for the OpenPGP package.
I've found related PEMReader class from bouncycastle.openssl package but nothing PGP-related so far.
You probably looked in the wrong BouncyCastle package. OpenPGP does not use keys in PEM format (which belongs to the X.509 standard), so this class will not be useful at all.
I came through the same situation sometimes back.
This was resolved by using the bouncy castle dependency and by using the method
decryptAndVerify(InputStream in, OutputStream fOut, InputStream publicKeyIn, InputStream keyIn, char[] passwd)
in PGP util class
The commercial OpenPGP Library for Java offers a convenient API for verifying clear text signatures. Sample code is:
import com.didisoft.pgp.*;
public class VerifyFile {
public static void main(String[] args) throws Exception{
// create an instance of the library
PGPLib pgp = new PGPLib();
// verify and extract the signed content
SignatureCheckResult signatureCheck = pgp.verifyAndExtract("signed.pgp", "sender_public_key.asc", "OUTPUT.txt");
if (signatureCheck == SignatureCheckResult.SignatureVerified) {
System.out.println("The signature is valid.");
} else if (signatureCheck == SignatureCheckResult.SignatureBroken) {
System.out.println("Message corrupted or signature forged");
} else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) {
System.out.println("Signature not matching provided public key (the message is from another sender)");
} else {
System.out.println("No signature found in message");
}
}
}
Disclaimer: I work for DidiSoft.
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 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.
I'm attempting to make a XML-RPC call that requires HmacSHA-256 hashing of a particular string. I'm currently using the Jasypt library with the following code:
StandardPBEStringEncryptor sha256 = new StandardPBEStringEncryptor();
sha256.setPassword(key);
sha256.setAlgorithm("PBEWithHmacSHA2");
On trying to use sha256.encrypt(string) I get this error:
Exception in thread "main" org.jasypt.exceptions.EncryptionInitializationException: java.security.NoSuchAlgorithmException: PBEWithHmacAndSHA256 SecretKeyFactory not available
at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.initialize(StandardPBEByteEncryptor.java:597)
at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.initialize(StandardPBEStringEncryptor.java:488)
at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.encrypt(StandardPBEStringEncryptor.java:541)
at nysenateapi.XmlRpc.main(XmlRpc.java:52)
Caused by: java.security.NoSuchAlgorithmException: PBEWithHmacAndSHA256 SecretKeyFactory not available
at javax.crypto.SecretKeyFactory.(DashoA13*..)
at javax.crypto.SecretKeyFactory.getInstance(DashoA13*..)
at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.initialize(StandardPBEByteEncryptor.java:584)
... 3 more
I downloaded the JCE Cryptography extension and placed the jars in my buildpath, but that doesn't seem to have done anything. I've tried using a number of combinations in setAlgorithm above, including "PBE", "PBEWithSha"(1|2|128|256)?, "PBEWithHmacSha", etc.
I also tried using BouncyCastle but I didn't have any luck there either. Any help or guidance appreciated!
As correctly noted by #Rook you need to specify a PBE algorithm that includes an encryption algorithm. Two examples out of many are "PBEWithSHA1AndDESede" which is supported by the SunJCE provider and "PBEWITHSHA256AND128BITAES-CBC-BC" which is supported by the Bouncycastle JCE provider.
The comments were helpful but I guess I was asking the wrong question. What I was looking to do was mimic the PHP function hash_hmac('sha256',string,key)...
I ended up using the following code:
Mac mac = Mac.getInstance("HmacSha256");
SecretKeySpec secret = new SecretKeySpec(key.getBytes(), "HmacSha256");
mac.init(secret);
byte[] shaDigest = mac.doFinal(phrase.getBytes());
String hash = "";
for(byte b:shaDigest) {
hash += String.format("%02x",b);
}
Thanks for the guidance, though. Will surely help me in the future.