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