package org.spongycastle.cms; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; import java.security.Provider; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.crypto.KeyGenerator; import org.spongycastle.asn1.ASN1Encodable; import org.spongycastle.asn1.ASN1Set; import org.spongycastle.asn1.cms.KEKRecipientInfo; import org.spongycastle.asn1.cms.KeyAgreeRecipientInfo; import org.spongycastle.asn1.cms.KeyTransRecipientInfo; import org.spongycastle.asn1.cms.PasswordRecipientInfo; import org.spongycastle.asn1.cms.RecipientInfo; import org.spongycastle.asn1.x509.AlgorithmIdentifier; import org.spongycastle.operator.DigestCalculator; 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"); } 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; } } int getKeySize(String oid) { Integer keySize = (Integer)KEYSIZES.get(oid); if (keySize == null) { throw new IllegalArgumentException("no keysize for " + oid); } return keySize.intValue(); } private KeyGenerator createKeyGenerator( String algName, Provider provider) throws NoSuchAlgorithmException { if (provider != null) { return KeyGenerator.getInstance(algName, provider); } else { return KeyGenerator.getInstance(algName); } } 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) { ASN1Encodable 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 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 CMSReadable readable; CMSAuthenticatedSecureReadable(AlgorithmIdentifier algorithm, CMSReadable readable) { this.algorithm = algorithm; this.readable = readable; } public InputStream getInputStream() throws IOException, CMSException { return readable.getInputStream(); } } static class CMSEnvelopedSecureReadable implements CMSSecureReadable { private AlgorithmIdentifier algorithm; private CMSReadable readable; CMSEnvelopedSecureReadable(AlgorithmIdentifier algorithm, CMSReadable readable) { this.algorithm = algorithm; this.readable = readable; } public InputStream getInputStream() throws IOException, CMSException { return readable.getInputStream(); } } }