1 package org.bouncycastle.jcajce.provider.symmetric.util;
2 
3 // BEGIN Android-added: Needed for compatibility helper
4 import java.lang.reflect.Method;
5 // END Android-added: Needed for compatibility helper
6 import java.security.InvalidAlgorithmParameterException;
7 import java.security.spec.AlgorithmParameterSpec;
8 
9 import javax.crypto.SecretKey;
10 // BEGIN Android-added: Allow IVs specified in parameters.
11 import javax.crypto.spec.IvParameterSpec;
12 // END Android-added: Allow IVs specified in parameters.
13 import javax.crypto.spec.PBEKeySpec;
14 import javax.crypto.spec.PBEParameterSpec;
15 
16 import org.bouncycastle.crypto.CipherParameters;
17 import org.bouncycastle.crypto.PBEParametersGenerator;
18 // Android-removed: Unsupported algorithms
19 // import org.bouncycastle.crypto.digests.GOST3411Digest;
20 // import org.bouncycastle.crypto.digests.MD2Digest;
21 // import org.bouncycastle.crypto.digests.RIPEMD160Digest;
22 // import org.bouncycastle.crypto.digests.TigerDigest;
23 import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
24 import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
25 import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator;
26 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
27 import org.bouncycastle.crypto.params.DESParameters;
28 import org.bouncycastle.crypto.params.KeyParameter;
29 import org.bouncycastle.crypto.params.ParametersWithIV;
30 // BEGIN Android-changed: Use Android digests
31 // import org.bouncycastle.crypto.util.DigestFactory;
32 import org.bouncycastle.crypto.digests.AndroidDigestFactory;
33 // END Android-changed: Use Android digests
34 
35 public interface PBE
36 {
37     //
38     // PBE Based encryption constants - by default we do PKCS12 with SHA-1
39     //
40     static final int        MD5          = 0;
41     static final int        SHA1         = 1;
42     // Android-removed: Unsupported algorithms
43     // static final int        RIPEMD160    = 2;
44     // static final int        TIGER        = 3;
45     static final int        SHA256       = 4;
46     // Android-removed: Unsupported algorithms
47     // static final int        MD2          = 5;
48     // static final int        GOST3411     = 6;
49     static final int        SHA224       = 7;
50     static final int        SHA384       = 8;
51     static final int        SHA512       = 9;
52 
53     static final int        PKCS5S1      = 0;
54     static final int        PKCS5S2      = 1;
55     static final int        PKCS12       = 2;
56     static final int        OPENSSL      = 3;
57     static final int        PKCS5S1_UTF8 = 4;
58     static final int        PKCS5S2_UTF8 = 5;
59 
60     /**
61      * uses the appropriate mixer to generate the key and IV if necessary.
62      */
63     static class Util
64     {
makePBEGenerator( int type, int hash)65         static private PBEParametersGenerator makePBEGenerator(
66             int                     type,
67             int                     hash)
68         {
69             PBEParametersGenerator  generator;
70 
71             if (type == PKCS5S1 || type == PKCS5S1_UTF8)
72             {
73                 switch (hash)
74                 {
75                 // Android-removed: Unsupported algorithms
76                 // case MD2:
77                 //     generator = new PKCS5S1ParametersGenerator(new MD2Digest());
78                 //     break;
79                 case MD5:
80                     // Android-changed: Use Android digests
81                     // generator = new PKCS5S1ParametersGenerator(DigestFactory.createMD5());
82                     generator = new PKCS5S1ParametersGenerator(AndroidDigestFactory.getMD5());
83                     break;
84                 case SHA1:
85                     // Android-changed: Use Android digests
86                     // generator = new PKCS5S1ParametersGenerator(DigestFactory.createSHA1());
87                     generator = new PKCS5S1ParametersGenerator(AndroidDigestFactory.getSHA1());
88                     break;
89                 default:
90                     throw new IllegalStateException("PKCS5 scheme 1 only supports MD2, MD5 and SHA1.");
91                 }
92             }
93             else if (type == PKCS5S2 || type == PKCS5S2_UTF8)
94             {
95                 switch (hash)
96                 {
97                 // Android-removed: Unsupported algorithms
98                 // case MD2:
99                 //     generator = new PKCS5S2ParametersGenerator(new MD2Digest());
100                 //     break;
101                 case MD5:
102                     // Android-changed: Use Android digests
103                     // generator = new PKCS5S2ParametersGenerator(DigestFactory.createMD5());
104                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getMD5());
105                     break;
106                 case SHA1:
107                     // Android-changed: Use Android digests
108                     // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA1());
109                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA1());
110                     break;
111                 // BEGIN Android-removed: Unsupported algorithms
112                 /*
113                 case RIPEMD160:
114                     generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest());
115                     break;
116                 case TIGER:
117                     generator = new PKCS5S2ParametersGenerator(new TigerDigest());
118                     break;
119                 */
120                 // END Android-removed: Unsupported algorithms
121                 case SHA256:
122                     // Android-changed: Use Android digests
123                     // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA256());
124                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA256());
125                     break;
126                 // Android-removed: Unsupported algorithms
127                 // case GOST3411:
128                 //     generator = new PKCS5S2ParametersGenerator(new GOST3411Digest());
129                 //     break;
130                 case SHA224:
131                     // Android-changed: Use Android digests
132                     // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA224());
133                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA224());
134                     break;
135                 case SHA384:
136                     // Android-changed: Use Android digests
137                     // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA384());
138                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA384());
139                     break;
140                 case SHA512:
141                     // Android-changed: Use Android digests
142                     // generator = new PKCS5S2ParametersGenerator(DigestFactory.createSHA512());
143                     generator = new PKCS5S2ParametersGenerator(AndroidDigestFactory.getSHA512());
144                     break;
145                 default:
146                     throw new IllegalStateException("unknown digest scheme for PBE PKCS5S2 encryption.");
147                 }
148             }
149             else if (type == PKCS12)
150             {
151                 switch (hash)
152                 {
153                 // Android-removed: Unsupported algorithms
154                 // case MD2:
155                 //     generator = new PKCS12ParametersGenerator(new MD2Digest());
156                 //     break;
157                 case MD5:
158                     // Android-changed: Use Android digests
159                     // generator = new PKCS12ParametersGenerator(DigestFactory.createMD5());
160                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getMD5());
161                     break;
162                 case SHA1:
163                     // Android-changed: Use Android digests
164                     // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA1());
165                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA1());
166                     break;
167                 // BEGIN Android-removed: Unsupported algorithms
168                 /*
169                 case RIPEMD160:
170                     generator = new PKCS12ParametersGenerator(new RIPEMD160Digest());
171                     break;
172                 case TIGER:
173                     generator = new PKCS12ParametersGenerator(new TigerDigest());
174                     break;
175                 */
176                 // END Android-removed: Unsupported algorithms
177                 case SHA256:
178                     // Android-changed: Use Android digests
179                     // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA256());
180                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA256());
181                     break;
182                 // Android-removed: Unsupported algorithms
183                 // case GOST3411:
184                 //     generator = new PKCS12ParametersGenerator(new GOST3411Digest());
185                 //     break;
186                 case SHA224:
187                     // Android-changed: Use Android digests
188                     // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA224());
189                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA224());
190                     break;
191                 case SHA384:
192                     // Android-changed: Use Android digests
193                     // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA384());
194                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA384());
195                     break;
196                 case SHA512:
197                     // Android-changed: Use Android digests
198                     // generator = new PKCS12ParametersGenerator(DigestFactory.createSHA512());
199                     generator = new PKCS12ParametersGenerator(AndroidDigestFactory.getSHA512());
200                     break;
201                 default:
202                     throw new IllegalStateException("unknown digest scheme for PBE encryption.");
203                 }
204             }
205             else
206             {
207                 generator = new OpenSSLPBEParametersGenerator();
208             }
209 
210             return generator;
211         }
212 
213         /**
214          * construct a key and iv (if necessary) suitable for use with a
215          * Cipher.
216          */
makePBEParameters( byte[] pbeKey, int scheme, int digest, int keySize, int ivSize, AlgorithmParameterSpec spec, String targetAlgorithm)217         public static CipherParameters makePBEParameters(
218             byte[] pbeKey,
219             int scheme,
220             int digest,
221             int keySize,
222             int ivSize,
223             AlgorithmParameterSpec spec,
224             String targetAlgorithm)
225             throws InvalidAlgorithmParameterException
226         {
227             if ((spec == null) || !(spec instanceof PBEParameterSpec))
228             {
229                 throw new InvalidAlgorithmParameterException("Need a PBEParameter spec with a PBE key.");
230             }
231 
232             PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
233             PBEParametersGenerator  generator = makePBEGenerator(scheme, digest);
234             byte[]                  key = pbeKey;
235             CipherParameters        param;
236 
237 //            if (pbeKey.shouldTryWrongPKCS12())
238 //            {
239 //                key = new byte[2];
240 //            }
241 
242             generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
243 
244             if (ivSize != 0)
245             {
246                 param = generator.generateDerivedParameters(keySize, ivSize);
247                 // BEGIN Android-added: Allow IVs specified in parameters.
248                 // PKCS5S2 doesn't specify that the IV must be generated from the password. If the
249                 // IV is passed as a parameter, use it.
250                 AlgorithmParameterSpec parameterSpecFromPBEParameterSpec =
251                         getParameterSpecFromPBEParameterSpec(pbeParam);
252                 if ((scheme == PKCS5S2 || scheme == PKCS5S2_UTF8)
253                         && parameterSpecFromPBEParameterSpec instanceof IvParameterSpec) {
254                     ParametersWithIV parametersWithIV = (ParametersWithIV) param;
255                     IvParameterSpec ivParameterSpec =
256                             (IvParameterSpec) parameterSpecFromPBEParameterSpec;
257                     param = new ParametersWithIV(
258                             (KeyParameter) parametersWithIV.getParameters(),
259                             ivParameterSpec.getIV());
260                 }
261                 // END Android-added: Allow IVs specified in parameters.
262             }
263             else
264             {
265                 param = generator.generateDerivedParameters(keySize);
266             }
267 
268             if (targetAlgorithm.startsWith("DES"))
269             {
270                 if (param instanceof ParametersWithIV)
271                 {
272                     KeyParameter    kParam = (KeyParameter)((ParametersWithIV)param).getParameters();
273 
274                     DESParameters.setOddParity(kParam.getKey());
275                 }
276                 else
277                 {
278                     KeyParameter    kParam = (KeyParameter)param;
279 
280                     DESParameters.setOddParity(kParam.getKey());
281                 }
282             }
283 
284             return param;
285         }
286 
287         /**
288          * construct a key and iv (if necessary) suitable for use with a
289          * Cipher.
290          */
makePBEParameters( BCPBEKey pbeKey, AlgorithmParameterSpec spec, String targetAlgorithm)291         public static CipherParameters makePBEParameters(
292             BCPBEKey pbeKey,
293             AlgorithmParameterSpec spec,
294             String targetAlgorithm)
295         {
296             if ((spec == null) || !(spec instanceof PBEParameterSpec))
297             {
298                 throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key.");
299             }
300 
301             PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
302             PBEParametersGenerator  generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest());
303             byte[]                  key = pbeKey.getEncoded();
304             CipherParameters        param;
305 
306             if (pbeKey.shouldTryWrongPKCS12())
307             {
308                 key = new byte[2];
309             }
310 
311             generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
312 
313             if (pbeKey.getIvSize() != 0)
314             {
315                 param = generator.generateDerivedParameters(pbeKey.getKeySize(), pbeKey.getIvSize());
316                 // BEGIN Android-added: Allow IVs specified in parameters.
317                 // PKCS5S2 doesn't specify that the IV must be generated from the password. If the
318                 // IV is passed as a parameter, use it.
319                 AlgorithmParameterSpec parameterSpecFromPBEParameterSpec =
320                         getParameterSpecFromPBEParameterSpec(pbeParam);
321                 if ((pbeKey.getType() == PKCS5S2 || pbeKey.getType() == PKCS5S2_UTF8)
322                         && parameterSpecFromPBEParameterSpec instanceof IvParameterSpec) {
323                     ParametersWithIV parametersWithIV = (ParametersWithIV) param;
324                     IvParameterSpec ivParameterSpec =
325                             (IvParameterSpec) parameterSpecFromPBEParameterSpec;
326                     param = new ParametersWithIV(
327                             (KeyParameter) parametersWithIV.getParameters(),
328                             ivParameterSpec.getIV());
329                 }
330                 // END Android-added: Allow IVs specified in parameters.
331             }
332             else
333             {
334                 param = generator.generateDerivedParameters(pbeKey.getKeySize());
335             }
336 
337             if (targetAlgorithm.startsWith("DES"))
338             {
339                 if (param instanceof ParametersWithIV)
340                 {
341                     KeyParameter    kParam = (KeyParameter)((ParametersWithIV)param).getParameters();
342 
343                     DESParameters.setOddParity(kParam.getKey());
344                 }
345                 else
346                 {
347                     KeyParameter    kParam = (KeyParameter)param;
348 
349                     DESParameters.setOddParity(kParam.getKey());
350                 }
351             }
352 
353             return param;
354         }
355 
356         /**
357          * generate a PBE based key suitable for a MAC algorithm, the
358          * key size is chosen according the MAC size, or the hashing algorithm,
359          * whichever is greater.
360          */
makePBEMacParameters( BCPBEKey pbeKey, AlgorithmParameterSpec spec)361         public static CipherParameters makePBEMacParameters(
362             BCPBEKey pbeKey,
363             AlgorithmParameterSpec spec)
364         {
365             if ((spec == null) || !(spec instanceof PBEParameterSpec))
366             {
367                 throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key.");
368             }
369 
370             PBEParameterSpec        pbeParam = (PBEParameterSpec)spec;
371             PBEParametersGenerator  generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest());
372             byte[]                  key = pbeKey.getEncoded();
373             CipherParameters        param;
374 
375             generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount());
376 
377             param = generator.generateDerivedMacParameters(pbeKey.getKeySize());
378 
379             return param;
380         }
381 
382         /**
383          * generate a PBE based key suitable for a MAC algorithm, the
384          * key size is chosen according the MAC size, or the hashing algorithm,
385          * whichever is greater.
386          */
makePBEMacParameters( PBEKeySpec keySpec, int type, int hash, int keySize)387         public static CipherParameters makePBEMacParameters(
388             PBEKeySpec keySpec,
389             int type,
390             int hash,
391             int keySize)
392         {
393             PBEParametersGenerator  generator = makePBEGenerator(type, hash);
394             byte[]                  key;
395             CipherParameters        param;
396 
397             key = convertPassword(type, keySpec);
398 
399             generator.init(key, keySpec.getSalt(), keySpec.getIterationCount());
400 
401             param = generator.generateDerivedMacParameters(keySize);
402 
403             for (int i = 0; i != key.length; i++)
404             {
405                 key[i] = 0;
406             }
407 
408             return param;
409         }
410 
411         /**
412          * construct a key and iv (if necessary) suitable for use with a
413          * Cipher.
414          */
makePBEParameters( PBEKeySpec keySpec, int type, int hash, int keySize, int ivSize)415         public static CipherParameters makePBEParameters(
416             PBEKeySpec keySpec,
417             int type,
418             int hash,
419             int keySize,
420             int ivSize)
421         {
422             PBEParametersGenerator  generator = makePBEGenerator(type, hash);
423             byte[]                  key;
424             CipherParameters        param;
425 
426             key = convertPassword(type, keySpec);
427 
428             generator.init(key, keySpec.getSalt(), keySpec.getIterationCount());
429 
430             if (ivSize != 0)
431             {
432                 param = generator.generateDerivedParameters(keySize, ivSize);
433             }
434             else
435             {
436                 param = generator.generateDerivedParameters(keySize);
437             }
438 
439             for (int i = 0; i != key.length; i++)
440             {
441                 key[i] = 0;
442             }
443 
444             return param;
445         }
446 
447         /**
448          * generate a PBE based key suitable for a MAC algorithm, the
449          * key size is chosen according the MAC size, or the hashing algorithm,
450          * whichever is greater.
451          */
makePBEMacParameters( SecretKey key, int type, int hash, int keySize, PBEParameterSpec pbeSpec)452         public static CipherParameters makePBEMacParameters(
453             SecretKey key,
454             int type,
455             int hash,
456             int keySize,
457             PBEParameterSpec pbeSpec)
458         {
459             PBEParametersGenerator  generator = makePBEGenerator(type, hash);
460             CipherParameters        param;
461 
462             byte[] keyBytes = key.getEncoded();
463 
464             generator.init(key.getEncoded(), pbeSpec.getSalt(), pbeSpec.getIterationCount());
465 
466             param = generator.generateDerivedMacParameters(keySize);
467 
468             for (int i = 0; i != keyBytes.length; i++)
469             {
470                 keyBytes[i] = 0;
471             }
472 
473             return param;
474         }
475 
476         // BEGIN Android-added: Add helper for 1.8 compatibility.
477         /**
478          * Invokes the method {@link PBEParameterSpec#getParameterSpec()} via reflection.
479          *
480          * Needed as the method was introduced in Java 1.8 and Bouncycastle level is 1.5.
481          *
482          * @return the parameter spec, or null if the method is not available.
483          */
getParameterSpecFromPBEParameterSpec( PBEParameterSpec pbeParameterSpec)484         public static AlgorithmParameterSpec getParameterSpecFromPBEParameterSpec(
485                 PBEParameterSpec pbeParameterSpec) {
486             try {
487                 Method getParameterSpecMethod = PBE.class.getClassLoader()
488                         .loadClass("javax.crypto.spec.PBEParameterSpec")
489                         .getMethod("getParameterSpec");
490                 return (AlgorithmParameterSpec) getParameterSpecMethod.invoke(pbeParameterSpec);
491             } catch (Exception e) {
492                 return null;
493             }
494         }
495         // END Android-added: Add helper for 1.8 compatibility.
496 
497 
convertPassword(int type, PBEKeySpec keySpec)498         private static byte[] convertPassword(int type, PBEKeySpec keySpec)
499         {
500             byte[] key;
501 
502             if (type == PKCS12)
503             {
504                 key = PBEParametersGenerator.PKCS12PasswordToBytes(keySpec.getPassword());
505             }
506             else if (type == PKCS5S2_UTF8 || type == PKCS5S1_UTF8)
507             {
508                 key = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(keySpec.getPassword());
509             }
510             else
511             {
512                 key = PBEParametersGenerator.PKCS5PasswordToBytes(keySpec.getPassword());
513             }
514             return key;
515         }
516     }
517 }
518