After struggling uncountable hours I finally have the current working code to generate CMS enveloped (RSA-OAEP / PKCS#1) data with recipient info using JCE/JCA:
String digest = "SHA-256";
String mgfDigest = "SHA-256";
// Data to encrypt
CMSTypedData msg = new CMSProcessableByteArray(data);
// Generator for my CMS enveloped data
CMSEnvelopedDataGenerator envelopedDataGen = new CMSEnvelopedDataGenerator();
// Recipient Info Stuff
JcaAlgorithmParametersConverter paramsConverter = new JcaAlgorithmParametersConverter();
OAEPParameterSpec oaepSpec = new OAEPParameterSpec(digest, "MGF1", new MGF1ParameterSpec(mgfDigest), PSource.PSpecified.DEFAULT);
AlgorithmIdentifier oaepAlgId = paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, oaepSpec);
envelopedDataGen.addRecipientInfoGenerator(
new JceKeyTransRecipientInfoGenerator(
getCert(),
oaepAlgId).setProvider("BC"));
/*
* Generate CMS-Data
* CMSOutputEncryptor is my own Class implementing OutputEncryptor
*/
CMSEnvelopedData ed = envelopedDataGen.generate(
msg,
new CMSOutputEncryptor());
byte[] encoded = ed.getEncoded();
This works as expected but because it uses JCE my customers need to install the unlimited strength api to use this code. I would prefer a way to overcome these needs because most of my customers fingers are thumbs...
Maybe someone can show me a piece of code which use a pure BouncyCastle way of doing the same so that one don't need to install the unlimited strength api?
Please note that I am not sure if this is legal in all countries / for all customers.
If you want to remove the restriction, you can work with some reflection magic. This is how I do it in my framework (Partially taken from: https://github.com/jruby/jruby/blob/0c345e1b186bd457ebd96143c0816abe93b18fdf/core/src/main/java/org/jruby/util/SecurityHelper.java):
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
public class UnlimitedStrengthHelper {
public static void removeCryptoStrengthRestriction() {
try {
if (Cipher.getMaxAllowedKeyLength("AES") < 256) {
Class jceSecurity = Class.forName("javax.crypto.JceSecurity");
Field isRestricted = jceSecurity.getDeclaredField("isRestricted");
if (Modifier.isFinal(isRestricted.getModifiers())) {
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(isRestricted, isRestricted.getModifiers() & ~Modifier.FINAL);
modifiers.setAccessible(false);
}
isRestricted.setAccessible(true);
isRestricted.setBoolean(null, false);
isRestricted.setAccessible(false);
}
} catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException
| NoSuchAlgorithmException | NoSuchFieldException | SecurityException ex) {
System.out.println("It is not possible to use unrestricted policy with this JDK, "
+ "consider reconfiguration: " + ex.getLocalizedMessage());
}
}
}
The code first checks whether the restriction exists (in that case, you cannot work with AES256). Afterwards, it takes the JceSecurity class and its isRestricted field. It ensures that we can access this field and finally sets its value to false.
Btw., thank you for your CMS example, it helped me a lot.