1 package org.bouncycastle.cert.crmf.jcajce;
2 
3 import java.io.IOException;
4 import java.security.AlgorithmParameterGenerator;
5 import java.security.AlgorithmParameters;
6 import java.security.GeneralSecurityException;
7 import java.security.InvalidAlgorithmParameterException;
8 import java.security.InvalidKeyException;
9 import java.security.Key;
10 import java.security.KeyFactory;
11 import java.security.MessageDigest;
12 import java.security.NoSuchAlgorithmException;
13 import java.security.NoSuchProviderException;
14 import java.security.PublicKey;
15 import java.security.SecureRandom;
16 import java.security.spec.InvalidKeySpecException;
17 import java.security.spec.InvalidParameterSpecException;
18 import java.security.spec.X509EncodedKeySpec;
19 import java.util.HashMap;
20 import java.util.Map;
21 
22 import javax.crypto.Cipher;
23 import javax.crypto.KeyGenerator;
24 import javax.crypto.Mac;
25 import javax.crypto.NoSuchPaddingException;
26 import javax.crypto.SecretKey;
27 import javax.crypto.spec.IvParameterSpec;
28 import javax.crypto.spec.RC2ParameterSpec;
29 
30 import org.bouncycastle.asn1.ASN1Encodable;
31 import org.bouncycastle.asn1.ASN1Null;
32 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
33 import org.bouncycastle.asn1.ASN1OctetString;
34 import org.bouncycastle.asn1.ASN1Primitive;
35 import org.bouncycastle.asn1.DERBitString;
36 import org.bouncycastle.asn1.DERNull;
37 import org.bouncycastle.asn1.iana.IANAObjectIdentifiers;
38 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
39 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
40 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
41 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
42 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
43 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
44 import org.bouncycastle.cert.crmf.CRMFException;
45 import org.bouncycastle.cms.CMSAlgorithm;
46 import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
47 import org.bouncycastle.jcajce.util.JcaJceHelper;
48 
49 class CRMFHelper
50 {
51     protected static final Map BASE_CIPHER_NAMES = new HashMap();
52     protected static final Map CIPHER_ALG_NAMES = new HashMap();
53     protected static final Map DIGEST_ALG_NAMES = new HashMap();
54     protected static final Map KEY_ALG_NAMES = new HashMap();
55     protected static final Map MAC_ALG_NAMES = new HashMap();
56 
57     static
58     {
BASE_CIPHER_NAMES.put(PKCSObjectIdentifiers.des_EDE3_CBC, R)59         BASE_CIPHER_NAMES.put(PKCSObjectIdentifiers.des_EDE3_CBC,  "DESEDE");
BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes128_CBC, R)60         BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes128_CBC,  "AES");
BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes192_CBC, R)61         BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes192_CBC,  "AES");
BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes256_CBC, R)62         BASE_CIPHER_NAMES.put(NISTObjectIdentifiers.id_aes256_CBC,  "AES");
63 
CIPHER_ALG_NAMES.put(CMSAlgorithm.DES_EDE3_CBC, R)64         CIPHER_ALG_NAMES.put(CMSAlgorithm.DES_EDE3_CBC,  "DESEDE/CBC/PKCS5Padding");
CIPHER_ALG_NAMES.put(CMSAlgorithm.AES128_CBC, R)65         CIPHER_ALG_NAMES.put(CMSAlgorithm.AES128_CBC,  "AES/CBC/PKCS5Padding");
CIPHER_ALG_NAMES.put(CMSAlgorithm.AES192_CBC, R)66         CIPHER_ALG_NAMES.put(CMSAlgorithm.AES192_CBC,  "AES/CBC/PKCS5Padding");
CIPHER_ALG_NAMES.put(CMSAlgorithm.AES256_CBC, R)67         CIPHER_ALG_NAMES.put(CMSAlgorithm.AES256_CBC,  "AES/CBC/PKCS5Padding");
CIPHER_ALG_NAMES.put(new ASN1ObjectIdentifier(PKCSObjectIdentifiers.rsaEncryption.getId()), R)68         CIPHER_ALG_NAMES.put(new ASN1ObjectIdentifier(PKCSObjectIdentifiers.rsaEncryption.getId()), "RSA/ECB/PKCS1Padding");
69 
DIGEST_ALG_NAMES.put(OIWObjectIdentifiers.idSHA1, R)70         DIGEST_ALG_NAMES.put(OIWObjectIdentifiers.idSHA1, "SHA1");
DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha224, R)71         DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha224, "SHA224");
DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha256, R)72         DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha256, "SHA256");
DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha384, R)73         DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha384, "SHA384");
DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha512, R)74         DIGEST_ALG_NAMES.put(NISTObjectIdentifiers.id_sha512, "SHA512");
75 
MAC_ALG_NAMES.put(IANAObjectIdentifiers.hmacSHA1, R)76         MAC_ALG_NAMES.put(IANAObjectIdentifiers.hmacSHA1, "HMACSHA1");
MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA1, R)77         MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA1, "HMACSHA1");
MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA224, R)78         MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA224, "HMACSHA224");
MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA256, R)79         MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA256, "HMACSHA256");
MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA384, R)80         MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA384, "HMACSHA384");
MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA512, R)81         MAC_ALG_NAMES.put(PKCSObjectIdentifiers.id_hmacWithSHA512, "HMACSHA512");
82 
KEY_ALG_NAMES.put(PKCSObjectIdentifiers.rsaEncryption, R)83         KEY_ALG_NAMES.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
KEY_ALG_NAMES.put(X9ObjectIdentifiers.id_dsa, R)84         KEY_ALG_NAMES.put(X9ObjectIdentifiers.id_dsa, "DSA");
85     }
86 
87     private JcaJceHelper helper;
88 
CRMFHelper(JcaJceHelper helper)89     CRMFHelper(JcaJceHelper helper)
90     {
91         this.helper = helper;
92     }
93 
toPublicKey(SubjectPublicKeyInfo subjectPublicKeyInfo)94     PublicKey toPublicKey(SubjectPublicKeyInfo subjectPublicKeyInfo)
95         throws CRMFException
96     {
97 
98         try
99         {
100         X509EncodedKeySpec xspec = new X509EncodedKeySpec(new DERBitString(subjectPublicKeyInfo).getBytes());
101         AlgorithmIdentifier keyAlg = subjectPublicKeyInfo.getAlgorithmId();
102             return createKeyFactory(keyAlg.getAlgorithm()).generatePublic(xspec);
103         }
104         catch (IOException e)
105         {
106             throw new CRMFException("invalid key: " + e.getMessage(), e);
107         }
108         catch (InvalidKeySpecException e)
109         {
110             throw new CRMFException("invalid key: " + e.getMessage(), e);
111         }
112     }
113 
createCipher(ASN1ObjectIdentifier algorithm)114     Cipher createCipher(ASN1ObjectIdentifier algorithm)
115         throws CRMFException
116     {
117         try
118         {
119             String cipherName = (String)CIPHER_ALG_NAMES.get(algorithm);
120 
121             if (cipherName != null)
122             {
123                 try
124                 {
125                     // this is reversed as the Sun policy files now allow unlimited strength RSA
126                     return helper.createCipher(cipherName);
127                 }
128                 catch (NoSuchAlgorithmException e)
129                 {
130                     // Ignore
131                 }
132             }
133             return helper.createCipher(algorithm.getId());
134         }
135         catch (NoSuchPaddingException e)
136         {
137             throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
138         }
139         catch (NoSuchAlgorithmException e)
140         {
141             throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
142         }
143         catch (NoSuchProviderException e)
144         {
145             throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
146         }
147     }
148 
createKeyGenerator(ASN1ObjectIdentifier algorithm)149     public KeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm)
150         throws CRMFException
151     {
152         try
153         {
154             String cipherName = (String)BASE_CIPHER_NAMES.get(algorithm);
155 
156             if (cipherName != null)
157             {
158                 try
159                 {
160                     // this is reversed as the Sun policy files now allow unlimited strength RSA
161                     return helper.createKeyGenerator(cipherName);
162                 }
163                 catch (NoSuchAlgorithmException e)
164                 {
165                     // Ignore
166                 }
167             }
168             return helper.createKeyGenerator(algorithm.getId());
169         }
170         catch (NoSuchAlgorithmException e)
171         {
172             throw new CRMFException("cannot create key generator: " + e.getMessage(), e);
173         }
174         catch (NoSuchProviderException e)
175         {
176             throw new CRMFException("cannot create key generator: " + e.getMessage(), e);
177         }
178     }
179 
createContentCipher(final Key sKey, final AlgorithmIdentifier encryptionAlgID)180     Cipher createContentCipher(final Key sKey, final AlgorithmIdentifier encryptionAlgID)
181         throws CRMFException
182     {
183         return (Cipher)execute(new JCECallback()
184         {
185             public Object doInJCE()
186                 throws CRMFException, InvalidAlgorithmParameterException,
187                 InvalidKeyException, InvalidParameterSpecException, NoSuchAlgorithmException,
188                 NoSuchPaddingException, NoSuchProviderException
189             {
190                 Cipher cipher = createCipher(encryptionAlgID.getAlgorithm());
191                 ASN1Primitive sParams = (ASN1Primitive)encryptionAlgID.getParameters();
192                 String encAlg = encryptionAlgID.getAlgorithm().getId();
193 
194                 if (sParams != null && !(sParams instanceof ASN1Null))
195                 {
196                     try
197                     {
198                         AlgorithmParameters params = createAlgorithmParameters(encryptionAlgID.getAlgorithm());
199 
200                         try
201                         {
202                             params.init(sParams.getEncoded(), "ASN.1");
203                         }
204                         catch (IOException e)
205                         {
206                             throw new CRMFException("error decoding algorithm parameters.", e);
207                         }
208 
209                         cipher.init(Cipher.DECRYPT_MODE, sKey, params);
210                     }
211                     catch (NoSuchAlgorithmException e)
212                     {
213                         if (encAlg.equals(CMSEnvelopedDataGenerator.DES_EDE3_CBC)
214                             || encAlg.equals(CMSEnvelopedDataGenerator.IDEA_CBC)
215                             || encAlg.equals(CMSEnvelopedDataGenerator.AES128_CBC)
216                             || encAlg.equals(CMSEnvelopedDataGenerator.AES192_CBC)
217                             || encAlg.equals(CMSEnvelopedDataGenerator.AES256_CBC))
218                         {
219                             cipher.init(Cipher.DECRYPT_MODE, sKey, new IvParameterSpec(
220                                 ASN1OctetString.getInstance(sParams).getOctets()));
221                         }
222                         else
223                         {
224                             throw e;
225                         }
226                     }
227                 }
228                 else
229                 {
230                     if (encAlg.equals(CMSEnvelopedDataGenerator.DES_EDE3_CBC)
231                         || encAlg.equals(CMSEnvelopedDataGenerator.IDEA_CBC)
232                         || encAlg.equals(CMSEnvelopedDataGenerator.CAST5_CBC))
233                     {
234                         cipher.init(Cipher.DECRYPT_MODE, sKey, new IvParameterSpec(new byte[8]));
235                     }
236                     else
237                     {
238                         cipher.init(Cipher.DECRYPT_MODE, sKey);
239                     }
240                 }
241 
242                 return cipher;
243             }
244         });
245     }
246 
247     AlgorithmParameters createAlgorithmParameters(ASN1ObjectIdentifier algorithm)
248         throws NoSuchAlgorithmException, NoSuchProviderException
249     {
250         String algorithmName = (String)BASE_CIPHER_NAMES.get(algorithm);
251 
252         if (algorithmName != null)
253         {
254             try
255             {
256                 // this is reversed as the Sun policy files now allow unlimited strength RSA
257                 return helper.createAlgorithmParameters(algorithmName);
258             }
259             catch (NoSuchAlgorithmException e)
260             {
261                 // Ignore
262             }
263         }
264         return helper.createAlgorithmParameters(algorithm.getId());
265     }
266 
267     KeyFactory createKeyFactory(ASN1ObjectIdentifier algorithm)
268         throws CRMFException
269     {
270         try
271         {
272             String algName = (String)KEY_ALG_NAMES.get(algorithm);
273 
274             if (algName != null)
275             {
276                 try
277                 {
278                     // this is reversed as the Sun policy files now allow unlimited strength RSA
279                     return helper.createKeyFactory(algName);
280                 }
281                 catch (NoSuchAlgorithmException e)
282                 {
283                     // Ignore
284                 }
285             }
286             return helper.createKeyFactory(algorithm.getId());
287         }
288         catch (NoSuchProviderException e)
289         {
290             throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
291         }
292         catch (NoSuchAlgorithmException e)
293         {
294             throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
295         }
296     }
297 
298     MessageDigest createDigest(ASN1ObjectIdentifier algorithm)
299         throws CRMFException
300     {
301         try
302         {
303             String digestName = (String)DIGEST_ALG_NAMES.get(algorithm);
304 
305             if (digestName != null)
306             {
307                 try
308                 {
309                     // this is reversed as the Sun policy files now allow unlimited strength RSA
310                     return helper.createDigest(digestName);
311                 }
312                 catch (NoSuchAlgorithmException e)
313                 {
314                     // Ignore
315                 }
316             }
317             return helper.createDigest(algorithm.getId());
318         }
319         catch (NoSuchAlgorithmException e)
320         {
321             throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
322         }
323         catch (NoSuchProviderException e)
324         {
325             throw new CRMFException("cannot create cipher: " + e.getMessage(), e);
326         }
327     }
328 
329     Mac createMac(ASN1ObjectIdentifier algorithm)
330         throws CRMFException
331     {
332         try
333         {
334             String macName = (String)MAC_ALG_NAMES.get(algorithm);
335 
336             if (macName != null)
337             {
338                 try
339                 {
340                     // this is reversed as the Sun policy files now allow unlimited strength RSA
341                     return helper.createMac(macName);
342                 }
343                 catch (NoSuchAlgorithmException e)
344                 {
345                     // Ignore
346                 }
347             }
348             return helper.createMac(algorithm.getId());
349         }
350         catch (NoSuchProviderException e)
351         {
352             throw new CRMFException("cannot create mac: " + e.getMessage(), e);
353         }
354         catch (NoSuchAlgorithmException e)
355         {
356             throw new CRMFException("cannot create mac: " + e.getMessage(), e);
357         }
358     }
359 
360     AlgorithmParameterGenerator createAlgorithmParameterGenerator(ASN1ObjectIdentifier algorithm)
361         throws GeneralSecurityException
362     {
363         String algorithmName = (String)BASE_CIPHER_NAMES.get(algorithm);
364 
365         try
366         {
367         if (algorithmName != null)
368         {
369             try
370             {
371                 // this is reversed as the Sun policy files now allow unlimited strength RSA
372                 return helper.createAlgorithmParameterGenerator(algorithmName);
373             }
374             catch (NoSuchAlgorithmException e)
375             {
376                 // Ignore
377             }
378         }
379         return helper.createAlgorithmParameterGenerator(algorithm.getId());
380         }
381         catch (NoSuchAlgorithmException e)
382         {
383             throw new GeneralSecurityException(e.toString());
384         }
385         catch (NoSuchProviderException e)
386         {
387             throw new GeneralSecurityException(e.toString());
388         }
389     }
390 
391     AlgorithmParameters generateParameters(ASN1ObjectIdentifier encryptionOID, SecretKey encKey, SecureRandom rand)
392         throws CRMFException
393     {
394         try
395         {
396             AlgorithmParameterGenerator pGen = createAlgorithmParameterGenerator(encryptionOID);
397 
398             if (encryptionOID.equals(CMSEnvelopedDataGenerator.RC2_CBC))
399             {
400                 byte[]  iv = new byte[8];
401 
402                 rand.nextBytes(iv);
403 
404                 try
405                 {
406                     pGen.init(new RC2ParameterSpec(encKey.getEncoded().length * 8, iv), rand);
407                 }
408                 catch (InvalidAlgorithmParameterException e)
409                 {
410                     throw new CRMFException("parameters generation error: " + e, e);
411                 }
412             }
413 
414             return pGen.generateParameters();
415         }
416         catch (GeneralSecurityException e)
417         {
418             throw new CRMFException("exception creating algorithm parameter generator: " + e, e);
419         }
420     }
421 
422     AlgorithmIdentifier getAlgorithmIdentifier(ASN1ObjectIdentifier encryptionOID, AlgorithmParameters params)
423         throws CRMFException
424     {
425         ASN1Encodable asn1Params;
426         if (params != null)
427         {
428             try
429             {
430                 asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1"));
431             }
432             catch (IOException e)
433             {
434                 throw new CRMFException("cannot encode parameters: " + e.getMessage(), e);
435             }
436         }
437         else
438         {
439             asn1Params = DERNull.INSTANCE;
440         }
441 
442         return new AlgorithmIdentifier(
443             encryptionOID,
444             asn1Params);
445     }
446 
447     static Object execute(JCECallback callback) throws CRMFException
448     {
449         try
450         {
451             return callback.doInJCE();
452         }
453         catch (NoSuchAlgorithmException e)
454         {
455             throw new CRMFException("can't find algorithm.", e);
456         }
457         catch (InvalidKeyException e)
458         {
459             throw new CRMFException("key invalid in message.", e);
460         }
461         catch (NoSuchProviderException e)
462         {
463             throw new CRMFException("can't find provider.", e);
464         }
465         catch (NoSuchPaddingException e)
466         {
467             throw new CRMFException("required padding not supported.", e);
468         }
469         catch (InvalidAlgorithmParameterException e)
470         {
471             throw new CRMFException("algorithm parameters invalid.", e);
472         }
473         catch (InvalidParameterSpecException e)
474         {
475             throw new CRMFException("MAC algorithm parameter spec invalid.", e);
476         }
477     }
478 
479     static interface JCECallback
480     {
481         Object doInJCE()
482             throws CRMFException, InvalidAlgorithmParameterException, InvalidKeyException, InvalidParameterSpecException,
483             NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException;
484     }
485 }
486