< prev index next >

src/share/classes/com/sun/crypto/provider/KeyProtector.java

Print this page
rev 12548 : 8181692: Update storage implementations
Reviewed-by: weijun, igerasim
   1 /*
   2  * Copyright (c) 1998, 2013, 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 com.sun.crypto.provider;
  27 
  28 import java.io.IOException;
  29 import java.io.Serializable;
  30 import java.security.Security;
  31 import java.security.Key;
  32 import java.security.PrivateKey;
  33 import java.security.Provider;
  34 import java.security.KeyFactory;
  35 import java.security.MessageDigest;
  36 import java.security.GeneralSecurityException;
  37 import java.security.NoSuchAlgorithmException;
  38 import java.security.NoSuchProviderException;
  39 import java.security.UnrecoverableKeyException;
  40 import java.security.AlgorithmParameters;

  41 import java.security.spec.PKCS8EncodedKeySpec;
  42 
  43 import javax.crypto.Cipher;
  44 import javax.crypto.CipherSpi;
  45 import javax.crypto.SecretKey;
  46 import javax.crypto.IllegalBlockSizeException;
  47 import javax.crypto.SealedObject;
  48 import javax.crypto.spec.*;
  49 import sun.security.x509.AlgorithmId;
  50 import sun.security.util.ObjectIdentifier;
  51 
  52 /**
  53  * This class implements a protection mechanism for private keys. In JCE, we
  54  * use a stronger protection mechanism than in the JDK, because we can use
  55  * the <code>Cipher</code> class.
  56  * Private keys are protected using the JCE mechanism, and are recovered using
  57  * either the JDK or JCE mechanism, depending on how the key has been
  58  * protected. This allows us to parse Sun's keystore implementation that ships
  59  * with JDK 1.2.
  60  *
  61  * @author Jan Luehe
  62  *
  63  *
  64  * @see JceKeyStore
  65  */
  66 
  67 final class KeyProtector {
  68 
  69     // defined by SunSoft (SKI project)
  70     private static final String PBE_WITH_MD5_AND_DES3_CBC_OID
  71             = "1.3.6.1.4.1.42.2.19.1";
  72 
  73     // JavaSoft proprietary key-protection algorithm (used to protect private
  74     // keys in the keystore implementation that comes with JDK 1.2)
  75     private static final String KEY_PROTECTOR_OID = "1.3.6.1.4.1.42.2.17.1.1";
  76 


  77     private static final int SALT_LEN = 20; // the salt length
  78     private static final int DIGEST_LEN = 20;
  79 
  80     // the password used for protecting/recovering keys passed through this
  81     // key protector
  82     private char[] password;
  83 
  84     KeyProtector(char[] password) {
  85         if (password == null) {
  86            throw new IllegalArgumentException("password can't be null");
  87         }
  88         this.password = password;
  89     }
  90 
  91     /**
  92      * Protects the given cleartext private key, using the password provided at
  93      * construction time.
  94      */
  95     byte[] protect(PrivateKey key)
  96         throws Exception
  97     {
  98         // create a random salt (8 bytes)
  99         byte[] salt = new byte[8];
 100         SunJCE.getRandom().nextBytes(salt);
 101 
 102         // create PBE parameters from salt and iteration count
 103         PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, 20);
 104 
 105         // create PBE key from password
 106         PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 107         SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 108         pbeKeySpec.clearPassword();
 109 
 110         // encrypt private key
 111         PBEWithMD5AndTripleDESCipher cipher;
 112         cipher = new PBEWithMD5AndTripleDESCipher();
 113         cipher.engineInit(Cipher.ENCRYPT_MODE, sKey, pbeSpec, null);
 114         byte[] plain = key.getEncoded();
 115         byte[] encrKey = cipher.engineDoFinal(plain, 0, plain.length);
 116 
 117         // wrap encrypted private key in EncryptedPrivateKeyInfo
 118         // (as defined in PKCS#8)
 119         AlgorithmParameters pbeParams =
 120             AlgorithmParameters.getInstance("PBE", SunJCE.getInstance());
 121         pbeParams.init(pbeSpec);
 122 
 123         AlgorithmId encrAlg = new AlgorithmId


 138             String encrAlg = encrInfo.getAlgorithm().getOID().toString();
 139             if (!encrAlg.equals(PBE_WITH_MD5_AND_DES3_CBC_OID)
 140                 && !encrAlg.equals(KEY_PROTECTOR_OID)) {
 141                 throw new UnrecoverableKeyException("Unsupported encryption "
 142                                                     + "algorithm");
 143             }
 144 
 145             if (encrAlg.equals(KEY_PROTECTOR_OID)) {
 146                 // JDK 1.2 style recovery
 147                 plain = recover(encrInfo.getEncryptedData());
 148             } else {
 149                 byte[] encodedParams =
 150                     encrInfo.getAlgorithm().getEncodedParams();
 151 
 152                 // parse the PBE parameters into the corresponding spec
 153                 AlgorithmParameters pbeParams =
 154                     AlgorithmParameters.getInstance("PBE");
 155                 pbeParams.init(encodedParams);
 156                 PBEParameterSpec pbeSpec =
 157                         pbeParams.getParameterSpec(PBEParameterSpec.class);



 158 
 159                 // create PBE key from password
 160                 PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 161                 SecretKey sKey =
 162                     new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 163                 pbeKeySpec.clearPassword();
 164 
 165                 // decrypt private key
 166                 PBEWithMD5AndTripleDESCipher cipher;
 167                 cipher = new PBEWithMD5AndTripleDESCipher();
 168                 cipher.engineInit(Cipher.DECRYPT_MODE, sKey, pbeSpec, null);
 169                 plain=cipher.engineDoFinal(encrInfo.getEncryptedData(), 0,
 170                                            encrInfo.getEncryptedData().length);
 171             }
 172 
 173             // determine the private-key algorithm, and parse private key
 174             // using the appropriate key factory
 175             String oidName = new AlgorithmId
 176                 (new PrivateKeyInfo(plain).getAlgorithm().getOID()).getName();
 177             KeyFactory kFac = KeyFactory.getInstance(oidName);


 268         for (i = 0; i < digest.length; i++) {
 269             if (digest[i] != protectedKey[SALT_LEN + encrKeyLen + i]) {
 270                 throw new UnrecoverableKeyException("Cannot recover key");
 271             }
 272         }
 273         return plainKey;
 274     }
 275 
 276     /**
 277      * Seals the given cleartext key, using the password provided at
 278      * construction time
 279      */
 280     SealedObject seal(Key key)
 281         throws Exception
 282     {
 283         // create a random salt (8 bytes)
 284         byte[] salt = new byte[8];
 285         SunJCE.getRandom().nextBytes(salt);
 286 
 287         // create PBE parameters from salt and iteration count
 288         PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, 20);
 289 
 290         // create PBE key from password
 291         PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 292         SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 293         pbeKeySpec.clearPassword();
 294 
 295         // seal key
 296         Cipher cipher;
 297 
 298         PBEWithMD5AndTripleDESCipher cipherSpi;
 299         cipherSpi = new PBEWithMD5AndTripleDESCipher();
 300         cipher = new CipherForKeyProtector(cipherSpi, SunJCE.getInstance(),
 301                                            "PBEWithMD5AndTripleDES");
 302         cipher.init(Cipher.ENCRYPT_MODE, sKey, pbeSpec);
 303         return new SealedObjectForKeyProtector(key, cipher);
 304     }
 305 
 306     /**
 307      * Unseals the sealed key.
 308      */
 309     Key unseal(SealedObject so)
 310         throws NoSuchAlgorithmException, UnrecoverableKeyException
 311     {
 312         try {
 313             // create PBE key from password
 314             PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 315             SecretKey skey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 316             pbeKeySpec.clearPassword();
 317 
 318             SealedObjectForKeyProtector soForKeyProtector = null;
 319             if (!(so instanceof SealedObjectForKeyProtector)) {
 320                 soForKeyProtector = new SealedObjectForKeyProtector(so);
 321             } else {
 322                 soForKeyProtector = (SealedObjectForKeyProtector)so;
 323             }
 324             AlgorithmParameters params = soForKeyProtector.getParameters();
 325             if (params == null) {
 326                 throw new UnrecoverableKeyException("Cannot get " +
 327                                                     "algorithm parameters");









 328             }
 329             PBEWithMD5AndTripleDESCipher cipherSpi;
 330             cipherSpi = new PBEWithMD5AndTripleDESCipher();
 331             Cipher cipher = new CipherForKeyProtector(cipherSpi,
 332                                                       SunJCE.getInstance(),
 333                                                       "PBEWithMD5AndTripleDES");
 334             cipher.init(Cipher.DECRYPT_MODE, skey, params);
 335             return (Key)soForKeyProtector.getObject(cipher);
 336         } catch (NoSuchAlgorithmException ex) {
 337             // Note: this catch needed to be here because of the
 338             // later catch of GeneralSecurityException
 339             throw ex;
 340         } catch (IOException ioe) {
 341             throw new UnrecoverableKeyException(ioe.getMessage());
 342         } catch (ClassNotFoundException cnfe) {
 343             throw new UnrecoverableKeyException(cnfe.getMessage());
 344         } catch (GeneralSecurityException gse) {
 345             throw new UnrecoverableKeyException(gse.getMessage());
 346         }
 347     }
   1 /*
   2  * Copyright (c) 1998, 2017, 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 com.sun.crypto.provider;
  27 
  28 import java.io.IOException;
  29 import java.io.Serializable;
  30 import java.security.Security;
  31 import java.security.Key;
  32 import java.security.PrivateKey;
  33 import java.security.Provider;
  34 import java.security.KeyFactory;
  35 import java.security.MessageDigest;
  36 import java.security.GeneralSecurityException;
  37 import java.security.NoSuchAlgorithmException;
  38 import java.security.NoSuchProviderException;
  39 import java.security.UnrecoverableKeyException;
  40 import java.security.AlgorithmParameters;
  41 import java.security.spec.InvalidParameterSpecException;
  42 import java.security.spec.PKCS8EncodedKeySpec;
  43 
  44 import javax.crypto.Cipher;
  45 import javax.crypto.CipherSpi;
  46 import javax.crypto.SecretKey;
  47 import javax.crypto.IllegalBlockSizeException;
  48 import javax.crypto.SealedObject;
  49 import javax.crypto.spec.*;
  50 import sun.security.x509.AlgorithmId;
  51 import sun.security.util.ObjectIdentifier;
  52 
  53 /**
  54  * This class implements a protection mechanism for private keys. In JCE, we
  55  * use a stronger protection mechanism than in the JDK, because we can use
  56  * the <code>Cipher</code> class.
  57  * Private keys are protected using the JCE mechanism, and are recovered using
  58  * either the JDK or JCE mechanism, depending on how the key has been
  59  * protected. This allows us to parse Sun's keystore implementation that ships
  60  * with JDK 1.2.
  61  *
  62  * @author Jan Luehe
  63  *
  64  *
  65  * @see JceKeyStore
  66  */
  67 
  68 final class KeyProtector {
  69 
  70     // defined by SunSoft (SKI project)
  71     private static final String PBE_WITH_MD5_AND_DES3_CBC_OID
  72             = "1.3.6.1.4.1.42.2.19.1";
  73 
  74     // JavaSoft proprietary key-protection algorithm (used to protect private
  75     // keys in the keystore implementation that comes with JDK 1.2)
  76     private static final String KEY_PROTECTOR_OID = "1.3.6.1.4.1.42.2.17.1.1";
  77 
  78     private static final int MAX_ITERATION_COUNT = 5000000;
  79     private static final int ITERATION_COUNT = 200000;
  80     private static final int SALT_LEN = 20; // the salt length
  81     private static final int DIGEST_LEN = 20;
  82 
  83     // the password used for protecting/recovering keys passed through this
  84     // key protector
  85     private char[] password;
  86 
  87     KeyProtector(char[] password) {
  88         if (password == null) {
  89            throw new IllegalArgumentException("password can't be null");
  90         }
  91         this.password = password;
  92     }
  93 
  94     /**
  95      * Protects the given cleartext private key, using the password provided at
  96      * construction time.
  97      */
  98     byte[] protect(PrivateKey key)
  99         throws Exception
 100     {
 101         // create a random salt (8 bytes)
 102         byte[] salt = new byte[8];
 103         SunJCE.getRandom().nextBytes(salt);
 104 
 105         // create PBE parameters from salt and iteration count
 106         PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, ITERATION_COUNT);
 107 
 108         // create PBE key from password
 109         PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 110         SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 111         pbeKeySpec.clearPassword();
 112 
 113         // encrypt private key
 114         PBEWithMD5AndTripleDESCipher cipher;
 115         cipher = new PBEWithMD5AndTripleDESCipher();
 116         cipher.engineInit(Cipher.ENCRYPT_MODE, sKey, pbeSpec, null);
 117         byte[] plain = key.getEncoded();
 118         byte[] encrKey = cipher.engineDoFinal(plain, 0, plain.length);
 119 
 120         // wrap encrypted private key in EncryptedPrivateKeyInfo
 121         // (as defined in PKCS#8)
 122         AlgorithmParameters pbeParams =
 123             AlgorithmParameters.getInstance("PBE", SunJCE.getInstance());
 124         pbeParams.init(pbeSpec);
 125 
 126         AlgorithmId encrAlg = new AlgorithmId


 141             String encrAlg = encrInfo.getAlgorithm().getOID().toString();
 142             if (!encrAlg.equals(PBE_WITH_MD5_AND_DES3_CBC_OID)
 143                 && !encrAlg.equals(KEY_PROTECTOR_OID)) {
 144                 throw new UnrecoverableKeyException("Unsupported encryption "
 145                                                     + "algorithm");
 146             }
 147 
 148             if (encrAlg.equals(KEY_PROTECTOR_OID)) {
 149                 // JDK 1.2 style recovery
 150                 plain = recover(encrInfo.getEncryptedData());
 151             } else {
 152                 byte[] encodedParams =
 153                     encrInfo.getAlgorithm().getEncodedParams();
 154 
 155                 // parse the PBE parameters into the corresponding spec
 156                 AlgorithmParameters pbeParams =
 157                     AlgorithmParameters.getInstance("PBE");
 158                 pbeParams.init(encodedParams);
 159                 PBEParameterSpec pbeSpec =
 160                         pbeParams.getParameterSpec(PBEParameterSpec.class);
 161                 if (pbeSpec.getIterationCount() > MAX_ITERATION_COUNT) {
 162                     throw new IOException("PBE iteration count too large");
 163                 }
 164 
 165                 // create PBE key from password
 166                 PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 167                 SecretKey sKey =
 168                     new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 169                 pbeKeySpec.clearPassword();
 170 
 171                 // decrypt private key
 172                 PBEWithMD5AndTripleDESCipher cipher;
 173                 cipher = new PBEWithMD5AndTripleDESCipher();
 174                 cipher.engineInit(Cipher.DECRYPT_MODE, sKey, pbeSpec, null);
 175                 plain=cipher.engineDoFinal(encrInfo.getEncryptedData(), 0,
 176                                            encrInfo.getEncryptedData().length);
 177             }
 178 
 179             // determine the private-key algorithm, and parse private key
 180             // using the appropriate key factory
 181             String oidName = new AlgorithmId
 182                 (new PrivateKeyInfo(plain).getAlgorithm().getOID()).getName();
 183             KeyFactory kFac = KeyFactory.getInstance(oidName);


 274         for (i = 0; i < digest.length; i++) {
 275             if (digest[i] != protectedKey[SALT_LEN + encrKeyLen + i]) {
 276                 throw new UnrecoverableKeyException("Cannot recover key");
 277             }
 278         }
 279         return plainKey;
 280     }
 281 
 282     /**
 283      * Seals the given cleartext key, using the password provided at
 284      * construction time
 285      */
 286     SealedObject seal(Key key)
 287         throws Exception
 288     {
 289         // create a random salt (8 bytes)
 290         byte[] salt = new byte[8];
 291         SunJCE.getRandom().nextBytes(salt);
 292 
 293         // create PBE parameters from salt and iteration count
 294         PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, ITERATION_COUNT);
 295 
 296         // create PBE key from password
 297         PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 298         SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 299         pbeKeySpec.clearPassword();
 300 
 301         // seal key
 302         Cipher cipher;
 303 
 304         PBEWithMD5AndTripleDESCipher cipherSpi;
 305         cipherSpi = new PBEWithMD5AndTripleDESCipher();
 306         cipher = new CipherForKeyProtector(cipherSpi, SunJCE.getInstance(),
 307                                            "PBEWithMD5AndTripleDES");
 308         cipher.init(Cipher.ENCRYPT_MODE, sKey, pbeSpec);
 309         return new SealedObjectForKeyProtector(key, cipher);
 310     }
 311 
 312     /**
 313      * Unseals the sealed key.
 314      */
 315     Key unseal(SealedObject so)
 316         throws NoSuchAlgorithmException, UnrecoverableKeyException
 317     {
 318         try {
 319             // create PBE key from password
 320             PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
 321             SecretKey skey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
 322             pbeKeySpec.clearPassword();
 323 
 324             SealedObjectForKeyProtector soForKeyProtector = null;
 325             if (!(so instanceof SealedObjectForKeyProtector)) {
 326                 soForKeyProtector = new SealedObjectForKeyProtector(so);
 327             } else {
 328                 soForKeyProtector = (SealedObjectForKeyProtector)so;
 329             }
 330             AlgorithmParameters params = soForKeyProtector.getParameters();
 331             if (params == null) {
 332                 throw new UnrecoverableKeyException("Cannot get " +
 333                                                     "algorithm parameters");
 334             }
 335             PBEParameterSpec pbeSpec;
 336             try {
 337                 pbeSpec = params.getParameterSpec(PBEParameterSpec.class);
 338             } catch (InvalidParameterSpecException ipse) {
 339                 throw new IOException("Invalid PBE algorithm parameters");
 340             }
 341             if (pbeSpec.getIterationCount() > MAX_ITERATION_COUNT) {
 342                 throw new IOException("PBE iteration count too large");
 343             }
 344             PBEWithMD5AndTripleDESCipher cipherSpi;
 345             cipherSpi = new PBEWithMD5AndTripleDESCipher();
 346             Cipher cipher = new CipherForKeyProtector(cipherSpi,
 347                                                       SunJCE.getInstance(),
 348                                                       "PBEWithMD5AndTripleDES");
 349             cipher.init(Cipher.DECRYPT_MODE, skey, params);
 350             return (Key)soForKeyProtector.getObject(cipher);
 351         } catch (NoSuchAlgorithmException ex) {
 352             // Note: this catch needed to be here because of the
 353             // later catch of GeneralSecurityException
 354             throw ex;
 355         } catch (IOException ioe) {
 356             throw new UnrecoverableKeyException(ioe.getMessage());
 357         } catch (ClassNotFoundException cnfe) {
 358             throw new UnrecoverableKeyException(cnfe.getMessage());
 359         } catch (GeneralSecurityException gse) {
 360             throw new UnrecoverableKeyException(gse.getMessage());
 361         }
 362     }
< prev index next >