001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Vladimir N. Molotkov, Stepan M. Mishura
020: * @version $Revision$
021: */package javax.crypto;
022:
023: import java.io.IOException;
024: import java.security.AlgorithmParameters;
025: import java.security.InvalidAlgorithmParameterException;
026: import java.security.InvalidKeyException;
027: import java.security.Key;
028: import java.security.NoSuchAlgorithmException;
029: import java.security.NoSuchProviderException;
030: import java.security.Provider;
031: import java.security.spec.InvalidKeySpecException;
032: import java.security.spec.PKCS8EncodedKeySpec;
033:
034: import org.apache.harmony.crypto.internal.nls.Messages;
035: import org.apache.harmony.security.asn1.ASN1Any;
036: import org.apache.harmony.security.asn1.ASN1Implicit;
037: import org.apache.harmony.security.asn1.ASN1Integer;
038: import org.apache.harmony.security.asn1.ASN1OctetString;
039: import org.apache.harmony.security.asn1.ASN1Sequence;
040: import org.apache.harmony.security.asn1.ASN1SetOf;
041: import org.apache.harmony.security.asn1.ASN1Type;
042: import org.apache.harmony.security.utils.AlgNameMapper;
043: import org.apache.harmony.security.x509.AlgorithmIdentifier;
044:
045: /**
046: * @com.intel.drl.spec_ref
047: */
048: public class EncryptedPrivateKeyInfo {
049: // Encryption algorithm name
050: private String algName;
051: // Encryption algorithm parameters
052: private final AlgorithmParameters algParameters;
053: // Encrypted private key data
054: private final byte[] encryptedData;
055: // Encryption algorithm OID
056: private String oid;
057: // This EncryptedPrivateKeyInfo ASN.1 DER encoding
058: private volatile byte[] encoded;
059:
060: /**
061: * @com.intel.drl.spec_ref
062: */
063: public EncryptedPrivateKeyInfo(byte[] encoded) throws IOException {
064: if (encoded == null) {
065: throw new NullPointerException(Messages
066: .getString("crypto.22")); //$NON-NLS-1$
067: }
068: this .encoded = new byte[encoded.length];
069: System.arraycopy(encoded, 0, this .encoded, 0, encoded.length);
070: Object[] values;
071:
072: values = (Object[]) asn1.decode(encoded);
073:
074: AlgorithmIdentifier aId = (AlgorithmIdentifier) values[0];
075:
076: algName = aId.getAlgorithm();
077: // algName == oid now
078: boolean mappingExists = mapAlgName();
079: // algName == name from map oid->name if mapping exists, or
080: // algName == oid if mapping does not exist
081:
082: AlgorithmParameters aParams = null;
083: byte[] params = aId.getParameters();
084: if (params != null && !isNullValue(params)) {
085: try {
086: aParams = AlgorithmParameters.getInstance(algName);
087: aParams.init(aId.getParameters());
088: if (!mappingExists) {
089: algName = aParams.getAlgorithm();
090: }
091: } catch (NoSuchAlgorithmException e) {
092: }
093: }
094: algParameters = aParams;
095:
096: encryptedData = (byte[]) values[1];
097: }
098:
099: private static boolean isNullValue(byte[] toCheck) {
100: return toCheck[0] == 5 && toCheck[1] == 0;
101: }
102:
103: /**
104: * @com.intel.drl.spec_ref
105: */
106: public EncryptedPrivateKeyInfo(String encrAlgName,
107: byte[] encryptedData) throws NoSuchAlgorithmException {
108: if (encrAlgName == null) {
109: throw new NullPointerException(Messages
110: .getString("crypto.23")); //$NON-NLS-1$
111: }
112: this .algName = encrAlgName;
113: if (!mapAlgName()) {
114: throw new NoSuchAlgorithmException(Messages.getString(
115: "crypto.24", this .algName)); //$NON-NLS-1$
116: }
117: if (encryptedData == null) {
118: throw new NullPointerException(Messages
119: .getString("crypto.25")); //$NON-NLS-1$
120: }
121: if (encryptedData.length == 0) {
122: throw new IllegalArgumentException(Messages
123: .getString("crypto.26")); //$NON-NLS-1$
124: }
125: this .encryptedData = new byte[encryptedData.length];
126: System.arraycopy(encryptedData, 0, this .encryptedData, 0,
127: encryptedData.length);
128: this .algParameters = null;
129: }
130:
131: /**
132: * @com.intel.drl.spec_ref
133: */
134: public EncryptedPrivateKeyInfo(AlgorithmParameters algParams,
135: byte[] encryptedData) throws NoSuchAlgorithmException {
136: if (algParams == null) {
137: throw new NullPointerException(Messages
138: .getString("crypto.27")); //$NON-NLS-1$
139: }
140: this .algParameters = algParams;
141: if (encryptedData == null) {
142: throw new NullPointerException(Messages
143: .getString("crypto.25")); //$NON-NLS-1$
144: }
145: if (encryptedData.length == 0) {
146: throw new IllegalArgumentException(Messages
147: .getString("crypto.26")); //$NON-NLS-1$
148: }
149: this .encryptedData = new byte[encryptedData.length];
150: System.arraycopy(encryptedData, 0, this .encryptedData, 0,
151: encryptedData.length);
152: this .algName = this .algParameters.getAlgorithm();
153: if (!mapAlgName()) {
154: throw new NoSuchAlgorithmException(Messages.getString(
155: "crypto.24", this .algName)); //$NON-NLS-1$
156: }
157: }
158:
159: /**
160: * @com.intel.drl.spec_ref
161: */
162: public String getAlgName() {
163: return algName;
164: }
165:
166: /**
167: * @com.intel.drl.spec_ref
168: */
169: public AlgorithmParameters getAlgParameters() {
170: return algParameters;
171: }
172:
173: /**
174: * @com.intel.drl.spec_ref
175: */
176: public byte[] getEncryptedData() {
177: byte[] ret = new byte[encryptedData.length];
178: System
179: .arraycopy(encryptedData, 0, ret, 0,
180: encryptedData.length);
181: return ret;
182: }
183:
184: /**
185: * @com.intel.drl.spec_ref
186: */
187: public PKCS8EncodedKeySpec getKeySpec(Cipher cipher)
188: throws InvalidKeySpecException {
189: if (cipher == null) {
190: throw new NullPointerException(Messages
191: .getString("crypto.28")); //$NON-NLS-1$
192: }
193: try {
194: byte[] decryptedData = cipher.doFinal(encryptedData);
195: try {
196: ASN1PrivateKeyInfo.verify(decryptedData);
197: } catch (IOException e1) {
198: throw new InvalidKeySpecException(Messages
199: .getString("crypto.29")); //$NON-NLS-1$
200: }
201: return new PKCS8EncodedKeySpec(decryptedData);
202: } catch (IllegalStateException e) {
203: throw new InvalidKeySpecException(e.getMessage());
204: } catch (IllegalBlockSizeException e) {
205: throw new InvalidKeySpecException(e.getMessage());
206: } catch (BadPaddingException e) {
207: throw new InvalidKeySpecException(e.getMessage());
208: }
209: }
210:
211: /**
212: * @com.intel.drl.spec_ref
213: */
214: public PKCS8EncodedKeySpec getKeySpec(Key decryptKey)
215: throws NoSuchAlgorithmException, InvalidKeyException {
216: if (decryptKey == null) {
217: throw new NullPointerException(Messages
218: .getString("crypto.2A")); //$NON-NLS-1$
219: }
220: try {
221: Cipher cipher = Cipher.getInstance(algName);
222: if (algParameters == null) {
223: cipher.init(Cipher.DECRYPT_MODE, decryptKey);
224: } else {
225: cipher.init(Cipher.DECRYPT_MODE, decryptKey,
226: algParameters);
227: }
228: byte[] decryptedData = cipher.doFinal(encryptedData);
229: try {
230: ASN1PrivateKeyInfo.verify(decryptedData);
231: } catch (IOException e1) {
232: throw new InvalidKeyException(Messages
233: .getString("crypto.29")); //$NON-NLS-1$
234: }
235: return new PKCS8EncodedKeySpec(decryptedData);
236: } catch (NoSuchPaddingException e) {
237: throw new NoSuchAlgorithmException(e.getMessage());
238: } catch (InvalidAlgorithmParameterException e) {
239: throw new NoSuchAlgorithmException(e.getMessage());
240: } catch (IllegalStateException e) {
241: throw new InvalidKeyException(e.getMessage());
242: } catch (IllegalBlockSizeException e) {
243: throw new InvalidKeyException(e.getMessage());
244: } catch (BadPaddingException e) {
245: throw new InvalidKeyException(e.getMessage());
246: }
247: }
248:
249: /**
250: * @com.intel.drl.spec_ref
251: */
252: public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,
253: String providerName) throws NoSuchProviderException,
254: NoSuchAlgorithmException, InvalidKeyException {
255: if (decryptKey == null) {
256: throw new NullPointerException(Messages
257: .getString("crypto.2A")); //$NON-NLS-1$
258: }
259: if (providerName == null) {
260: throw new NullPointerException(Messages
261: .getString("crypto.2B")); //$NON-NLS-1$
262: }
263: try {
264: Cipher cipher = Cipher.getInstance(algName, providerName);
265: if (algParameters == null) {
266: cipher.init(Cipher.DECRYPT_MODE, decryptKey);
267: } else {
268: cipher.init(Cipher.DECRYPT_MODE, decryptKey,
269: algParameters);
270: }
271: byte[] decryptedData = cipher.doFinal(encryptedData);
272: try {
273: ASN1PrivateKeyInfo.verify(decryptedData);
274: } catch (IOException e1) {
275: throw new InvalidKeyException(Messages
276: .getString("crypto.29")); //$NON-NLS-1$
277: }
278: return new PKCS8EncodedKeySpec(decryptedData);
279: } catch (NoSuchPaddingException e) {
280: throw new NoSuchAlgorithmException(e.getMessage());
281: } catch (InvalidAlgorithmParameterException e) {
282: throw new NoSuchAlgorithmException(e.getMessage());
283: } catch (IllegalStateException e) {
284: throw new InvalidKeyException(e.getMessage());
285: } catch (IllegalBlockSizeException e) {
286: throw new InvalidKeyException(e.getMessage());
287: } catch (BadPaddingException e) {
288: throw new InvalidKeyException(e.getMessage());
289: }
290: }
291:
292: /**
293: * @com.intel.drl.spec_ref
294: */
295: public PKCS8EncodedKeySpec getKeySpec(Key decryptKey,
296: Provider provider) throws NoSuchAlgorithmException,
297: InvalidKeyException {
298: if (decryptKey == null) {
299: throw new NullPointerException(Messages
300: .getString("crypto.2A")); //$NON-NLS-1$
301: }
302: if (provider == null) {
303: throw new NullPointerException(Messages
304: .getString("crypto.2C")); //$NON-NLS-1$
305: }
306: try {
307: Cipher cipher = Cipher.getInstance(algName, provider);
308: if (algParameters == null) {
309: cipher.init(Cipher.DECRYPT_MODE, decryptKey);
310: } else {
311: cipher.init(Cipher.DECRYPT_MODE, decryptKey,
312: algParameters);
313: }
314: byte[] decryptedData = cipher.doFinal(encryptedData);
315: try {
316: ASN1PrivateKeyInfo.verify(decryptedData);
317: } catch (IOException e1) {
318: throw new InvalidKeyException(Messages
319: .getString("crypto.29")); //$NON-NLS-1$
320: }
321: return new PKCS8EncodedKeySpec(decryptedData);
322: } catch (NoSuchPaddingException e) {
323: throw new NoSuchAlgorithmException(e.getMessage());
324: } catch (InvalidAlgorithmParameterException e) {
325: throw new NoSuchAlgorithmException(e.getMessage());
326: } catch (IllegalStateException e) {
327: throw new InvalidKeyException(e.getMessage());
328: } catch (IllegalBlockSizeException e) {
329: throw new InvalidKeyException(e.getMessage());
330: } catch (BadPaddingException e) {
331: throw new InvalidKeyException(e.getMessage());
332: }
333: }
334:
335: /**
336: * @com.intel.drl.spec_ref
337: */
338: public byte[] getEncoded() throws IOException {
339: if (encoded == null) {
340: // Generate ASN.1 encoding:
341: encoded = asn1.encode(this );
342: }
343: byte[] ret = new byte[encoded.length];
344: System.arraycopy(encoded, 0, ret, 0, encoded.length);
345: return ret;
346: }
347:
348: // Performs all needed alg name mappings.
349: // Returns 'true' if mapping available 'false' otherwise
350: private boolean mapAlgName() {
351: if (AlgNameMapper.isOID(this .algName)) {
352: // OID provided to the ctor
353: // get rid of possible leading "OID."
354: this .oid = AlgNameMapper.normalize(this .algName);
355: // try to find mapping OID->algName
356: this .algName = AlgNameMapper.map2AlgName(this .oid);
357: // if there is no mapping OID->algName
358: // set OID as algName
359: if (this .algName == null) {
360: this .algName = this .oid;
361: }
362: } else {
363: String stdName = AlgNameMapper
364: .getStandardName(this .algName);
365: // Alg name provided to the ctor
366: // try to find mapping algName->OID or
367: // (algName->stdAlgName)->OID
368: this .oid = AlgNameMapper.map2OID(this .algName);
369: if (this .oid == null) {
370: if (stdName == null) {
371: // no above mappings available
372: return false;
373: }
374: this .oid = AlgNameMapper.map2OID(stdName);
375: if (this .oid == null) {
376: return false;
377: }
378: this .algName = stdName;
379: } else if (stdName != null) {
380: this .algName = stdName;
381: }
382: }
383: return true;
384: }
385:
386: //
387: // EncryptedPrivateKeyInfo DER encoder/decoder.
388: // EncryptedPrivateKeyInfo ASN.1 definition
389: // (as defined in PKCS #8: Private-Key Information Syntax Standard
390: // http://www.ietf.org/rfc/rfc2313.txt)
391: //
392: // EncryptedPrivateKeyInfo ::= SEQUENCE {
393: // encryptionAlgorithm AlgorithmIdentifier,
394: // encryptedData OCTET STRING }
395: //
396:
397: private static final byte[] nullParam = new byte[] { 5, 0 };
398:
399: private static final ASN1Sequence asn1 = new ASN1Sequence(
400: new ASN1Type[] { AlgorithmIdentifier.ASN1,
401: ASN1OctetString.getInstance() }) {
402:
403: @Override
404: protected void getValues(Object object, Object[] values) {
405:
406: EncryptedPrivateKeyInfo epki = (EncryptedPrivateKeyInfo) object;
407:
408: try {
409: byte[] algParmsEncoded = (epki.algParameters == null) ? nullParam
410: : epki.algParameters.getEncoded();
411: values[0] = new AlgorithmIdentifier(epki.oid,
412: algParmsEncoded);
413: values[1] = epki.encryptedData;
414: } catch (IOException e) {
415: throw new RuntimeException(e.getMessage());
416: }
417: }
418: };
419:
420: // PrivateKeyInfo DER decoder.
421: // PrivateKeyInfo ASN.1 definition
422: // (as defined in PKCS #8: Private-Key Information Syntax Standard
423: // http://www.ietf.org/rfc/rfc2313.txt)
424: //
425: //
426: // PrivateKeyInfo ::= SEQUENCE {
427: // version Version,
428: // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
429: // privateKey PrivateKey,
430: // attributes [0] IMPLICIT Attributes OPTIONAL }
431: //
432: // Version ::= INTEGER
433: //
434: // PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
435: //
436: // PrivateKey ::= OCTET STRING
437: //
438: // Attributes ::= SET OF Attribute
439:
440: private static final ASN1SetOf ASN1Attributes = new ASN1SetOf(
441: ASN1Any.getInstance());
442:
443: private static final ASN1Sequence ASN1PrivateKeyInfo = new ASN1Sequence(
444: new ASN1Type[] { ASN1Integer.getInstance(),
445: AlgorithmIdentifier.ASN1,
446: ASN1OctetString.getInstance(),
447: new ASN1Implicit(0, ASN1Attributes) }) {
448: {
449: setOptional(3); //attributes are optional
450: }
451: };
452: }
|