blob: b906da65591ce26095ed2a5a43863c833ca9feca [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.crypto;
27
28import java.io.*;
29import java.security.*;
30import java.security.spec.*;
31import sun.security.x509.AlgorithmId;
32import sun.security.util.DerValue;
33import sun.security.util.DerInputStream;
34import 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
58public 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 */
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}/../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 */
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 */
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}/../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 */
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 */
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 */
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 */
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
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 */
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 */
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 */
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 */
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
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
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}