Skip to content

Commit cb995a4

Browse files
committed
Merge branch 'CMSAuthEnveloped' of https://github.com/MadMud/bc-java into MadMud-CMSAuthEnveloped
2 parents 0a42b59 + 74bde5f commit cb995a4

6 files changed

Lines changed: 168 additions & 25 deletions

File tree

core/src/main/java/org/bouncycastle/crypto/util/AlgorithmIdentifierFactory.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package org.bouncycastle.crypto.util;
22

3-
import java.security.SecureRandom;
4-
53
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
64
import org.bouncycastle.asn1.DERNull;
75
import org.bouncycastle.asn1.DEROctetString;
6+
import org.bouncycastle.asn1.cms.GCMParameters;
87
import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
98
import org.bouncycastle.asn1.misc.CAST5CBCParameters;
109
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
@@ -14,6 +13,8 @@
1413
import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
1514
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
1615

16+
import java.security.SecureRandom;
17+
1718
/**
1819
* Factory methods for common AlgorithmIdentifiers.
1920
*/
@@ -72,6 +73,15 @@ public static AlgorithmIdentifier generateEncryptionAlgID(ASN1ObjectIdentifier e
7273

7374
return new AlgorithmIdentifier(encryptionOID, new DEROctetString(iv));
7475
}
76+
else if(encryptionOID.equals(NISTObjectIdentifiers.id_aes128_GCM)
77+
|| encryptionOID.equals(NISTObjectIdentifiers.id_aes192_GCM)
78+
|| encryptionOID.equals(NISTObjectIdentifiers.id_aes256_GCM)){
79+
byte[] iv =new byte[16];
80+
81+
random.nextBytes(iv);
82+
83+
return new AlgorithmIdentifier(encryptionOID, new GCMParameters(iv, 12));
84+
}
7585
else if (encryptionOID.equals(PKCSObjectIdentifiers.des_EDE3_CBC)
7686
|| encryptionOID.equals(IDEA_CBC)
7787
|| encryptionOID.equals(OIWObjectIdentifiers.desCBC))

core/src/main/java/org/bouncycastle/crypto/util/CipherFactory.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package org.bouncycastle.crypto.util;
22

3-
import java.io.OutputStream;
4-
53
import org.bouncycastle.asn1.ASN1Null;
64
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
75
import org.bouncycastle.asn1.ASN1OctetString;
86
import org.bouncycastle.asn1.ASN1Primitive;
7+
import org.bouncycastle.asn1.cms.GCMParameters;
98
import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
109
import org.bouncycastle.asn1.misc.CAST5CBCParameters;
1110
import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
@@ -28,12 +27,16 @@
2827
import org.bouncycastle.crypto.io.CipherOutputStream;
2928
import org.bouncycastle.crypto.modes.AEADBlockCipher;
3029
import org.bouncycastle.crypto.modes.CBCBlockCipher;
30+
import org.bouncycastle.crypto.modes.GCMBlockCipher;
3131
import org.bouncycastle.crypto.paddings.PKCS7Padding;
3232
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
33+
import org.bouncycastle.crypto.params.AEADParameters;
3334
import org.bouncycastle.crypto.params.KeyParameter;
3435
import org.bouncycastle.crypto.params.ParametersWithIV;
3536
import org.bouncycastle.crypto.params.RC2Parameters;
3637

38+
import java.io.OutputStream;
39+
3740
/**
3841
* Factory methods for creating Cipher objects and CipherOutputStreams.
3942
*/
@@ -81,6 +84,19 @@ public static Object createContentCipher(boolean forEncryption, CipherParameters
8184

8285
return cipher;
8386
}
87+
else if(encAlg.equals(NISTObjectIdentifiers.id_aes128_GCM)
88+
|| encAlg.equals(NISTObjectIdentifiers.id_aes192_GCM)
89+
|| encAlg.equals(NISTObjectIdentifiers.id_aes256_GCM))
90+
{
91+
AEADBlockCipher cipher = createAEADCipher(encryptionAlgID.getAlgorithm());
92+
GCMParameters gcmParameters = GCMParameters.getInstance(encryptionAlgID.getParameters());
93+
if(!(encKey instanceof KeyParameter)){
94+
throw new IllegalArgumentException("key data must be accessible for GCM operation") ;
95+
}
96+
AEADParameters aeadParameters = new AEADParameters((KeyParameter) encKey, gcmParameters.getIcvLen() * 8, gcmParameters.getNonce());
97+
cipher.init(forEncryption, aeadParameters);
98+
return cipher;
99+
}
84100
else
85101
{
86102
BufferedBlockCipher cipher = createCipher(encryptionAlgID.getAlgorithm());
@@ -137,6 +153,19 @@ else if (encAlg.equals(PKCSObjectIdentifiers.RC2_CBC))
137153
}
138154
}
139155

