View Javadoc
1   /*
2    * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.crypto.provider;
27  
28  import java.io.*;
29  import java.math.BigInteger;
30  import java.security.NoSuchAlgorithmException;
31  import java.security.AlgorithmParametersSpi;
32  import java.security.spec.AlgorithmParameterSpec;
33  import java.security.spec.InvalidParameterSpecException;
34  import javax.crypto.spec.IvParameterSpec;
35  import javax.crypto.spec.PBEParameterSpec;
36  import sun.misc.HexDumpEncoder;
37  import sun.security.util.*;
38  
39  /**
40   * This class implements the parameter set used with password-based
41   * encryption scheme 2 (PBES2), which is defined in PKCS#5 as follows:
42   *
43   * <pre>
44   * -- PBES2
45   *
46   * PBES2Algorithms ALGORITHM-IDENTIFIER ::=
47   *   { {PBES2-params IDENTIFIED BY id-PBES2}, ...}
48   *
49   * id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13}
50   *
51   * PBES2-params ::= SEQUENCE {
52   *   keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
53   *   encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
54   *
55   * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
56   *   { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
57   *
58   * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
59   *
60   * -- PBKDF2
61   *
62   * PBKDF2Algorithms ALGORITHM-IDENTIFIER ::=
63   *   { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ...}
64   *
65   * id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12}
66   *
67   * PBKDF2-params ::= SEQUENCE {
68   *     salt CHOICE {
69   *       specified OCTET STRING,
70   *       otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
71   *     },
72   *     iterationCount INTEGER (1..MAX),
73   *     keyLength INTEGER (1..MAX) OPTIONAL,
74   *     prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
75   * }
76   *
77   * PBKDF2-SaltSources ALGORITHM-IDENTIFIER ::= { ... }
78   *
79   * PBKDF2-PRFs ALGORITHM-IDENTIFIER ::= {
80   *     {NULL IDENTIFIED BY id-hmacWithSHA1} |
81   *     {NULL IDENTIFIED BY id-hmacWithSHA224} |
82   *     {NULL IDENTIFIED BY id-hmacWithSHA256} |
83   *     {NULL IDENTIFIED BY id-hmacWithSHA384} |
84   *     {NULL IDENTIFIED BY id-hmacWithSHA512}, ... }
85   *
86   * algid-hmacWithSHA1 AlgorithmIdentifier {{PBKDF2-PRFs}} ::=
87   *     {algorithm id-hmacWithSHA1, parameters NULL : NULL}
88   *
89   * id-hmacWithSHA1 OBJECT IDENTIFIER ::= {digestAlgorithm 7}
90   *
91   * PBES2-Encs ALGORITHM-IDENTIFIER ::= { ... }
92   *
93   * </pre>
94   */
95  
96  abstract class PBES2Parameters extends AlgorithmParametersSpi {
97  
98      private static final int pkcs5PBKDF2[] =
99                                          {1, 2, 840, 113549, 1, 5, 12};
100     private static final int pkcs5PBES2[] =
101                                         {1, 2, 840, 113549, 1, 5, 13};
102     private static final int hmacWithSHA1[] =
103                                         {1, 2, 840, 113549, 2, 7};
104     private static final int hmacWithSHA224[] =
105                                         {1, 2, 840, 113549, 2, 8};
106     private static final int hmacWithSHA256[] =
107                                         {1, 2, 840, 113549, 2, 9};
108     private static final int hmacWithSHA384[] =
109                                         {1, 2, 840, 113549, 2, 10};
110     private static final int hmacWithSHA512[] =
111                                         {1, 2, 840, 113549, 2, 11};
112     private static final int aes128CBC[] =
113                                         {2, 16, 840, 1, 101, 3, 4, 1, 2};
114     private static final int aes192CBC[] =
115                                         {2, 16, 840, 1, 101, 3, 4, 1, 22};
116     private static final int aes256CBC[] =
117                                         {2, 16, 840, 1, 101, 3, 4, 1, 42};
118 
119     private static ObjectIdentifier pkcs5PBKDF2_OID;
120     private static ObjectIdentifier pkcs5PBES2_OID;
121     private static ObjectIdentifier hmacWithSHA1_OID;
122     private static ObjectIdentifier hmacWithSHA224_OID;
123     private static ObjectIdentifier hmacWithSHA256_OID;
124     private static ObjectIdentifier hmacWithSHA384_OID;
125     private static ObjectIdentifier hmacWithSHA512_OID;
126     private static ObjectIdentifier aes128CBC_OID;
127     private static ObjectIdentifier aes192CBC_OID;
128     private static ObjectIdentifier aes256CBC_OID;
129 
130     static {
131         try {
132             pkcs5PBKDF2_OID = new ObjectIdentifier(pkcs5PBKDF2);
133             pkcs5PBES2_OID = new ObjectIdentifier(pkcs5PBES2);
134             hmacWithSHA1_OID = new ObjectIdentifier(hmacWithSHA1);
135             hmacWithSHA224_OID = new ObjectIdentifier(hmacWithSHA224);
136             hmacWithSHA256_OID = new ObjectIdentifier(hmacWithSHA256);
137             hmacWithSHA384_OID = new ObjectIdentifier(hmacWithSHA384);
138             hmacWithSHA512_OID = new ObjectIdentifier(hmacWithSHA512);
139             aes128CBC_OID = new ObjectIdentifier(aes128CBC);
140             aes192CBC_OID = new ObjectIdentifier(aes192CBC);
141             aes256CBC_OID = new ObjectIdentifier(aes256CBC);
142         } catch (IOException ioe) {
143             // should not happen
144         }
145     }
146 
147     // the PBES2 algorithm name
148     private String pbes2AlgorithmName = null;
149 
150     // the salt
151     private byte[] salt = null;
152 
153     // the iteration count
154     private int iCount = 0;
155 
156     // the cipher parameter
157     private AlgorithmParameterSpec cipherParam = null;
158 
159     // the key derivation function (default is HmacSHA1)
160     private ObjectIdentifier kdfAlgo_OID = hmacWithSHA1_OID;
161 
162     // the encryption function
163     private ObjectIdentifier cipherAlgo_OID = null;
164 
165     // the cipher keysize (in bits)
166     private int keysize = -1;
167 
168     PBES2Parameters() {
169         // KDF, encryption & keysize values are set later, in engineInit(byte[])
170     }
171 
172     PBES2Parameters(String pbes2AlgorithmName) throws NoSuchAlgorithmException {
173         int and;
174         String kdfAlgo = null;
175         String cipherAlgo = null;
176 
177         // Extract the KDF and encryption algorithm names
178         this.pbes2AlgorithmName = pbes2AlgorithmName;
179         if (pbes2AlgorithmName.startsWith("PBEWith") &&
180             (and = pbes2AlgorithmName.indexOf("And", 7 + 1)) > 0) {
181             kdfAlgo = pbes2AlgorithmName.substring(7, and);
182             cipherAlgo = pbes2AlgorithmName.substring(and + 3);
183 
184             // Check for keysize
185             int underscore;
186             if ((underscore = cipherAlgo.indexOf('_')) > 0) {
187                 int slash;
188                 if ((slash = cipherAlgo.indexOf('/', underscore + 1)) > 0) {
189                     keysize =
190                         Integer.parseInt(cipherAlgo.substring(underscore + 1,
191                             slash));
192                 } else {
193                     keysize =
194                         Integer.parseInt(cipherAlgo.substring(underscore + 1));
195                 }
196                 cipherAlgo = cipherAlgo.substring(0, underscore);
197             }
198         } else {
199             throw new NoSuchAlgorithmException("No crypto implementation for " +
200                 pbes2AlgorithmName);
201         }
202 
203         switch (kdfAlgo) {
204         case "HmacSHA1":
205             kdfAlgo_OID = hmacWithSHA1_OID;
206             break;
207         case "HmacSHA224":
208             kdfAlgo_OID = hmacWithSHA224_OID;
209             break;
210         case "HmacSHA256":
211             kdfAlgo_OID = hmacWithSHA256_OID;
212             break;
213         case "HmacSHA384":
214             kdfAlgo_OID = hmacWithSHA384_OID;
215             break;
216         case "HmacSHA512":
217             kdfAlgo_OID = hmacWithSHA512_OID;
218             break;
219         default:
220             throw new NoSuchAlgorithmException(
221                 "No crypto implementation for " + kdfAlgo);
222         }
223 
224         if (cipherAlgo.equals("AES")) {
225             this.keysize = keysize;
226             switch (keysize) {
227             case 128:
228                 cipherAlgo_OID = aes128CBC_OID;
229                 break;
230             case 256:
231                 cipherAlgo_OID = aes256CBC_OID;
232                 break;
233             default:
234                 throw new NoSuchAlgorithmException(
235                     "No Cipher implementation for " + keysize + "-bit " +
236                         cipherAlgo);
237             }
238         } else {
239             throw new NoSuchAlgorithmException("No Cipher implementation for " +
240                 cipherAlgo);
241         }
242     }
243 
244     protected void engineInit(AlgorithmParameterSpec paramSpec)
245         throws InvalidParameterSpecException
246     {
247        if (!(paramSpec instanceof PBEParameterSpec)) {
248            throw new InvalidParameterSpecException
249                ("Inappropriate parameter specification");
250        }
251        this.salt = ((PBEParameterSpec)paramSpec).getSalt().clone();
252        this.iCount = ((PBEParameterSpec)paramSpec).getIterationCount();
253        this.cipherParam = ((PBEParameterSpec)paramSpec).getParameterSpec();
254     }
255 
256     protected void engineInit(byte[] encoded)
257         throws IOException
258     {
259         String kdfAlgo = null;
260         String cipherAlgo = null;
261 
262         DerValue pBES2Algorithms = new DerValue(encoded);
263         if (pBES2Algorithms.tag != DerValue.tag_Sequence) {
264             throw new IOException("PBE parameter parsing error: "
265                                   + "not an ASN.1 SEQUENCE tag");
266         }
267         if (!pkcs5PBES2_OID.equals(pBES2Algorithms.data.getOID())) {
268             throw new IOException("PBE parameter parsing error: "
269                 + "expecting the object identifier for PBES2");
270         }
271         if (pBES2Algorithms.tag != DerValue.tag_Sequence) {
272             throw new IOException("PBE parameter parsing error: "
273                 + "not an ASN.1 SEQUENCE tag");
274         }
275 
276         DerValue pBES2_params = pBES2Algorithms.data.getDerValue();
277         if (pBES2_params.tag != DerValue.tag_Sequence) {
278             throw new IOException("PBE parameter parsing error: "
279                 + "not an ASN.1 SEQUENCE tag");
280         }
281         kdfAlgo = parseKDF(pBES2_params.data.getDerValue());
282 
283         if (pBES2_params.tag != DerValue.tag_Sequence) {
284             throw new IOException("PBE parameter parsing error: "
285                 + "not an ASN.1 SEQUENCE tag");
286         }
287         cipherAlgo = parseES(pBES2_params.data.getDerValue());
288 
289         pbes2AlgorithmName = new StringBuilder().append("PBEWith")
290             .append(kdfAlgo).append("And").append(cipherAlgo).toString();
291     }
292 
293     private String parseKDF(DerValue keyDerivationFunc) throws IOException {
294         String kdfAlgo = null;
295 
296         if (!pkcs5PBKDF2_OID.equals(keyDerivationFunc.data.getOID())) {
297             throw new IOException("PBE parameter parsing error: "
298                 + "expecting the object identifier for PBKDF2");
299         }
300         if (keyDerivationFunc.tag != DerValue.tag_Sequence) {
301             throw new IOException("PBE parameter parsing error: "
302                 + "not an ASN.1 SEQUENCE tag");
303         }
304         DerValue pBKDF2_params = keyDerivationFunc.data.getDerValue();
305         if (pBKDF2_params.tag != DerValue.tag_Sequence) {
306             throw new IOException("PBE parameter parsing error: "
307                 + "not an ASN.1 SEQUENCE tag");
308         }
309         DerValue specified = pBKDF2_params.data.getDerValue();
310         // the 'specified' ASN.1 CHOICE for 'salt' is supported
311         if (specified.tag == DerValue.tag_OctetString) {
312             salt = specified.getOctetString();
313         } else {
314             // the 'otherSource' ASN.1 CHOICE for 'salt' is not supported
315             throw new IOException("PBE parameter parsing error: "
316                 + "not an ASN.1 OCTET STRING tag");
317         }
318         iCount = pBKDF2_params.data.getInteger();
319         DerValue keyLength = pBKDF2_params.data.getDerValue();
320         if (keyLength.tag == DerValue.tag_Integer) {
321             keysize = keyLength.getInteger() * 8; // keysize (in bits)
322         }
323         if (pBKDF2_params.tag == DerValue.tag_Sequence) {
324             DerValue prf = pBKDF2_params.data.getDerValue();
325             kdfAlgo_OID = prf.data.getOID();
326             if (hmacWithSHA1_OID.equals(kdfAlgo_OID)) {
327                 kdfAlgo = "HmacSHA1";
328             } else if (hmacWithSHA224_OID.equals(kdfAlgo_OID)) {
329                 kdfAlgo = "HmacSHA224";
330             } else if (hmacWithSHA256_OID.equals(kdfAlgo_OID)) {
331                 kdfAlgo = "HmacSHA256";
332             } else if (hmacWithSHA384_OID.equals(kdfAlgo_OID)) {
333                 kdfAlgo = "HmacSHA384";
334             } else if (hmacWithSHA512_OID.equals(kdfAlgo_OID)) {
335                 kdfAlgo = "HmacSHA512";
336             } else {
337                 throw new IOException("PBE parameter parsing error: "
338                     + "expecting the object identifier for a HmacSHA key "
339                     + "derivation function");
340             }
341             if (prf.data.available() != 0) {
342                 // parameter is 'NULL' for all HmacSHA KDFs
343                 DerValue parameter = prf.data.getDerValue();
344                 if (parameter.tag != DerValue.tag_Null) {
345                     throw new IOException("PBE parameter parsing error: "
346                         + "not an ASN.1 NULL tag");
347                 }
348             }
349         }
350 
351         return kdfAlgo;
352     }
353 
354     private String parseES(DerValue encryptionScheme) throws IOException {
355         String cipherAlgo = null;
356 
357         cipherAlgo_OID = encryptionScheme.data.getOID();
358         if (aes128CBC_OID.equals(cipherAlgo_OID)) {
359             cipherAlgo = "AES_128";
360             // parameter is AES-IV 'OCTET STRING (SIZE(16))'
361             cipherParam =
362                 new IvParameterSpec(encryptionScheme.data.getOctetString());
363             keysize = 128;
364         } else if (aes256CBC_OID.equals(cipherAlgo_OID)) {
365             cipherAlgo = "AES_256";
366             // parameter is AES-IV 'OCTET STRING (SIZE(16))'
367             cipherParam =
368                 new IvParameterSpec(encryptionScheme.data.getOctetString());
369             keysize = 256;
370         } else {
371             throw new IOException("PBE parameter parsing error: "
372                 + "expecting the object identifier for AES cipher");
373         }
374 
375         return cipherAlgo;
376     }
377 
378     protected void engineInit(byte[] encoded, String decodingMethod)
379         throws IOException
380     {
381         engineInit(encoded);
382     }
383 
384     protected <T extends AlgorithmParameterSpec>
385             T engineGetParameterSpec(Class<T> paramSpec)
386         throws InvalidParameterSpecException
387     {
388         if (PBEParameterSpec.class.isAssignableFrom(paramSpec)) {
389             return paramSpec.cast(
390                 new PBEParameterSpec(this.salt, this.iCount, this.cipherParam));
391         } else {
392             throw new InvalidParameterSpecException
393                 ("Inappropriate parameter specification");
394         }
395     }
396 
397     protected byte[] engineGetEncoded() throws IOException {
398         DerOutputStream out = new DerOutputStream();
399         DerOutputStream pBES2Algorithms = new DerOutputStream();
400         pBES2Algorithms.putOID(pkcs5PBES2_OID);
401 
402         DerOutputStream pBES2_params = new DerOutputStream();
403 
404         DerOutputStream keyDerivationFunc = new DerOutputStream();
405         keyDerivationFunc.putOID(pkcs5PBKDF2_OID);
406 
407         DerOutputStream pBKDF2_params = new DerOutputStream();
408         pBKDF2_params.putOctetString(salt); // choice: 'specified OCTET STRING'
409         pBKDF2_params.putInteger(iCount);
410         pBKDF2_params.putInteger(keysize / 8); // derived key length (in octets)
411 
412         DerOutputStream prf = new DerOutputStream();
413         // algorithm is id-hmacWithSHA1/SHA224/SHA256/SHA384/SHA512
414         prf.putOID(kdfAlgo_OID);
415         // parameters is 'NULL'
416         prf.putNull();
417         pBKDF2_params.write(DerValue.tag_Sequence, prf);
418 
419         keyDerivationFunc.write(DerValue.tag_Sequence, pBKDF2_params);
420         pBES2_params.write(DerValue.tag_Sequence, keyDerivationFunc);
421 
422         DerOutputStream encryptionScheme = new DerOutputStream();
423         // algorithm is id-aes128-CBC or id-aes256-CBC
424         encryptionScheme.putOID(cipherAlgo_OID);
425         // parameters is 'AES-IV ::= OCTET STRING (SIZE(16))'
426         if (cipherParam != null && cipherParam instanceof IvParameterSpec) {
427             encryptionScheme.putOctetString(
428                 ((IvParameterSpec)cipherParam).getIV());
429         } else {
430             throw new IOException("Wrong parameter type: IV expected");
431         }
432         pBES2_params.write(DerValue.tag_Sequence, encryptionScheme);
433 
434         pBES2Algorithms.write(DerValue.tag_Sequence, pBES2_params);
435         out.write(DerValue.tag_Sequence, pBES2Algorithms);
436 
437         return out.toByteArray();
438     }
439 
440     protected byte[] engineGetEncoded(String encodingMethod)
441         throws IOException
442     {
443         return engineGetEncoded();
444     }
445 
446     /*
447      * Returns a formatted string describing the parameters.
448      *
449      * The algorithn name pattern is: "PBEWith<prf>And<encryption>"
450      * where <prf> is one of: HmacSHA1, HmacSHA224, HmacSHA256, HmacSHA384,
451      * or HmacSHA512, and <encryption> is AES with a keysize suffix.
452      */
453     protected String engineToString() {
454         return pbes2AlgorithmName;
455     }
456 
457     public static final class General extends PBES2Parameters {
458         public General() throws NoSuchAlgorithmException {
459             super();
460         }
461     }
462 
463     public static final class HmacSHA1AndAES_128 extends PBES2Parameters {
464         public HmacSHA1AndAES_128() throws NoSuchAlgorithmException {
465             super("PBEWithHmacSHA1AndAES_128");
466         }
467     }
468 
469     public static final class HmacSHA224AndAES_128 extends PBES2Parameters {
470         public HmacSHA224AndAES_128() throws NoSuchAlgorithmException {
471             super("PBEWithHmacSHA224AndAES_128");
472         }
473     }
474 
475     public static final class HmacSHA256AndAES_128 extends PBES2Parameters {
476         public HmacSHA256AndAES_128() throws NoSuchAlgorithmException {
477             super("PBEWithHmacSHA256AndAES_128");
478         }
479     }
480 
481     public static final class HmacSHA384AndAES_128 extends PBES2Parameters {
482         public HmacSHA384AndAES_128() throws NoSuchAlgorithmException {
483             super("PBEWithHmacSHA384AndAES_128");
484         }
485     }
486 
487     public static final class HmacSHA512AndAES_128 extends PBES2Parameters {
488         public HmacSHA512AndAES_128() throws NoSuchAlgorithmException {
489             super("PBEWithHmacSHA512AndAES_128");
490         }
491     }
492 
493     public static final class HmacSHA1AndAES_256 extends PBES2Parameters {
494         public HmacSHA1AndAES_256() throws NoSuchAlgorithmException {
495             super("PBEWithHmacSHA1AndAES_256");
496         }
497     }
498 
499     public static final class HmacSHA224AndAES_256 extends PBES2Parameters {
500         public HmacSHA224AndAES_256() throws NoSuchAlgorithmException {
501             super("PBEWithHmacSHA224AndAES_256");
502         }
503     }
504 
505     public static final class HmacSHA256AndAES_256 extends PBES2Parameters {
506         public HmacSHA256AndAES_256() throws NoSuchAlgorithmException {
507             super("PBEWithHmacSHA256AndAES_256");
508         }
509     }
510 
511     public static final class HmacSHA384AndAES_256 extends PBES2Parameters {
512         public HmacSHA384AndAES_256() throws NoSuchAlgorithmException {
513             super("PBEWithHmacSHA384AndAES_256");
514         }
515     }
516 
517     public static final class HmacSHA512AndAES_256 extends PBES2Parameters {
518         public HmacSHA512AndAES_256() throws NoSuchAlgorithmException {
519             super("PBEWithHmacSHA512AndAES_256");
520         }
521     }
522 }