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 }
|