Cannot get bouncycaste cipher instance - java

I cannot seem to get a bouncy castle Cipher to load an instance.
I printed out all the algorithms and all the providers, and part of the list is as follows:
RSA/ECB/PKCS1Padding : [SunMSCAPI version 1.7]
RSA/ISO9796-1 : [BC version 1.46]
RSA/OAEP : [BC version 1.46]
RSA/PKCS1 : [BC version 1.46]
According to examples I have seen, this should work
try
{
Cipher cipherb = Cipher.getInstance("RSA/PKCS1", "BC");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
...
}
catch(Exception e)
{
e.printStackTrace();
return ;
}
But I get an exception on the the call to Cipher.getInstance
java.security.NoSuchAlgorithmException:
Invalid transformation format:RSA/PKCS1
However, RSA/PKCS1 is in the list. The only cipher I can get an instance of is the Sun one RSA/ECB/PKCS1Padding, which is not compatible with where my data to be decrypted is coming from. And I can't figure out why this does not work.
Any suggestions greatly appreciated.

The first parameter for Cipher.getInstance(..)is the transformation that have the form:
"algorithm/mode/padding" or
"algorithm"
According to bouncycastle's specification RSA + PKCS1 have the form: "RSA/NONE/PKCS1Padding" in BouncyCastle
This is as Maarten hinted in his comment, the same as what you get with the SUN provider using "RSA/ECB/PKCS1Padding"

Related

Decryption of AES-CBC-256 in Java

I need to encrypt a value in Java using AES CBC 256 but I am unable to decrypt it in NodeJS:
The cyphered string is generated with the following code:
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String result = DatatypeConverter.printHexBinary(cipher.doFinal(test));
and decrypted with this one:
var crypto = require('crypto');
var mykey = crypto.createDecipher('aes-256-cbc', key);
var mystr = mykey.update(value, 'hex', 'utf8')
but the code returns an error:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
at Decipher.final (internal/crypto/cipher.js:104:26)
at evalmachine.<anonymous>:12:16
at Script.runInContext (vm.js:74:29)
at Object.runInContext (vm.js:182:6)
at evaluate (/run_dir/repl.js:133:14)
at ReadStream.<anonymous> (/run_dir/repl.js:116:5)
at ReadStream.emit (events.js:180:13)
at addChunk (_stream_readable.js:274:12)
at readableAddChunk (_stream_readable.js:261:11)
at ReadStream.Readable.push (_stream_readable.js:218:10)
I assume it has something to do with the padding strategy but I can not find any documentation on the one applied by NodeJS.
What is the equivalent padding in Java Cypher corresponging to the one provided by the cripto package in NodeJS?
CBC mode requires an Initialization Vector (IV) to be pressent when encryptin the first block. In the Java implementation of AES you can provide this when you Initialize the Cipher, or you can let Java generate one for you. In your case Java have genereted an IV for you. As this is needed when you decrypt, you need to grab this and pass it along with the encrypted data (this ought to be random, but don't need to be kept secret). Use the Cipher.getIV() methode to get it.
In NodeJS there is a variant of the crypto.createDecipher() where you can provide this IV called crypto.createDecipheriv()

How to replace android.util.Base64 by org.apache.commons.codec.binary.Base64?

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.

SecretKeyFactory doesn't support algorithm "PBEWithHmacSHA256AndDESede"

I'm working on Java 1.7 - Window 7 (64 bit). I want to get SecretKeyFactory instance and Cipher instance of PBEWithHmacSHA256AndDESede algorithm. Unfortunately, I only got exceptions
java.security.NoSuchAlgorithmException: Cannot find any provider supporting PBEWithHmacSHA256AndDESede
at javax.crypto.Cipher.getInstance(Cipher.java:524)
Can you share me your solution of this problem?
The solution to this problem is two-part:
First, you need to call SecretKeyFactory.getInstance("PBEWithHmacSHA256AndDESede") instead of Cipher.getInstance("PBEWithHmacSHA256AndDESede"), as indicated by your stacktrace.
Second, you need to find the security provider that supports that algorithm. Neither SunJCE no BC 1.50 support it, by the way, so you will have to search for more exotic variants.
You can use the following code to check all installed providers and SecretKeyFactory algorithms supported by them:
// Security.addProvider( new BouncyCastleProvider() );
for ( Provider provider : Security.getProviders() ) {
System.out.println( provider );
for ( Provider.Service service : provider.getServices() ) {
if ( "SecretKeyFactory".equals( service.getType() ) ) {
System.out.println( service );
}
}
}
BC 1.50 definitely supports PBE with SHA-256 and AES variants under the names PBEWITHSHA256AND128BITAES-CBC-BC (OID 1.3.6.1.4.1.22554.1.2.1.2.1.2), PBEWITHSHA256AND192BITAES-CBC-BC (OID 1.3.6.1.4.1.22554.1.2.1.2.1.22) and PBEWITHSHA256AND256BITAES-CBC-BC (OID 1.3.6.1.4.1.22554.1.2.1.2.1.42).
It also supports a SecretKeyFactory algorithm with the name PBEWITHHMACSHA256 and OID 2.16.840.1.101.3.4.2.1, but that OID designates the plain SHA-256 hash function, meaning that this secret factory will use only hash instead of hash+cipher.
Have you:
Confirmed that security provider you are using supports 'PBEWithHmacSHA256AndDESede'
Have you installed the unlimited crypto -> http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
If the current security provider does not support 'PBEWithHmacSHA256AndDESede' use https://www.bouncycastle.org/ and be sure to add it as a security provider.
As #user3465651 points out, you have to have a library that supports this, such as BouncyCastle. In my case the error was:
java.security.NoSuchAlgorithmException: PBEWithSHA256And256BitAES-CBC-BC SecretKeyFactory not available
In troubleshooting, you can explicitly specify the library that contains the algorithm this way:
SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC","BC");
If you've done that you may still get this error if you're using Maven's shade plugin:
java.security.NoSuchProviderException: JCE cannot authenticate the provider BC
The shade plugin breaks the the cryptographic signature (explanation).
You can instead use the executable packer maven plugin solution that uses a jar-in-jar approach which preserves the signature for JCE in a single, executable jar.
Try this:
Old code:
String passphrase = "test";
KeySpec keySpec = new PBEKeySpec(passphrase.toCharArray());
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
...
ecipher.init(Cipher.ENCRYPT_MODE, ket, paramSpec);
...
New Code :
String passphrase = "test";
KeySpec keySpec = new PBEKeySpec(passphrase.toCharArray());
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
ecipher = Cipher.getInstance("PBEWithMD5AndDES");
...
ecipher.init(Cipher.ENCRYPT_MODE, ket, paramSpec);

What is the default initialization vector behavior for the Java implementation of AES?

We have a client who is using the Java code below to create a cipher before transmitting us some data:
SecretKeySpec localSecretKeySpec = new SecretKeySpec(arrayOfByte, "AES");
Cipher localCipher = Cipher.getInstance("AES");
We're trying to understand how the Initialization Vector is generated for the above when only a key is specified.
The Sun provider defaults to AES/ECB/PKCS5Padding when given the "AES" transformation. ECB mode does not use an initialization vector. Note that ECB is not considered secure.

Is RSA PKCS1-OAEP padding supported in bouncycastle?

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);

Categories