• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package javax.crypto;
19 
20 import java.io.IOException;
21 import java.security.AlgorithmParameters;
22 import java.security.InvalidAlgorithmParameterException;
23 import java.security.InvalidKeyException;
24 import java.security.Key;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.NoSuchProviderException;
27 import java.security.Provider;
28 import java.security.spec.InvalidKeySpecException;
29 import java.security.spec.PKCS8EncodedKeySpec;
30 import org.apache.harmony.security.asn1.ASN1Any;
31 import org.apache.harmony.security.asn1.ASN1Implicit;
32 import org.apache.harmony.security.asn1.ASN1Integer;
33 import org.apache.harmony.security.asn1.ASN1OctetString;
34 import org.apache.harmony.security.asn1.ASN1Sequence;
35 import org.apache.harmony.security.asn1.ASN1SetOf;
36 import org.apache.harmony.security.asn1.ASN1Type;
37 import org.apache.harmony.security.utils.AlgNameMapper;
38 import org.apache.harmony.security.x509.AlgorithmIdentifier;
39 
40 
41 /**
42  * This class implements the {@code EncryptedPrivateKeyInfo} ASN.1 type as
43  * specified in <a href="http://www.ietf.org/rfc/rfc5208.txt">PKCS
44  * #8 - Private-Key Information Syntax Standard</a>.
45  * <p>
46  * The definition of ASN.1 is as follows:
47  * <dl>
48  * EncryptedPrivateKeyInfo ::= SEQUENCE {
49  * <dd>encryptionAlgorithm AlgorithmIdentifier,</dd>
50  * <dd>encryptedData OCTET STRING }</dd>
51  * </dl>
52  * <dl>
53  * AlgorithmIdentifier ::= SEQUENCE {
54  * <dd>algorithm OBJECT IDENTIFIER,</dd>
55  * <dd>parameters ANY DEFINED BY algorithm OPTIONAL }</dd>
56  * </dl>
57  */
58 public class EncryptedPrivateKeyInfo {
59     // Encryption algorithm name
60     private String algName;
61     // Encryption algorithm parameters
62     private final AlgorithmParameters algParameters;
63     // Encrypted private key data
64     private final byte[] encryptedData;
65     // Encryption algorithm OID
66     private String oid;
67     // This EncryptedPrivateKeyInfo ASN.1 DER encoding
68     private volatile byte[] encoded;
69 
70     /**
71      * Creates an {@code EncryptedPrivateKeyInfo} instance from its encoded
72      * representation by parsing it.
73      *
74      * @param encoded
75      *            the encoded representation of this object
76      * @throws IOException
77      *             if parsing the encoded representation fails.
78      * @throws NullPointerException
79      *             if {@code encoded} is {@code null}.
80      */
EncryptedPrivateKeyInfo(byte[] encoded)81     public EncryptedPrivateKeyInfo(byte[] encoded) throws IOException {
82         if (encoded == null) {
83             throw new NullPointerException("encoded == null");
84         }
85         this.encoded = new byte[encoded.length];
86         System.arraycopy(encoded, 0, this.encoded, 0, encoded.length);
87         Object[] values;
88 
89         values = (Object[])asn1.decode(encoded);
90 
91         AlgorithmIdentifier aId = (AlgorithmIdentifier) values[0];
92 
93         algName = aId.getAlgorithm();
94         // algName == oid now
95         boolean mappingExists = mapAlgName();
96         // algName == name from map oid->name if mapping exists, or
97         // algName == oid if mapping does not exist
98 
99         AlgorithmParameters aParams = null;
100         byte[] params = aId.getParameters();
101         if (params != null && !isNullValue(params)) {
102             try {
103                 aParams = AlgorithmParameters.getInstance(algName);
104                 aParams.init(aId.getParameters());
105                 if (!mappingExists) {
106                     algName = aParams.getAlgorithm();
107                 }
108             } catch (NoSuchAlgorithmException e) {
109             }
110         }
111         algParameters = aParams;
112 
113         encryptedData = (byte[]) values[1];
114     }
115 
isNullValue(byte[] toCheck)116     private static boolean isNullValue(byte[] toCheck) {
117         return toCheck[0] == 5 && toCheck[1] == 0;
118     }
119 
120     /**
121      * Creates an {@code EncryptedPrivateKeyInfo} instance from an algorithm
122      * name and its encrypted data.
123      *
124      * @param encryptionAlgorithmName
125      *            the name of an algorithm.
126      * @param encryptedData
127      *            the encrypted data.
128      * @throws NoSuchAlgorithmException
129      *             if the {@code encrAlgName} is not a supported algorithm.
130      * @throws NullPointerException
131      *             if {@code encrAlgName} or {@code encryptedData} is {@code
132      *             null}.
133      * @throws IllegalArgumentException
134      *             if {@code encryptedData} is empty.
135      */
EncryptedPrivateKeyInfo(String encryptionAlgorithmName, byte[] encryptedData)136     public EncryptedPrivateKeyInfo(String encryptionAlgorithmName, byte[] encryptedData)
137         throws NoSuchAlgorithmException {
138         if (encryptionAlgorithmName == null) {
139             throw new NullPointerException("encryptionAlgorithmName == null");
140         }
141         this.algName = encryptionAlgorithmName;
142         if (!mapAlgName()) {
143             throw new NoSuchAlgorithmException("Unsupported algorithm: " + this.algName);
144         }
145         if (encryptedData == null) {
146             throw new NullPointerException("encryptedData == null");
147         }
148         if (encryptedData.length == 0) {
149             throw new IllegalArgumentException("encryptedData.length == 0");
150         }
151         this.encryptedData = new byte[encryptedData.length];
152         System.arraycopy(encryptedData, 0,
153                 this.encryptedData, 0, encryptedData.length);
154         this.algParameters = null;
155     }
156 
157     /**
158      * Creates an {@code EncryptedPrivateKeyInfo} instance from the
159      * encryption algorithm parameters an its encrypted data.
160      *
161      * @param algParams
162      *            the encryption algorithm parameters.
163      * @param encryptedData
164      *            the encrypted data.
165      * @throws NoSuchAlgorithmException
166      *             if the algorithm name of the specified {@code algParams}
167      *             parameter is not supported.
168      * @throws NullPointerException
169      *             if {@code algParams} or {@code encryptedData} is
170      *             {@code null}.
171      */
EncryptedPrivateKeyInfo(AlgorithmParameters algParams, byte[] encryptedData)172     public EncryptedPrivateKeyInfo(AlgorithmParameters algParams, byte[] encryptedData)
173         throws NoSuchAlgorithmException {
174         if (algParams == null) {
175             throw new NullPointerException("algParams == null");
176         }
177         this.algParameters = algParams;
178         if (encryptedData == null) {
179             throw new NullPointerException("encryptedData == null");
180         }
181         if (encryptedData.length == 0) {
182             throw new IllegalArgumentException("encryptedData.length == 0");
183         }
184         this.encryptedData = new byte[encryptedData.length];
185         System.arraycopy(encryptedData, 0,
186                 this.encryptedData, 0, encryptedData.length);
187         this.algName = this.algParameters.getAlgorithm();
188         if (!mapAlgName()) {
189             throw new NoSuchAlgorithmException("Unsupported algorithm: " + this.algName);
190         }
191     }
192 
193     /**
194      * Returns the name of the encryption algorithm.
195      *
196      * @return the name of the encryption algorithm.
197      */
getAlgName()198     public String getAlgName() {
199         return algName;
200     }
201 
202     /**
203      * Returns the parameters used by the encryption algorithm.
204      *
205      * @return the parameters used by the encryption algorithm.
206      */
getAlgParameters()207     public AlgorithmParameters getAlgParameters() {
208         return algParameters;
209     }
210 
211     /**
212      * Returns the encrypted data of this key.
213      *
214      * @return the encrypted data of this key, each time this method is called a
215      *         new array is returned.
216      */
getEncryptedData()217     public byte[] getEncryptedData() {
218         byte[] ret = new byte[encryptedData.length];
219         System.arraycopy(encryptedData, 0, ret, 0, encryptedData.length);
220         return ret;
221     }
222 
223     /**
224      * Returns the {@code PKCS8EncodedKeySpec} object extracted from the
225      * encrypted data.
226      * <p>
227      * The cipher must be initialize in either {@code Cipher.DECRYPT_MODE} or
228      * {@code Cipher.UNWRAP_MODE} with the same parameters and key used for
229      * encrypting this.
230      *
231      * @param cipher
232      *            the cipher initialized for decrypting the encrypted data.
233      * @return the extracted {@code PKCS8EncodedKeySpec}.
234      * @throws InvalidKeySpecException
235      *             if the specified cipher is not suited to decrypt the
236      *             encrypted data.
237      * @throws NullPointerException
238      *             if {@code cipher} is {@code null}.
239      */
getKeySpec(Cipher cipher)240     public PKCS8EncodedKeySpec getKeySpec(Cipher cipher)
241         throws InvalidKeySpecException {
242         if (cipher == null) {
243             throw new NullPointerException("cipher == null");
244         }
245         try {
246             byte[] decryptedData = cipher.doFinal(encryptedData);
247             try {
248                 ASN1PrivateKeyInfo.verify(decryptedData);
249             } catch (IOException e1) {
250                 throw new InvalidKeySpecException("Decrypted data does not represent valid PKCS#8 PrivateKeyInfo");
251             }
252             return new PKCS8EncodedKeySpec(decryptedData);
253         } catch (IllegalStateException e) {
254             throw new InvalidKeySpecException(e.getMessage());
255         } catch (IllegalBlockSizeException e) {
256             throw new InvalidKeySpecException(e.getMessage());
257         } catch (BadPaddingException e) {
258             throw new InvalidKeySpecException(e.getMessage());
259         }
260     }
261 
262     /**
263      * Returns the {@code PKCS8EncodedKeySpec} object extracted from the
264      * encrypted data.
265      *
266      * @param decryptKey
267      *            the key to decrypt the encrypted data with.
268      * @return the extracted {@code PKCS8EncodedKeySpec}.
269      * @throws NoSuchAlgorithmException
270      *             if no usable cipher can be found to decrypt the encrypted
271      *             data.
272      * @throws InvalidKeyException
273      *             if {@code decryptKey} is not usable to decrypt the encrypted
274      *             data.
275      * @throws NullPointerException
276      *             if {@code decryptKey} is {@code null}.
277      */
getKeySpec(Key decryptKey)278     public PKCS8EncodedKeySpec getKeySpec(Key decryptKey) throws NoSuchAlgorithmException,
279                InvalidKeyException {
280         if (decryptKey == null) {
281             throw new NullPointerException("decryptKey == null");
282         }
283         try {
284             Cipher cipher = Cipher.getInstance(algName);
285             if (algParameters == null) {
286                 cipher.init(Cipher.DECRYPT_MODE, decryptKey);
287             } else {
288                 cipher.init(Cipher.DECRYPT_MODE, decryptKey, algParameters);
289             }
290             byte[] decryptedData = cipher.doFinal(encryptedData);
291             try {
292                 ASN1PrivateKeyInfo.verify(decryptedData);
293             } catch (IOException e1) {
294                 throw invalidKey();
295             }
296             return new PKCS8EncodedKeySpec(decryptedData);
297         } catch (NoSuchPaddingException e) {
298             throw new NoSuchAlgorithmException(e.getMessage());
299         } catch (InvalidAlgorithmParameterException e) {
300             throw new NoSuchAlgorithmException(e.getMessage());
301         } catch (IllegalStateException e) {
302             throw new InvalidKeyException(e.getMessage());
303         } catch (IllegalBlockSizeException e) {
304             throw new InvalidKeyException(e.getMessage());
305         } catch (BadPaddingException e) {
306             throw new InvalidKeyException(e.getMessage());
307         }
308     }
309 
310     /**
311      * Returns the {@code PKCS8EncodedKeySpec} object extracted from the
312      * encrypted data.
313      *
314      * @param decryptKey
315      *            the key to decrypt the encrypted data with.
316      * @param providerName
317      *            the name of a provider whose cipher implementation should be
318      *            used.
319      * @return the extracted {@code PKCS8EncodedKeySpec}.
320      * @throws NoSuchProviderException
321      *             if no provider with {@code providerName} can be found.
322      * @throws NoSuchAlgorithmException
323      *             if no usable cipher can be found to decrypt the encrypted
324      *             data.
325      * @throws InvalidKeyException
326      *             if {@code decryptKey} is not usable to decrypt the encrypted
327      *             data.
328      * @throws NullPointerException
329      *             if {@code decryptKey} or {@code providerName} is {@code null}
330      *             .
331      */
getKeySpec(Key decryptKey, String providerName)332     public PKCS8EncodedKeySpec getKeySpec(Key decryptKey, String providerName)
333         throws NoSuchProviderException,
334                NoSuchAlgorithmException,
335                InvalidKeyException {
336         if (decryptKey == null) {
337             throw new NullPointerException("decryptKey == null");
338         }
339         if (providerName == null) {
340             throw new NullPointerException("providerName == null");
341         }
342         try {
343             Cipher cipher = Cipher.getInstance(algName, providerName);
344             if (algParameters == null) {
345                 cipher.init(Cipher.DECRYPT_MODE, decryptKey);
346             } else {
347                 cipher.init(Cipher.DECRYPT_MODE, decryptKey, algParameters);
348             }
349             byte[] decryptedData = cipher.doFinal(encryptedData);
350             try {
351                 ASN1PrivateKeyInfo.verify(decryptedData);
352             } catch (IOException e1) {
353                 throw invalidKey();
354             }
355             return new PKCS8EncodedKeySpec(decryptedData);
356         } catch (NoSuchPaddingException e) {
357             throw new NoSuchAlgorithmException(e.getMessage());
358         } catch (InvalidAlgorithmParameterException e) {
359             throw new NoSuchAlgorithmException(e.getMessage());
360         } catch (IllegalStateException e) {
361             throw new InvalidKeyException(e.getMessage());
362         } catch (IllegalBlockSizeException e) {
363             throw new InvalidKeyException(e.getMessage());
364         } catch (BadPaddingException e) {
365             throw new InvalidKeyException(e.getMessage());
366         }
367     }
368 
369     /**
370      * Returns the {@code PKCS8EncodedKeySpec} object extracted from the
371      * encrypted data.
372      *
373      * @param decryptKey
374      *            the key to decrypt the encrypted data with.
375      * @param provider
376      *            the provider whose cipher implementation should be used.
377      * @return the extracted {@code PKCS8EncodedKeySpec}.
378      * @throws NoSuchAlgorithmException
379      *             if no usable cipher can be found to decrypt the encrypted
380      *             data.
381      * @throws InvalidKeyException
382      *             if {@code decryptKey} is not usable to decrypt the encrypted
383      *             data.
384      * @throws NullPointerException
385      *             if {@code decryptKey} or {@code provider} is {@code null}.
386      */
getKeySpec(Key decryptKey, Provider provider)387     public PKCS8EncodedKeySpec getKeySpec(Key decryptKey, Provider provider)
388         throws NoSuchAlgorithmException,
389                InvalidKeyException {
390         if (decryptKey == null) {
391             throw new NullPointerException("decryptKey == null");
392         }
393         if (provider == null) {
394             throw new NullPointerException("provider == null");
395         }
396         try {
397             Cipher cipher = Cipher.getInstance(algName, provider);
398             if (algParameters == null) {
399                 cipher.init(Cipher.DECRYPT_MODE, decryptKey);
400             } else {
401                 cipher.init(Cipher.DECRYPT_MODE, decryptKey, algParameters);
402             }
403             byte[] decryptedData = cipher.doFinal(encryptedData);
404             try {
405                 ASN1PrivateKeyInfo.verify(decryptedData);
406             } catch (IOException e1) {
407                 throw invalidKey();
408             }
409             return new PKCS8EncodedKeySpec(decryptedData);
410         } catch (NoSuchPaddingException e) {
411             throw new NoSuchAlgorithmException(e.getMessage());
412         } catch (InvalidAlgorithmParameterException e) {
413             throw new NoSuchAlgorithmException(e.getMessage());
414         } catch (IllegalStateException e) {
415             throw new InvalidKeyException(e.getMessage());
416         } catch (IllegalBlockSizeException e) {
417             throw new InvalidKeyException(e.getMessage());
418         } catch (BadPaddingException e) {
419             throw new InvalidKeyException(e.getMessage());
420         }
421     }
422 
invalidKey()423     private InvalidKeyException invalidKey() throws InvalidKeyException {
424         throw new InvalidKeyException("Decrypted data does not represent valid PKCS#8 PrivateKeyInfo");
425     }
426 
427     /**
428      * Returns the ASN.1 encoded representation of this object.
429      *
430      * @return the ASN.1 encoded representation of this object.
431      * @throws IOException
432      *             if encoding this object fails.
433      */
getEncoded()434     public byte[] getEncoded() throws IOException {
435         if (encoded == null) {
436             // Generate ASN.1 encoding:
437             encoded = asn1.encode(this);
438         }
439         byte[] ret = new byte[encoded.length];
440         System.arraycopy(encoded, 0, ret, 0, encoded.length);
441         return ret;
442     }
443 
444     // Performs all needed alg name mappings.
445     // Returns 'true' if mapping available 'false' otherwise
mapAlgName()446     private boolean mapAlgName() {
447         if (AlgNameMapper.isOID(this.algName)) {
448             // OID provided to the ctor
449             // get rid of possible leading "OID."
450             this.oid = AlgNameMapper.normalize(this.algName);
451             // try to find mapping OID->algName
452             this.algName = AlgNameMapper.map2AlgName(this.oid);
453             // if there is no mapping OID->algName
454             // set OID as algName
455             if (this.algName == null) {
456                 this.algName = this.oid;
457             }
458         } else {
459             String stdName = AlgNameMapper.getStandardName(this.algName);
460             // Alg name provided to the ctor
461             // try to find mapping algName->OID or
462             // (algName->stdAlgName)->OID
463             this.oid = AlgNameMapper.map2OID(this.algName);
464             if (this.oid == null) {
465                 if (stdName == null) {
466                     // no above mappings available
467                     return false;
468                 }
469                 this.oid = AlgNameMapper.map2OID(stdName);
470                 if (this.oid == null) {
471                     return false;
472                 }
473                 this.algName = stdName;
474             } else if (stdName != null) {
475                 this.algName = stdName;
476             }
477         }
478         return true;
479     }
480 
481     //
482     // EncryptedPrivateKeyInfo DER encoder/decoder.
483     // EncryptedPrivateKeyInfo ASN.1 definition
484     // (as defined in PKCS #8: Private-Key Information Syntax Standard
485     //  http://www.ietf.org/rfc/rfc2313.txt)
486     //
487     // EncryptedPrivateKeyInfo ::=  SEQUENCE {
488     //      encryptionAlgorithm   AlgorithmIdentifier,
489     //      encryptedData   OCTET STRING }
490     //
491 
492     private static final byte[] nullParam = new byte[] { 5, 0 };
493 
494     private static final ASN1Sequence asn1 = new ASN1Sequence(new ASN1Type[] {
495             AlgorithmIdentifier.ASN1, ASN1OctetString.getInstance() }) {
496 
497                 @Override
498                 protected void getValues(Object object, Object[] values) {
499 
500                     EncryptedPrivateKeyInfo epki = (EncryptedPrivateKeyInfo) object;
501 
502                     try {
503                         byte[] algParmsEncoded = (epki.algParameters == null) ? nullParam
504                                 : epki.algParameters.getEncoded();
505                         values[0] = new AlgorithmIdentifier(epki.oid, algParmsEncoded);
506                         values[1] = epki.encryptedData;
507                     } catch (IOException e) {
508                         throw new RuntimeException(e.getMessage());
509                     }
510                 }
511     };
512 
513     // PrivateKeyInfo DER decoder.
514     // PrivateKeyInfo ASN.1 definition
515     // (as defined in PKCS #8: Private-Key Information Syntax Standard
516     //  http://www.ietf.org/rfc/rfc2313.txt)
517     //
518     //
519     //    PrivateKeyInfo ::= SEQUENCE {
520     //        version Version,
521     //        privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
522     //        privateKey PrivateKey,
523     //        attributes [0] IMPLICIT Attributes OPTIONAL }
524     //
525     //      Version ::= INTEGER
526     //
527     //      PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
528     //
529     //      PrivateKey ::= OCTET STRING
530     //
531     //      Attributes ::= SET OF Attribute
532 
533     private static final ASN1SetOf ASN1Attributes = new ASN1SetOf(ASN1Any.getInstance());
534 
535     private static final ASN1Sequence ASN1PrivateKeyInfo = new ASN1Sequence(
536             new ASN1Type[] { ASN1Integer.getInstance(), AlgorithmIdentifier.ASN1,
537                     ASN1OctetString.getInstance(),
538                     new ASN1Implicit(0, ASN1Attributes) }) {
539         {
540             setOptional(3); //attributes are optional
541         }
542     };
543 }
544