001/* EncryptedPrivateKeyInfo.java -- As in PKCS #8.
002   Copyright (C) 2004  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.crypto;
040
041import gnu.java.security.OID;
042import gnu.java.security.der.DER;
043import gnu.java.security.der.DERReader;
044import gnu.java.security.der.DERValue;
045
046import java.io.IOException;
047import java.security.AlgorithmParameters;
048import java.security.NoSuchAlgorithmException;
049import java.security.spec.InvalidKeySpecException;
050import java.security.spec.PKCS8EncodedKeySpec;
051import java.util.ArrayList;
052import java.util.List;
053
054/**
055 * An implementation of the <code>EncryptedPrivateKeyInfo</code> ASN.1
056 * type as specified in <a
057 * href="http://www.rsasecurity.com/rsalabs/pkcs/pkcs-8/">PKCS #8 -
058 * Private-Key Information Syntax Standard</a>.
059 *
060 * <p>The ASN.1 type <code>EncryptedPrivateKeyInfo</code> is:
061 *
062 * <blockquote>
063 * <pre>EncryptedPrivateKeyInfo ::= SEQUENCE {
064 *   encryptionAlgorithm EncryptionAlgorithmIdentifier,
065 *   encryptedData EncryptedData }
066 *
067 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
068 *
069 * EncrytpedData ::= OCTET STRING
070 *
071 * AlgorithmIdentifier ::= SEQUENCE {
072 *   algorithm  OBJECT IDENTIFIER,
073 *   parameters ANY DEFINED BY algorithm OPTIONAL }</pre>
074 * </blockquote>
075 *
076 * @author Casey Marshall (csm@gnu.org)
077 * @since 1.4
078 * @see java.security.spec.PKCS8EncodedKeySpec
079 */
080public class EncryptedPrivateKeyInfo
081{
082
083  // Fields.
084  // ------------------------------------------------------------------------
085
086  /** The encrypted data. */
087  private byte[] encryptedData;
088
089  /** The encoded, encrypted key. */
090  private byte[] encoded;
091
092  /** The OID of the encryption algorithm. */
093  private OID algOid;
094
095  /** The encryption algorithm name. */
096  private String algName;
097
098  /** The encryption algorithm's parameters. */
099  private AlgorithmParameters params;
100
101  /** The encoded ASN.1 algorithm parameters. */
102  private byte[] encodedParams;
103
104  // Constructors.
105  // ------------------------------------------------------------------------
106
107  /**
108   * Create a new <code>EncryptedPrivateKeyInfo</code> object from raw
109   * encrypted data and the parameters used for encryption.
110   *
111   * <p>The <code>encryptedData</code> array is cloned.
112   *
113   * @param params        The encryption algorithm parameters.
114   * @param encryptedData The encrypted key data.
115   * @throws java.lang.IllegalArgumentException If the
116   *         <code>encryptedData</code> array is empty (zero-length).
117   * @throws java.security.NoSuchAlgorithmException If the algorithm
118   *         specified in the parameters is not supported.
119   * @throws java.lang.NullPointerException If <code>encryptedData</code>
120   *         is null.
121   */
122  public EncryptedPrivateKeyInfo(AlgorithmParameters params,
123                                 byte[] encryptedData)
124    throws IllegalArgumentException, NoSuchAlgorithmException
125  {
126    if (encryptedData.length == 0)
127      {
128        throw new IllegalArgumentException("0-length encryptedData");
129      }
130    this.params = params;
131    algName = params.getAlgorithm ();
132    algOid = getOid (algName);
133    this.encryptedData = (byte[]) encryptedData.clone();
134  }
135
136  /**
137   * Create a new <code>EncryptedPrivateKeyInfo</code> from an encoded
138   * representation, parsing the ASN.1 sequence.
139   *
140   * @param encoded The encoded info.
141   * @throws java.io.IOException If parsing the encoded data fails.
142   * @throws java.lang.NullPointerException If <code>encoded</code> is
143   *         null.
144   */
145  public EncryptedPrivateKeyInfo(byte[] encoded)
146    throws IOException
147  {
148    this.encoded = (byte[]) encoded.clone();
149    decode();
150  }
151
152  /**
153   * Create a new <code>EncryptedPrivateKeyInfo</code> from the cipher
154   * name and the encrytpedData.
155   *
156   * <p>The <code>encryptedData</code> array is cloned.
157   *
158   * @param algName       The name of the algorithm (as an object identifier).
159   * @param encryptedData The encrypted key data.
160   * @throws java.lang.IllegalArgumentException If the
161   *         <code>encryptedData</code> array is empty (zero-length).
162   * @throws java.security.NoSuchAlgorithmException If algName is not
163   *         the name of a supported algorithm.
164   * @throws java.lang.NullPointerException If <code>encryptedData</code>
165   *         is null.
166   */
167  public EncryptedPrivateKeyInfo(String algName, byte[] encryptedData)
168    throws IllegalArgumentException, NoSuchAlgorithmException,
169           NullPointerException
170  {
171    if (encryptedData.length == 0)
172      {
173        throw new IllegalArgumentException("0-length encryptedData");
174      }
175    this.algName = algName.toString (); // do NP check
176    this.algOid = getOid (algName);
177    this.encryptedData = (byte[]) encryptedData.clone();
178  }
179
180  /**
181   * Return the OID for the given cipher name.
182   *
183   * @param str The string.
184   * @throws NoSuchAlgorithmException If the OID is not known.
185   */
186  private static OID getOid (final String str)
187    throws NoSuchAlgorithmException
188  {
189    if (str.equalsIgnoreCase ("DSA"))
190      {
191        return new OID ("1.2.840.10040.4.3");
192      }
193    // FIXME add more
194
195    try
196      {
197        return new OID (str);
198      }
199    catch (Throwable t)
200      {
201      }
202    throw new NoSuchAlgorithmException ("cannot determine OID for '" + str + "'");
203  }
204
205  // Instance methods.
206  // ------------------------------------------------------------------------
207
208  /**
209   * Return the name of the cipher used to encrypt this key.
210   *
211   * @return The algorithm name.
212   */
213  public String getAlgName()
214  {
215    return algOid.toString();
216  }
217
218  public AlgorithmParameters getAlgParameters()
219  {
220    if (params == null && encodedParams != null)
221      {
222        try
223          {
224            params = AlgorithmParameters.getInstance(getAlgName());
225            params.init(encodedParams);
226          }
227        catch (NoSuchAlgorithmException ignore)
228          {
229            // FIXME throw exception?
230          }
231        catch (IOException ignore)
232          {
233          }
234      }
235    return params;
236  }
237
238  public synchronized byte[] getEncoded() throws IOException
239  {
240    if (encoded == null) encode();
241    return (byte[]) encoded.clone();
242  }
243
244  public byte[] getEncryptedData()
245  {
246    return encryptedData;
247  }
248
249  public PKCS8EncodedKeySpec getKeySpec(Cipher cipher)
250    throws InvalidKeySpecException
251  {
252    try
253      {
254        return new PKCS8EncodedKeySpec(cipher.doFinal(encryptedData));
255      }
256    catch (Exception x)
257      {
258        throw new InvalidKeySpecException(x.toString());
259      }
260  }
261
262  // Own methods.
263  // -------------------------------------------------------------------------
264
265  private void decode() throws IOException
266  {
267    DERReader der = new DERReader(encoded);
268    DERValue val = der.read();
269    if (val.getTag() != DER.SEQUENCE)
270      throw new IOException("malformed EncryptedPrivateKeyInfo");
271    val = der.read();
272    if (val.getTag() != DER.SEQUENCE)
273      throw new IOException("malformed AlgorithmIdentifier");
274    int algpLen = val.getLength();
275    DERValue oid = der.read();
276    if (oid.getTag() != DER.OBJECT_IDENTIFIER)
277      throw new IOException("malformed AlgorithmIdentifier");
278    algOid = (OID) oid.getValue();
279    if (algpLen == 0)
280      {
281        val = der.read();
282        if (val.getTag() != 0)
283          {
284            encodedParams = val.getEncoded();
285            der.read();
286          }
287      }
288    else if (oid.getEncodedLength() < val.getLength())
289      {
290        val = der.read();
291        encodedParams = val.getEncoded();
292      }
293    val = der.read();
294    if (val.getTag() != DER.OCTET_STRING)
295      throw new IOException("malformed AlgorithmIdentifier");
296    encryptedData = (byte[]) val.getValue();
297  }
298
299  private void encode() throws IOException
300  {
301    List algId = new ArrayList(2);
302    algId.add(new DERValue(DER.OBJECT_IDENTIFIER, algOid));
303    getAlgParameters();
304    if (params != null)
305      {
306        algId.add (DERReader.read (params.getEncoded()));
307      }
308    else
309      {
310        algId.add (new DERValue (DER.NULL, null));
311      }
312    List epki = new ArrayList(2);
313    epki.add(new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, algId));
314    epki.add(new DERValue(DER.OCTET_STRING, encryptedData));
315    encoded = new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, epki).getEncoded();
316  }
317}