1 package org.bouncycastle.cms.bc; 2 3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.security.SecureRandom; 6 7 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 8 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; 9 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 10 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 11 import org.bouncycastle.cms.CMSException; 12 import org.bouncycastle.crypto.CipherKeyGenerator; 13 import org.bouncycastle.crypto.modes.AEADBlockCipher; 14 import org.bouncycastle.crypto.params.KeyParameter; 15 import org.bouncycastle.crypto.util.CipherFactory; 16 import org.bouncycastle.operator.DefaultSecretKeySizeProvider; 17 import org.bouncycastle.operator.GenericKey; 18 import org.bouncycastle.operator.MacCaptureStream; 19 import org.bouncycastle.operator.OutputAEADEncryptor; 20 import org.bouncycastle.operator.OutputEncryptor; 21 import org.bouncycastle.operator.SecretKeySizeProvider; 22 23 public class BcCMSContentEncryptorBuilder 24 { 25 private static final SecretKeySizeProvider KEY_SIZE_PROVIDER = DefaultSecretKeySizeProvider.INSTANCE; 26 27 private final ASN1ObjectIdentifier encryptionOID; 28 private final int keySize; 29 30 private EnvelopedDataHelper helper = new EnvelopedDataHelper(); 31 private SecureRandom random; 32 BcCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)33 public BcCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID) 34 { 35 this(encryptionOID, KEY_SIZE_PROVIDER.getKeySize(encryptionOID)); 36 } 37 BcCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)38 public BcCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize) 39 { 40 this.encryptionOID = encryptionOID; 41 int fixedSize = KEY_SIZE_PROVIDER.getKeySize(encryptionOID); 42 43 if (encryptionOID.equals(PKCSObjectIdentifiers.des_EDE3_CBC)) 44 { 45 if (keySize != 168 && keySize != fixedSize) 46 { 47 throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder."); 48 } 49 this.keySize = 168; 50 } 51 else if (encryptionOID.equals(OIWObjectIdentifiers.desCBC)) 52 { 53 if (keySize != 56 && keySize != fixedSize) 54 { 55 throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder."); 56 } 57 this.keySize = 56; 58 } 59 else 60 { 61 if (fixedSize > 0 && fixedSize != keySize) 62 { 63 throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder."); 64 } 65 this.keySize = keySize; 66 } 67 } 68 setSecureRandom(SecureRandom random)69 public BcCMSContentEncryptorBuilder setSecureRandom(SecureRandom random) 70 { 71 this.random = random; 72 73 return this; 74 } 75 build()76 public OutputEncryptor build() 77 throws CMSException 78 { 79 if (helper.isAuthEnveloped(encryptionOID)) 80 { 81 return new CMSAuthOutputEncryptor(encryptionOID, keySize, random); 82 } 83 return new CMSOutputEncryptor(encryptionOID, keySize, random); 84 } 85 86 private class CMSOutputEncryptor 87 implements OutputEncryptor 88 { 89 private KeyParameter encKey; 90 private AlgorithmIdentifier algorithmIdentifier; 91 protected Object cipher; 92 CMSOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)93 CMSOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random) 94 throws CMSException 95 { 96 if (random == null) 97 { 98 random = new SecureRandom(); 99 } 100 101 CipherKeyGenerator keyGen = helper.createKeyGenerator(encryptionOID, keySize, random); 102 103 encKey = new KeyParameter(keyGen.generateKey()); 104 105 algorithmIdentifier = helper.generateEncryptionAlgID(encryptionOID, encKey, random); 106 107 cipher = EnvelopedDataHelper.createContentCipher(true, encKey, algorithmIdentifier); 108 } 109 getAlgorithmIdentifier()110 public AlgorithmIdentifier getAlgorithmIdentifier() 111 { 112 return algorithmIdentifier; 113 } 114 getOutputStream(OutputStream dOut)115 public OutputStream getOutputStream(OutputStream dOut) 116 { 117 return CipherFactory.createOutputStream(dOut, cipher); 118 } 119 getKey()120 public GenericKey getKey() 121 { 122 return new GenericKey(algorithmIdentifier, encKey.getKey()); 123 } 124 } 125 126 private class CMSAuthOutputEncryptor 127 extends CMSOutputEncryptor 128 implements OutputAEADEncryptor 129 { 130 private AEADBlockCipher aeadCipher; 131 private MacCaptureStream macOut; 132 CMSAuthOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)133 CMSAuthOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random) 134 throws CMSException 135 { 136 super(encryptionOID, keySize, random); 137 138 aeadCipher = getCipher(); 139 } 140 getCipher()141 private AEADBlockCipher getCipher() 142 { 143 if (!(cipher instanceof AEADBlockCipher)) 144 { 145 throw new IllegalArgumentException("Unable to create Authenticated Output Encryptor without Authenticaed Data cipher!"); 146 } 147 return (AEADBlockCipher)cipher; 148 } 149 getOutputStream(OutputStream dOut)150 public OutputStream getOutputStream(OutputStream dOut) 151 { 152 macOut = new MacCaptureStream(dOut, aeadCipher.getMac().length); 153 return CipherFactory.createOutputStream(macOut, cipher); 154 } 155 getAADStream()156 public OutputStream getAADStream() 157 { 158 return new AADStream(aeadCipher); 159 } 160 getMAC()161 public byte[] getMAC() 162 { 163 return macOut.getMac(); 164 } 165 } 166 167 private static class AADStream 168 extends OutputStream 169 { 170 private AEADBlockCipher cipher; 171 AADStream(AEADBlockCipher cipher)172 public AADStream(AEADBlockCipher cipher) 173 { 174 this.cipher = cipher; 175 } 176 write(byte[] buf, int off, int len)177 public void write(byte[] buf, int off, int len) 178 throws IOException 179 { 180 cipher.processAADBytes(buf, off, len); 181 } 182 write(int b)183 public void write(int b) 184 throws IOException 185 { 186 cipher.processAADByte((byte)b); 187 } 188 } 189 } 190