• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2001, 2007, 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 javax.crypto;
27 
28 import java.io.*;
29 import java.security.*;
30 import java.security.spec.*;
31 import sun.security.x509.AlgorithmId;
32 import sun.security.util.DerValue;
33 import sun.security.util.DerInputStream;
34 import sun.security.util.DerOutputStream;
35 
36 /**
37  * This class implements the <code>EncryptedPrivateKeyInfo</code> type
38  * as defined in PKCS #8.
39  * <p>Its ASN.1 definition is as follows:
40  *
41  * <pre>
42  * EncryptedPrivateKeyInfo ::=  SEQUENCE {
43  *     encryptionAlgorithm   AlgorithmIdentifier,
44  *     encryptedData   OCTET STRING }
45  *
46  * AlgorithmIdentifier  ::=  SEQUENCE  {
47  *     algorithm              OBJECT IDENTIFIER,
48  *     parameters             ANY DEFINED BY algorithm OPTIONAL  }
49  * </pre>
50  *
51  * @author Valerie Peng
52  *
53  * @see java.security.spec.PKCS8EncodedKeySpec
54  *
55  * @since 1.4
56  */
57 
58 public class EncryptedPrivateKeyInfo {
59 
60     // the "encryptionAlgorithm" field
61     private AlgorithmId algid;
62 
63     // the "encryptedData" field
64     private byte[] encryptedData;
65 
66     // the ASN.1 encoded contents of this class
67     private byte[] encoded = null;
68 
69     /**
70      * Constructs (i.e., parses) an <code>EncryptedPrivateKeyInfo</code> from
71      * its ASN.1 encoding.
72      * @param encoded the ASN.1 encoding of this object. The contents of
73      * the array are copied to protect against subsequent modification.
74      * @exception NullPointerException if the <code>encoded</code> is null.
75      * @exception IOException if error occurs when parsing the ASN.1 encoding.
76      */
EncryptedPrivateKeyInfo(byte[] encoded)77     public EncryptedPrivateKeyInfo(byte[] encoded)
78         throws IOException {
79         if (encoded == null) {
80             throw new NullPointerException("the encoded parameter " +
81                                            "must be non-null");
82         }
83         this.encoded = (byte[])encoded.clone();
84         DerValue val = new DerValue(this.encoded);
85 
86         DerValue[] seq = new DerValue[2];
87 
88         seq[0] = val.data.getDerValue();
89         seq[1] = val.data.getDerValue();
90 
91         if (val.data.available() != 0) {
92             throw new IOException("overrun, bytes = " + val.data.available());
93         }
94 
95         this.algid = AlgorithmId.parse(seq[0]);
96         if (seq[0].data.available() != 0) {
97             throw new IOException("encryptionAlgorithm field overrun");
98         }
99 
100         this.encryptedData = seq[1].getOctetString();
101         if (seq[1].data.available() != 0) {
102             throw new IOException("encryptedData field overrun");
103         }
104     }
105 
106     /**
107      * Constructs an <code>EncryptedPrivateKeyInfo</code> from the
108      * encryption algorithm name and the encrypted data.
109      *
110      * <p>Note: This constructor will use null as the value of the
111      * algorithm parameters. If the encryption algorithm has
112      * parameters whose value is not null, a different constructor,
113      * e.g. EncryptedPrivateKeyInfo(AlgorithmParameters, byte[]),
114      * should be used.
115      *
116      * @param algName encryption algorithm name. See Appendix A in the
117      * <a href=
118      *   "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html#AppA">
119      * Java Cryptography Architecture Reference Guide</a>
120      * for information about standard Cipher algorithm names.
121      * @param encryptedData encrypted data. The contents of
122      * <code>encrypedData</code> are copied to protect against subsequent
123      * modification when constructing this object.
124      * @exception NullPointerException if <code>algName</code> or
125      * <code>encryptedData</code> is null.
126      * @exception IllegalArgumentException if <code>encryptedData</code>
127      * is empty, i.e. 0-length.
128      * @exception NoSuchAlgorithmException if the specified algName is
129      * not supported.
130      */
EncryptedPrivateKeyInfo(String algName, byte[] encryptedData)131     public EncryptedPrivateKeyInfo(String algName, byte[] encryptedData)
132         throws NoSuchAlgorithmException {
133 
134         if (algName == null)
135                 throw new NullPointerException("the algName parameter " +
136                                                "must be non-null");
137         this.algid = AlgorithmId.get(algName);
138 
139         if (encryptedData == null) {
140             throw new NullPointerException("the encryptedData " +
141                                            "parameter must be non-null");
142         } else if (encryptedData.length == 0) {
143             throw new IllegalArgumentException("the encryptedData " +
144                                                 "parameter must not be empty");
145         } else {
146             this.encryptedData = (byte[])encryptedData.clone();
147         }
148         // delay the generation of ASN.1 encoding until
149         // getEncoded() is called
150         this.encoded = null;
151     }
152 
153     /**
154      * Constructs an <code>EncryptedPrivateKeyInfo</code> from the
155      * encryption algorithm parameters and the encrypted data.
156      *
157      * @param algParams the algorithm parameters for the encryption
158      * algorithm. <code>algParams.getEncoded()</code> should return
159      * the ASN.1 encoded bytes of the <code>parameters</code> field
160      * of the <code>AlgorithmIdentifer</code> component of the
161      * <code>EncryptedPrivateKeyInfo</code> type.
162      * @param encryptedData encrypted data. The contents of
163      * <code>encrypedData</code> are copied to protect against
164      * subsequent modification when constructing this object.
165      * @exception NullPointerException if <code>algParams</code> or
166      * <code>encryptedData</code> is null.
167      * @exception IllegalArgumentException if <code>encryptedData</code>
168      * is empty, i.e. 0-length.
169      * @exception NoSuchAlgorithmException if the specified algName of
170      * the specified <code>algParams</code> parameter is not supported.
171      */
EncryptedPrivateKeyInfo(AlgorithmParameters algParams, byte[] encryptedData)172     public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,
173         byte[] encryptedData) throws NoSuchAlgorithmException {
174 
175         if (algParams == null) {
176             throw new NullPointerException("algParams must be non-null");
177         }
178         this.algid = AlgorithmId.get(algParams);
179 
180         if (encryptedData == null) {
181             throw new NullPointerException("encryptedData must be non-null");
182         } else if (encryptedData.length == 0) {
183             throw new IllegalArgumentException("the encryptedData " +
184                                                 "parameter must not be empty");
185         } else {
186             this.encryptedData = (byte[])encryptedData.clone();
187         }
188 
189         // delay the generation of ASN.1 encoding until
190         // getEncoded() is called
191         this.encoded = null;
192     }
193 
194 
195     /**
196      * Returns the encryption algorithm.
197      * <p>Note: Standard name is returned instead of the specified one
198      * in the constructor when such mapping is available.
199      * See Appendix A in the
200      * <a href=
201      *   "{@docRoot}openjdk-redirect.html?v=8&path=/technotes/guides/security/crypto/CryptoSpec.html#AppA">
202      * Java Cryptography Architecture Reference Guide</a>
203      * for information about standard Cipher algorithm names.
204      *
205      * @return the encryption algorithm name.
206      */
getAlgName()207     public String getAlgName() {
208         return this.algid.getName();
209     }
210 
211     /**
212      * Returns the algorithm parameters used by the encryption algorithm.
213      * @return the algorithm parameters.
214      */
getAlgParameters()215     public AlgorithmParameters getAlgParameters() {
216         return this.algid.getParameters();
217     }
218 
219     /**
220      * Returns the encrypted data.
221      * @return the encrypted data. Returns a new array
222      * each time this method is called.
223      */
getEncryptedData()224     public byte[] getEncryptedData() {
225         return (byte[])this.encryptedData.clone();
226     }
227 
228     /**
229      * Extract the enclosed PKCS8EncodedKeySpec object from the
230      * encrypted data and return it.
231      * <br>Note: In order to successfully retrieve the enclosed
232      * PKCS8EncodedKeySpec object, <code>cipher</code> needs
233      * to be initialized to either Cipher.DECRYPT_MODE or
234      * Cipher.UNWRAP_MODE, with the same key and parameters used
235      * for generating the encrypted data.
236      *
237      * @param cipher the initialized cipher object which will be
238      * used for decrypting the encrypted data.
239      * @return the PKCS8EncodedKeySpec object.
240      * @exception NullPointerException if <code>cipher</code>
241      * is null.
242      * @exception InvalidKeySpecException if the given cipher is
243      * inappropriate for the encrypted data or the encrypted
244      * data is corrupted and cannot be decrypted.
245      */
getKeySpec(Cipher cipher)246     public PKCS8EncodedKeySpec getKeySpec(Cipher cipher)
247         throws InvalidKeySpecException {
248         byte[] encoded = null;
249         try {
250             encoded = cipher.doFinal((byte[])encryptedData);
251             checkPKCS8Encoding(encoded);
252         } catch (GeneralSecurityException gse) {
253             InvalidKeySpecException ikse = new
254                 InvalidKeySpecException(
255                     "Cannot retrieve the PKCS8EncodedKeySpec");
256             ikse.initCause(gse);
257             throw ikse;
258         } catch (IOException ioe) {
259             InvalidKeySpecException ikse = new
260                 InvalidKeySpecException(
261                     "Cannot retrieve the PKCS8EncodedKeySpec");
262             ikse.initCause(ioe);
263             throw ikse;
264         } catch (IllegalStateException ise) {
265             InvalidKeySpecException ikse = new
266                 InvalidKeySpecException(
267                     "Cannot retrieve the PKCS8EncodedKeySpec");
268             ikse.initCause(ise);
269             throw ikse;
270         }
271         return new PKCS8EncodedKeySpec(encoded);
272     }
273 
getKeySpecImpl(Key decryptKey, Provider provider)274     private PKCS8EncodedKeySpec getKeySpecImpl(Key decryptKey,
275         Provider provider) throws NoSuchAlgorithmException,
276         InvalidKeyException {
277         byte[] encoded = null;
278         Cipher c;
279         try {
280             if (provider == null) {
281                 // use the most preferred one
282                 c = Cipher.getInstance(algid.getName());
283             } else {
284                 c = Cipher.getInstance(algid.getName(), provider);
285             }
286             c.init(Cipher.DECRYPT_MODE, decryptKey, algid.getParameters());
287             encoded = c.doFinal(encryptedData);
288             checkPKCS8Encoding(encoded);
289         } catch (NoSuchAlgorithmException nsae) {
290             // rethrow
291             throw nsae;
292         } catch (GeneralSecurityException gse) {
293             InvalidKeyException ike = new InvalidKeyException
294                 ("Cannot retrieve the PKCS8EncodedKeySpec");
295             ike.initCause(gse);
296             throw ike;
297         } catch (IOException ioe) {
298             InvalidKeyException ike = new InvalidKeyException
299                 ("Cannot retrieve the PKCS8EncodedKeySpec");
300             ike.initCause(ioe);
301             throw ike;
302         }
303         return new PKCS8EncodedKeySpec(encoded);
304     }
305 
306     /**
307      * Extract the enclosed PKCS8EncodedKeySpec object from the
308      * encrypted data and return it.
309      * @param decryptKey key used for decrypting the encrypted data.
310      * @return the PKCS8EncodedKeySpec object.
311      * @exception NullPointerException if <code>decryptKey</code>
312      * is null.
313      * @exception NoSuchAlgorithmException if cannot find appropriate
314      * cipher to decrypt the encrypted data.
315      * @exception InvalidKeyException if <code>decryptKey</code>
316      * cannot be used to decrypt the encrypted data or the decryption
317      * result is not a valid PKCS8KeySpec.
318      *
319      * @since 1.5
320      */
getKeySpec(Key decryptKey)321     public PKCS8EncodedKeySpec getKeySpec(Key decryptKey)
322         throws NoSuchAlgorithmException, InvalidKeyException {
323         if (decryptKey == null) {
324             throw new NullPointerException("decryptKey is null");
325         }
326         return getKeySpecImpl(decryptKey, null);
327     }
328 
329     /**
330      * Extract the enclosed PKCS8EncodedKeySpec object from the
331      * encrypted data and return it.
332      * @param decryptKey key used for decrypting the encrypted data.
333      * @param providerName the name of provider whose Cipher
334      * implementation will be used.
335      * @return the PKCS8EncodedKeySpec object.
336      * @exception NullPointerException if <code>decryptKey</code>
337      * or <code>providerName</code> is null.
338      * @exception NoSuchProviderException if no provider
339      * <code>providerName</code> is registered.
340      * @exception NoSuchAlgorithmException if cannot find appropriate
341      * cipher to decrypt the encrypted data.
342      * @exception InvalidKeyException if <code>decryptKey</code>
343      * cannot be used to decrypt the encrypted data or the decryption
344      * result is not a valid PKCS8KeySpec.
345      *
346      * @since 1.5
347      */
getKeySpec(Key decryptKey, String providerName)348     public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,
349         String providerName) throws NoSuchProviderException,
350         NoSuchAlgorithmException, InvalidKeyException {
351         if (decryptKey == null) {
352             throw new NullPointerException("decryptKey is null");
353         }
354         if (providerName == null) {
355             throw new NullPointerException("provider is null");
356         }
357         Provider provider = Security.getProvider(providerName);
358         if (provider == null) {
359             throw new NoSuchProviderException("provider " +
360                 providerName + " not found");
361         }
362         return getKeySpecImpl(decryptKey, provider);
363     }
364 
365     /**
366      * Extract the enclosed PKCS8EncodedKeySpec object from the
367      * encrypted data and return it.
368      * @param decryptKey key used for decrypting the encrypted data.
369      * @param provider the name of provider whose Cipher implementation
370      * will be used.
371      * @return the PKCS8EncodedKeySpec object.
372      * @exception NullPointerException if <code>decryptKey</code>
373      * or <code>provider</code> is null.
374      * @exception NoSuchAlgorithmException if cannot find appropriate
375      * cipher to decrypt the encrypted data in <code>provider</code>.
376      * @exception InvalidKeyException if <code>decryptKey</code>
377      * cannot be used to decrypt the encrypted data or the decryption
378      * result is not a valid PKCS8KeySpec.
379      *
380      * @since 1.5
381      */
getKeySpec(Key decryptKey, Provider provider)382     public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,
383         Provider provider) throws NoSuchAlgorithmException,
384         InvalidKeyException {
385         if (decryptKey == null) {
386             throw new NullPointerException("decryptKey is null");
387         }
388         if (provider == null) {
389             throw new NullPointerException("provider is null");
390         }
391         return getKeySpecImpl(decryptKey, provider);
392     }
393 
394     /**
395      * Returns the ASN.1 encoding of this object.
396      * @return the ASN.1 encoding. Returns a new array
397      * each time this method is called.
398      * @exception IOException if error occurs when constructing its
399      * ASN.1 encoding.
400      */
getEncoded()401     public byte[] getEncoded() throws IOException {
402         if (this.encoded == null) {
403             DerOutputStream out = new DerOutputStream();
404             DerOutputStream tmp = new DerOutputStream();
405 
406             // encode encryption algorithm
407             algid.encode(tmp);
408 
409             // encode encrypted data
410             tmp.putOctetString(encryptedData);
411 
412             // wrap everything into a SEQUENCE
413             out.write(DerValue.tag_Sequence, tmp);
414             this.encoded = out.toByteArray();
415         }
416         return (byte[])this.encoded.clone();
417     }
418 
checkTag(DerValue val, byte tag, String valName)419     private static void checkTag(DerValue val, byte tag, String valName)
420         throws IOException {
421         if (val.getTag() != tag) {
422             throw new IOException("invalid key encoding - wrong tag for " +
423                                   valName);
424         }
425     }
426 
checkPKCS8Encoding(byte[] encodedKey)427     private static void checkPKCS8Encoding(byte[] encodedKey)
428         throws IOException {
429         DerInputStream in = new DerInputStream(encodedKey);
430         DerValue[] values = in.getSequence(3);
431 
432         switch (values.length) {
433         case 4:
434             checkTag(values[3], DerValue.TAG_CONTEXT, "attributes");
435         case 3:
436             checkTag(values[0], DerValue.tag_Integer, "version");
437             DerInputStream algid = values[1].toDerInputStream();
438             algid.getOID();
439             if (algid.available() != 0) {
440                 algid.getDerValue();
441             }
442             checkTag(values[2], DerValue.tag_OctetString, "privateKey");
443             break;
444         default:
445             throw new IOException("invalid key encoding");
446         }
447     }
448 }
449