156+
private static AEADBlockCipher createAEADCipher(ASN1ObjectIdentifier algorithm){
157+
if (NISTObjectIdentifiers.id_aes128_GCM.equals(algorithm)
158+
|| NISTObjectIdentifiers.id_aes192_GCM.equals(algorithm)
159+
|| NISTObjectIdentifiers.id_aes256_GCM.equals(algorithm))
160+
{
161+
return new GCMBlockCipher(new AESEngine());
162+
}
163+
else
164+
{
165+
throw new IllegalArgumentException("cannot recognise cipher: " + algorithm);
166+
}
167+
}
168+
140169
private static BufferedBlockCipher createCipher(ASN1ObjectIdentifier algorithm)
141170
throws IllegalArgumentException
142171
{

core/src/main/java/org/bouncycastle/crypto/util/CipherKeyGeneratorFactory.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.bouncycastle.crypto.util;
22

3-
import java.security.SecureRandom;
4-
53
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
64
import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
75
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
@@ -13,6 +11,8 @@
1311
import org.bouncycastle.crypto.generators.DESKeyGenerator;
1412
import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
1513

14+
import java.security.SecureRandom;
15+
1616
/**
1717
* Factory methods for generating secret key generators for symmetric ciphers.
1818
*/
@@ -45,6 +45,18 @@ else if (NISTObjectIdentifiers.id_aes256_CBC.equals(algorithm))
4545
{
4646
return createCipherKeyGenerator(random, 256);
4747
}
48+
else if (NISTObjectIdentifiers.id_aes128_GCM.equals(algorithm))
49+
{
50+
return createCipherKeyGenerator(random, 128);
51+
}
52+
else if (NISTObjectIdentifiers.id_aes192_GCM.equals(algorithm))
53+
{
54+
return createCipherKeyGenerator(random, 192);
55+
}
56+
else if (NISTObjectIdentifiers.id_aes256_GCM.equals(algorithm))
57+
{
58+
return createCipherKeyGenerator(random, 256);
59+
}
4860
else if (PKCSObjectIdentifiers.des_EDE3_CBC.equals(algorithm))
4961
{
5062
DESedeKeyGenerator keyGen = new DESedeKeyGenerator();

pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
package org.bouncycastle.cms.bc;
22

3-
import java.io.OutputStream;
4-
import java.security.SecureRandom;
5-
import java.util.HashMap;
6-
import java.util.Map;
7-
83
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
4+
import org.bouncycastle.asn1.DERSet;
5+
import org.bouncycastle.asn1.cms.AttributeTable;
96
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
107
import org.bouncycastle.cms.CMSAlgorithm;
8+
import org.bouncycastle.cms.CMSAttributeTableGenerator;
119
import org.bouncycastle.cms.CMSException;
1210
import org.bouncycastle.crypto.CipherKeyGenerator;
11+
import org.bouncycastle.crypto.modes.AEADBlockCipher;
1312
import org.bouncycastle.crypto.params.KeyParameter;
1413
import org.bouncycastle.crypto.util.CipherFactory;
1514
import org.bouncycastle.operator.GenericKey;
1615
import org.bouncycastle.operator.OutputEncryptor;
1716
import org.bouncycastle.util.Integers;
1817

18+
import java.io.IOException;
19+
import java.io.OutputStream;
20+
import java.security.SecureRandom;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
1924
public class BcCMSContentEncryptorBuilder
2025
{
2126
private static Map keySizes = new HashMap();
@@ -26,6 +31,10 @@ public class BcCMSContentEncryptorBuilder
2631
keySizes.put(CMSAlgorithm.AES192_CBC, Integers.valueOf(192));
2732
keySizes.put(CMSAlgorithm.AES256_CBC, Integers.valueOf(256));
2833

34+
keySizes.put(CMSAlgorithm.AES128_GCM, Integers.valueOf(128));
35+
keySizes.put(CMSAlgorithm.AES192_GCM, Integers.valueOf(192));
36+
keySizes.put(CMSAlgorithm.AES256_GCM, Integers.valueOf(256));
37+
2938
keySizes.put(CMSAlgorithm.CAMELLIA128_CBC, Integers.valueOf(128));
3039
keySizes.put(CMSAlgorithm.CAMELLIA192_CBC, Integers.valueOf(192));
3140
keySizes.put(CMSAlgorithm.CAMELLIA256_CBC, Integers.valueOf(256));
@@ -70,6 +79,9 @@ public BcCMSContentEncryptorBuilder setSecureRandom(SecureRandom random)
7079
public OutputEncryptor build()
7180
throws CMSException
7281
{
82+
if(helper.isAuthEnveloped(encryptionOID)){
83+
return new CMSAuthOutputEncryptor(encryptionOID, keySize, random);
84+
}
7385
return new CMSOutputEncryptor(encryptionOID, keySize, random);
7486
}
7587

@@ -78,7 +90,7 @@ private class CMSOutputEncryptor
7890
{
7991
private KeyParameter encKey;
8092
private AlgorithmIdentifier algorithmIdentifier;
81-
private Object cipher;
93+
protected Object cipher;
8294

8395
CMSOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
8496
throws CMSException
@@ -112,4 +124,42 @@ public GenericKey getKey()
112124
return new GenericKey(algorithmIdentifier, encKey.getKey());
113125
}
114126
}
127+
128+
public interface MacProvider{
129+
byte[] addAuthenticatedAttributes(AttributeTable authAttributes) throws IOException;
130+
byte[] getMac();
131+
}
132+
private class CMSAuthOutputEncryptor extends CMSOutputEncryptor implements MacProvider {
133+
CMSAttributeTableGenerator authenticatedAttributeGenerator;
134+
135+
CMSAuthOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
136+
throws CMSException
137+
{
138+
super(encryptionOID, keySize, random);
139+
//check the cipher is able to produce authenticated results.
140+
getCipher();
141+
}
142+
143+
private AEADBlockCipher getCipher()
144+
{
145+
if(!(cipher instanceof AEADBlockCipher)){
146+
throw new IllegalArgumentException("Unable to create Authenticated Output Encryptor without Authenticaed Data cipher!") ;
147+
}
148+
return (AEADBlockCipher)cipher;
149+
}
150+
151+
byte[] aad = null;
152+
public byte[] addAuthenticatedAttributes(AttributeTable authAttributes) throws IOException {
153+
aad = new DERSet(authAttributes.toASN1EncodableVector())
154+
.toASN1Primitive().getEncoded();
155+
return aad;
156+
}
157+
158+
public byte[] getMac() {
159+
if(aad != null){
160+
getCipher().processAADBytes(aad, 0, aad.length);
161+
}
162+
return getCipher().getMac();
163+
}
164+
}
115165
}

pkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package org.bouncycastle.cms.bc;
22

3-
import java.security.SecureRandom;
4-
import java.util.Collections;
5-
import java.util.HashMap;
6-
import java.util.Map;
7-
83
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
94
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
105
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
@@ -33,6 +28,11 @@
3328
import org.bouncycastle.operator.OperatorCreationException;
3429
import org.bouncycastle.operator.bc.BcDigestProvider;
3530

31+
import java.security.SecureRandom;
32+
import java.util.Collections;
33+
import java.util.HashMap;
34+
import java.util.Map;
35+
3636
class EnvelopedDataHelper
3737
{
3838
protected static final Map BASE_CIPHER_NAMES = new HashMap();
@@ -172,4 +172,10 @@ CipherKeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm, SecureRand
172172
throw new CMSException(e.getMessage(), e);
173173
}
174174
}
175+
176+
boolean isAuthEnveloped(ASN1ObjectIdentifier algorithm) {
177+
return NISTObjectIdentifiers.id_aes128_GCM.equals(algorithm)
178+
|| NISTObjectIdentifiers.id_aes192_GCM.equals(algorithm)
179+
|| NISTObjectIdentifiers.id_aes256_GCM.equals(algorithm);
180+
}
175181
}

pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
11
package org.bouncycastle.cms.test;
22

3-
import java.security.KeyFactory;
4-
import java.security.KeyPair;
5-
import java.security.PrivateKey;
6-
import java.security.Security;
7-
import java.security.cert.X509Certificate;
8-
import java.security.spec.PKCS8EncodedKeySpec;
9-
10-
import junit.framework.Assert;
113
import junit.framework.Test;
124
import junit.framework.TestCase;
135
import junit.framework.TestSuite;
6+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
7+
import org.bouncycastle.asn1.ASN1Set;
8+
import org.bouncycastle.asn1.DERSet;
9+
import org.bouncycastle.asn1.cms.Attribute;
10+
import org.bouncycastle.asn1.cms.AttributeTable;
11+
import org.bouncycastle.asn1.cms.CMSAttributes;
12+
import org.bouncycastle.asn1.cms.GCMParameters;
13+
import org.bouncycastle.asn1.cms.Time;
14+
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
1415
import org.bouncycastle.cms.CMSAuthEnvelopedData;
16+
import org.bouncycastle.cms.CMSException;
1517
import org.bouncycastle.cms.RecipientInformation;
1618
import org.bouncycastle.cms.RecipientInformationStore;
19+
import org.bouncycastle.cms.bc.BcCMSContentEncryptorBuilder;
1720
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
1821
import org.bouncycastle.jce.provider.BouncyCastleProvider;
22+
import org.bouncycastle.operator.OutputEncryptor;
1923
import org.bouncycastle.util.Strings;
2024
import org.bouncycastle.util.encoders.Base64;
25+
import org.junit.Assert;
26+
27+
import java.io.IOException;
28+
import java.security.KeyFactory;
29+
import java.security.KeyPair;
30+
import java.security.PrivateKey;
31+
import java.security.Security;
32+
import java.security.cert.CertificateEncodingException;
33+
import java.security.cert.X509Certificate;
34+
import java.security.spec.PKCS8EncodedKeySpec;
35+
import java.util.Date;
36+
import java.util.Hashtable;
2137

