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