org.bouncycastle.cms.CMSEnvelopedHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcmail-jdk16 Show documentation
Show all versions of bcmail-jdk16 Show documentation
The Bouncy Castle Java CMS and S/MIME APIs for handling the CMS and S/MIME protocols. This jar contains CMS and S/MIME APIs for JDK 1.6. The APIs can be used in conjunction with a JCE/JCA provider such as the one provided with the Bouncy Castle Cryptography APIs. If the S/MIME API is used, the JavaMail API and the Java activation framework will also be needed.
The newest version!
package org.bouncycastle.cms;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.AlgorithmParameterGenerator;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.spec.InvalidParameterSpecException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import org.bouncycastle.asn1.ASN1Null;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.DEREncodable;
import org.bouncycastle.asn1.cms.KEKRecipientInfo;
import org.bouncycastle.asn1.cms.KeyAgreeRecipientInfo;
import org.bouncycastle.asn1.cms.KeyTransRecipientInfo;
import org.bouncycastle.asn1.cms.PasswordRecipientInfo;
import org.bouncycastle.asn1.cms.RecipientInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.util.io.TeeInputStream;
class CMSEnvelopedHelper
{
static final CMSEnvelopedHelper INSTANCE = new CMSEnvelopedHelper();
private static final Map KEYSIZES = new HashMap();
private static final Map BASE_CIPHER_NAMES = new HashMap();
private static final Map CIPHER_ALG_NAMES = new HashMap();
private static final Map MAC_ALG_NAMES = new HashMap();
static
{
KEYSIZES.put(CMSEnvelopedGenerator.DES_EDE3_CBC, new Integer(192));
KEYSIZES.put(CMSEnvelopedGenerator.AES128_CBC, new Integer(128));
KEYSIZES.put(CMSEnvelopedGenerator.AES192_CBC, new Integer(192));
KEYSIZES.put(CMSEnvelopedGenerator.AES256_CBC, new Integer(256));
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.DES_EDE3_CBC, "DESEDE");
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.AES128_CBC, "AES");
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.AES192_CBC, "AES");
BASE_CIPHER_NAMES.put(CMSEnvelopedGenerator.AES256_CBC, "AES");
CIPHER_ALG_NAMES.put(CMSEnvelopedGenerator.DES_EDE3_CBC, "DESEDE/CBC/PKCS5Padding");
CIPHER_ALG_NAMES.put(CMSEnvelopedGenerator.AES128_CBC, "AES/CBC/PKCS5Padding");
CIPHER_ALG_NAMES.put(CMSEnvelopedGenerator.AES192_CBC, "AES/CBC/PKCS5Padding");
CIPHER_ALG_NAMES.put(CMSEnvelopedGenerator.AES256_CBC, "AES/CBC/PKCS5Padding");
MAC_ALG_NAMES.put(CMSEnvelopedGenerator.DES_EDE3_CBC, "DESEDEMac");
MAC_ALG_NAMES.put(CMSEnvelopedGenerator.AES128_CBC, "AESMac");
MAC_ALG_NAMES.put(CMSEnvelopedGenerator.AES192_CBC, "AESMac");
MAC_ALG_NAMES.put(CMSEnvelopedGenerator.AES256_CBC, "AESMac");
}
String getAsymmetricEncryptionAlgName(
String encryptionAlgOID)
{
if (PKCSObjectIdentifiers.rsaEncryption.getId().equals(encryptionAlgOID))
{
return "RSA/ECB/PKCS1Padding";
}
return encryptionAlgOID;
}
Cipher createAsymmetricCipher(
String encryptionOid,
String provName)
throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException
{
String asymName = getAsymmetricEncryptionAlgName(encryptionOid);
if (!asymName.equals(encryptionOid))
{
try
{
// this is reversed as the Sun policy files now allow unlimited strength RSA
return Cipher.getInstance(asymName, provName);
}
catch (NoSuchAlgorithmException e)
{
// Ignore
}
}
return Cipher.getInstance(encryptionOid, provName);
}
Cipher createAsymmetricCipher(
String encryptionOid,
Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException
{
String asymName = getAsymmetricEncryptionAlgName(encryptionOid);
if (!asymName.equals(encryptionOid))
{
try
{
// this is reversed as the Sun policy files now allow unlimited strength RSA
return getCipherInstance(asymName, provider);
}
catch (NoSuchAlgorithmException e)
{
// Ignore
}
}
return getCipherInstance(encryptionOid, provider);
}
KeyGenerator createSymmetricKeyGenerator(
String encryptionOID,
Provider provider)
throws NoSuchAlgorithmException
{
try
{
return createKeyGenerator(encryptionOID, provider);
}
catch (NoSuchAlgorithmException e)
{
try
{
String algName = (String)BASE_CIPHER_NAMES.get(encryptionOID);
if (algName != null)
{
return createKeyGenerator(algName, provider);
}
}
catch (NoSuchAlgorithmException ex)
{
// ignore
}
if (provider != null)
{
return createSymmetricKeyGenerator(encryptionOID, null);
}
throw e;
}
}
AlgorithmParameters createAlgorithmParameters(
String encryptionOID,
Provider provider)
throws NoSuchAlgorithmException
{
try
{
return createAlgorithmParams(encryptionOID, provider);
}
catch (NoSuchAlgorithmException e)
{
try
{
String algName = (String)BASE_CIPHER_NAMES.get(encryptionOID);
if (algName != null)
{
return createAlgorithmParams(algName, provider);
}
}
catch (NoSuchAlgorithmException ex)
{
// ignore
}
//
// can't try with default provider here as parameters must be from the specified provider.
//
throw e;
}
}
AlgorithmParameterGenerator createAlgorithmParameterGenerator(
String encryptionOID,
Provider provider)
throws NoSuchAlgorithmException
{
try
{
return createAlgorithmParamsGenerator(encryptionOID, provider);
}
catch (NoSuchAlgorithmException e)
{
try
{
String algName = (String)BASE_CIPHER_NAMES.get(encryptionOID);
if (algName != null)
{
return createAlgorithmParamsGenerator(algName, provider);
}
}
catch (NoSuchAlgorithmException ex)
{
// ignore
}
//
// can't try with default provider here as parameters must be from the specified provider.
//
throw e;
}
}
String getRFC3211WrapperName(String oid)
{
String alg = (String)BASE_CIPHER_NAMES.get(oid);
if (alg == null)
{
throw new IllegalArgumentException("no name for " + oid);
}
return alg + "RFC3211Wrap";
}
int getKeySize(String oid)
{
Integer keySize = (Integer)KEYSIZES.get(oid);
if (keySize == null)
{
throw new IllegalArgumentException("no keysize for " + oid);
}
return keySize.intValue();
}
private Cipher getCipherInstance(
String algName,
Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException
{
if (provider != null)
{
return Cipher.getInstance(algName, provider);
}
else
{
return Cipher.getInstance(algName);
}
}
private AlgorithmParameters createAlgorithmParams(
String algName,
Provider provider)
throws NoSuchAlgorithmException
{
if (provider != null)
{
return AlgorithmParameters.getInstance(algName, provider);
}
else
{
return AlgorithmParameters.getInstance(algName);
}
}
private AlgorithmParameterGenerator createAlgorithmParamsGenerator(
String algName,
Provider provider)
throws NoSuchAlgorithmException
{
if (provider != null)
{
return AlgorithmParameterGenerator.getInstance(algName, provider);
}
else
{
return AlgorithmParameterGenerator.getInstance(algName);
}
}
private KeyGenerator createKeyGenerator(
String algName,
Provider provider)
throws NoSuchAlgorithmException
{
if (provider != null)
{
return KeyGenerator.getInstance(algName, provider);
}
else
{
return KeyGenerator.getInstance(algName);
}
}
Cipher createSymmetricCipher(String encryptionOID, Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException
{
try
{
return getCipherInstance(encryptionOID, provider);
}
catch (NoSuchAlgorithmException e)
{
String alternate = (String)CIPHER_ALG_NAMES.get(encryptionOID);
try
{
return getCipherInstance(alternate, provider);
}
catch (NoSuchAlgorithmException ex)
{
if (provider != null)
{
return createSymmetricCipher(encryptionOID, null); // roll back to default
}
throw e;
}
}
}
private Mac createMac(
String algName,
Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException
{
if (provider != null)
{
return Mac.getInstance(algName, provider);
}
else
{
return Mac.getInstance(algName);
}
}
Mac getMac(String macOID, Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException
{
try
{
return createMac(macOID, provider);
}
catch (NoSuchAlgorithmException e)
{
String alternate = (String)MAC_ALG_NAMES.get(macOID);
try
{
return createMac(alternate, provider);
}
catch (NoSuchAlgorithmException ex)
{
if (provider != null)
{
return getMac(macOID, null); // roll back to default
}
throw e;
}
}
}
AlgorithmParameters getEncryptionAlgorithmParameters(
String encOID,
byte[] encParams,
Provider provider)
throws CMSException
{
if (encParams == null)
{
return null;
}
try
{
AlgorithmParameters params = createAlgorithmParameters(encOID, provider);
params.init(encParams, "ASN.1");
return params;
}
catch (NoSuchAlgorithmException e)
{
throw new CMSException("can't find parameters for algorithm", e);
}
catch (IOException e)
{
throw new CMSException("can't find parse parameters", e);
}
}
String getSymmetricCipherName(String oid)
{
String algName = (String)BASE_CIPHER_NAMES.get(oid);
if (algName != null)
{
return algName;
}
return oid;
}
static RecipientInformationStore buildRecipientInformationStore(
ASN1Set recipientInfos, AlgorithmIdentifier messageAlgorithm, CMSSecureReadable secureReadable)
{
return buildRecipientInformationStore(recipientInfos, messageAlgorithm, secureReadable, null);
}
static RecipientInformationStore buildRecipientInformationStore(
ASN1Set recipientInfos, AlgorithmIdentifier messageAlgorithm, CMSSecureReadable secureReadable, AuthAttributesProvider additionalData)
{
List infos = new ArrayList();
for (int i = 0; i != recipientInfos.size(); i++)
{
RecipientInfo info = RecipientInfo.getInstance(recipientInfos.getObjectAt(i));
readRecipientInfo(infos, info, messageAlgorithm, secureReadable, additionalData);
}
return new RecipientInformationStore(infos);
}
private static void readRecipientInfo(
List infos, RecipientInfo info, AlgorithmIdentifier messageAlgorithm, CMSSecureReadable secureReadable, AuthAttributesProvider additionalData)
{
DEREncodable recipInfo = info.getInfo();
if (recipInfo instanceof KeyTransRecipientInfo)
{
infos.add(new KeyTransRecipientInformation(
(KeyTransRecipientInfo)recipInfo, messageAlgorithm, secureReadable, additionalData));
}
else if (recipInfo instanceof KEKRecipientInfo)
{
infos.add(new KEKRecipientInformation(
(KEKRecipientInfo)recipInfo, messageAlgorithm, secureReadable, additionalData));
}
else if (recipInfo instanceof KeyAgreeRecipientInfo)
{
KeyAgreeRecipientInformation.readRecipientInfo(infos,
(KeyAgreeRecipientInfo)recipInfo, messageAlgorithm, secureReadable, additionalData);
}
else if (recipInfo instanceof PasswordRecipientInfo)
{
infos.add(new PasswordRecipientInformation(
(PasswordRecipientInfo)recipInfo, messageAlgorithm, secureReadable, additionalData));
}
}
static class CMSDigestAuthenticatedSecureReadable
implements CMSSecureReadable
{
private DigestCalculator digestCalculator;
private CMSReadable readable;
public CMSDigestAuthenticatedSecureReadable(DigestCalculator digestCalculator, CMSReadable readable)
{
this.digestCalculator = digestCalculator;
this.readable = readable;
}
public AlgorithmIdentifier getAlgorithm()
{
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public Object getCryptoObject()
{
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public CMSReadable getReadable(SecretKey key, Provider provider)
throws CMSException
{
return null; //To change body of implemented methods use File | Settings | File Templates.
}
public InputStream getInputStream()
throws IOException, CMSException
{
return new FilterInputStream(readable.getInputStream())
{
public int read()
throws IOException
{
int b = in.read();
if (b >= 0)
{
digestCalculator.getOutputStream().write(b);
}
return b;
}
public int read(byte[] inBuf, int inOff, int inLen)
throws IOException
{
int n = in.read(inBuf, inOff, inLen);
if (n >= 0)
{
digestCalculator.getOutputStream().write(inBuf, inOff, n);
}
return n;
}
};
}
public byte[] getDigest()
{
return digestCalculator.getDigest();
}
}
static class CMSAuthenticatedSecureReadable implements CMSSecureReadable
{
private AlgorithmIdentifier algorithm;
private Mac mac;
private CMSReadable readable;
CMSAuthenticatedSecureReadable(AlgorithmIdentifier algorithm, CMSReadable readable)
{
this.algorithm = algorithm;
this.readable = readable;
}
public AlgorithmIdentifier getAlgorithm()
{
return this.algorithm;
}
public Object getCryptoObject()
{
return this.mac;
}
public InputStream getInputStream()
throws IOException, CMSException
{
return readable.getInputStream();
}
public CMSReadable getReadable(final SecretKey sKey, final Provider provider)
throws CMSException
{
final String macAlg = this.algorithm.getObjectId().getId();
final ASN1Object sParams = (ASN1Object)this.algorithm.getParameters();
this.mac = (Mac)execute(new JCECallback()
{
public Object doInJCE() throws CMSException, InvalidAlgorithmParameterException,
InvalidKeyException, InvalidParameterSpecException, NoSuchAlgorithmException,
NoSuchPaddingException
{
Mac mac = CMSEnvelopedHelper.INSTANCE.getMac(macAlg, provider);
if (sParams != null && !(sParams instanceof ASN1Null))
{
AlgorithmParameters params = CMSEnvelopedHelper.INSTANCE.createAlgorithmParameters(
macAlg, provider);
try
{
params.init(sParams.getEncoded(), "ASN.1");
}
catch (IOException e)
{
throw new CMSException("error decoding algorithm parameters.", e);
}
mac.init(sKey, params.getParameterSpec(IvParameterSpec.class));
}
else
{
mac.init(sKey);
}
return mac;
}
});
try
{
return new CMSProcessableInputStream(
new TeeInputStream(readable.getInputStream(), new MacOutputStream(this.mac)));
}
catch (IOException e)
{
throw new CMSException("error reading content.", e);
}
}
}
static class CMSEnvelopedSecureReadable implements CMSSecureReadable
{
private AlgorithmIdentifier algorithm;
private Cipher cipher;
private CMSReadable readable;
CMSEnvelopedSecureReadable(AlgorithmIdentifier algorithm, CMSReadable readable)
{
this.algorithm = algorithm;
this.readable = readable;
}
public AlgorithmIdentifier getAlgorithm()
{
return this.algorithm;
}
public InputStream getInputStream()
throws IOException, CMSException
{
return readable.getInputStream();
}
public Object getCryptoObject()
{
return this.cipher;
}
public CMSReadable getReadable(final SecretKey sKey, final Provider provider)
throws CMSException
{
final String encAlg = this.algorithm.getObjectId().getId();
final ASN1Object sParams = (ASN1Object)this.algorithm.getParameters();
this.cipher = (Cipher)execute(new JCECallback()
{
public Object doInJCE() throws CMSException, InvalidAlgorithmParameterException,
InvalidKeyException, InvalidParameterSpecException, NoSuchAlgorithmException,
NoSuchPaddingException
{
Cipher cipher = CMSEnvelopedHelper.INSTANCE.createSymmetricCipher(encAlg, provider);
if (sParams != null && !(sParams instanceof ASN1Null))
{
try
{
AlgorithmParameters params = CMSEnvelopedHelper.INSTANCE.createAlgorithmParameters(
encAlg, cipher.getProvider());
try
{
params.init(sParams.getEncoded(), "ASN.1");
}
catch (IOException e)
{
throw new CMSException("error decoding algorithm parameters.", e);
}
cipher.init(Cipher.DECRYPT_MODE, sKey, params);
}
catch (NoSuchAlgorithmException e)
{
if (encAlg.equals(CMSEnvelopedDataGenerator.DES_EDE3_CBC)
|| encAlg.equals(CMSEnvelopedDataGenerator.IDEA_CBC)
|| encAlg.equals(CMSEnvelopedDataGenerator.AES128_CBC)
|| encAlg.equals(CMSEnvelopedDataGenerator.AES192_CBC)
|| encAlg.equals(CMSEnvelopedDataGenerator.AES256_CBC))
{
cipher.init(Cipher.DECRYPT_MODE, sKey, new IvParameterSpec(
ASN1OctetString.getInstance(sParams).getOctets()));
}
else
{
throw e;
}
}
}
else
{
if (encAlg.equals(CMSEnvelopedDataGenerator.DES_EDE3_CBC)
|| encAlg.equals(CMSEnvelopedDataGenerator.IDEA_CBC)
|| encAlg.equals(CMSEnvelopedDataGenerator.CAST5_CBC))
{
cipher.init(Cipher.DECRYPT_MODE, sKey, new IvParameterSpec(new byte[8]));
}
else
{
cipher.init(Cipher.DECRYPT_MODE, sKey);
}
}
return cipher;
}
});
try
{
return new CMSProcessableInputStream(new CipherInputStream(readable.getInputStream(), cipher));
}
catch (IOException e)
{
throw new CMSException("error reading content.", e);
}
}
}
static Object execute(JCECallback callback) throws CMSException
{
try
{
return callback.doInJCE();
}
catch (NoSuchAlgorithmException e)
{
throw new CMSException("can't find algorithm.", e);
}
catch (InvalidKeyException e)
{
throw new CMSException("key invalid in message.", e);
}
catch (NoSuchPaddingException e)
{
throw new CMSException("required padding not supported.", e);
}
catch (InvalidAlgorithmParameterException e)
{
throw new CMSException("algorithm parameters invalid.", e);
}
catch (InvalidParameterSpecException e)
{
throw new CMSException("MAC algorithm parameter spec invalid.", e);
}
}
static interface JCECallback
{
Object doInJCE()
throws CMSException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidParameterSpecException,
NoSuchAlgorithmException, NoSuchPaddingException;
}
}