Home » openjdk-7 » javax » crypto » [javadoc | source]

    1   /*
    2    * Copyright (c) 2001, 2007, 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 "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   }

Home » openjdk-7 » javax » crypto » [javadoc | source]