2238
public class AuthEnvelopedDataTest
2339
extends TestCase
@@ -131,4 +147,24 @@ public void testSample1()
131147

132148
Assert.assertEquals("auth-enveloped data", Strings.fromByteArray(recData));
133149
}
150+
151+
public void testAttributes() throws CertificateEncodingException, IOException, CMSException {
152+
OutputEncryptor candidate = new BcCMSContentEncryptorBuilder(NISTObjectIdentifiers.id_aes128_GCM).build();
153+
154+
Assert.assertEquals(NISTObjectIdentifiers.id_aes128_GCM, candidate.getAlgorithmIdentifier().getAlgorithm());
155+
Assert.assertNotNull(GCMParameters.getInstance(candidate.getAlgorithmIdentifier().getParameters()));
156+
157+
Assert.assertTrue( candidate instanceof BcCMSContentEncryptorBuilder.MacProvider);
158+
BcCMSContentEncryptorBuilder.MacProvider macProvider = ((BcCMSContentEncryptorBuilder.MacProvider) candidate);
159+
160+
Hashtable<ASN1ObjectIdentifier, Attribute> attrs = new Hashtable<ASN1ObjectIdentifier, Attribute>();
161+
Attribute testAttr = new Attribute(CMSAttributes.signingTime,
162+
new DERSet(new Time(new Date())));
163+
attrs.put(testAttr.getAttrType(), testAttr);
164+
AttributeTable table = new AttributeTable(attrs);
165+
166+
byte[] encodedAuthAttributes = macProvider.addAuthenticatedAttributes(table);
167+
Assert.assertEquals(table.size(),ASN1Set.getInstance(encodedAuthAttributes).size());
168+
169+
}
134170
}

0 commit comments

Comments
 (0)