History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/crypto/provider/KeyProtector.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/crypto/provider/KeyProtector.java+410−0
@@ -0,0 +1,410 @@
1/*
2 * Copyright (c) 1998, 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
26package com.sun.crypto.provider;
27
28import java.io.UnsupportedEncodingException;
29import java.io.IOException;
30import java.io.Serializable;
31import java.io.ByteArrayInputStream;
32import java.io.ByteArrayOutputStream;
33import java.io.ObjectInputStream;
34import java.io.ObjectOutputStream;
35import java.io.ObjectInputStream.GetField;
36import java.security.Security;
37import java.security.Key;
38import java.security.PrivateKey;
39import java.security.Provider;
40import java.security.KeyFactory;
41import java.security.MessageDigest;
42import java.security.GeneralSecurityException;
43import java.security.NoSuchAlgorithmException;
44import java.security.NoSuchProviderException;
45import java.security.SecureRandom;
46import java.security.UnrecoverableKeyException;
47import java.security.InvalidParameterException;
48import java.security.InvalidAlgorithmParameterException;
49import java.security.InvalidKeyException;
50import java.security.AlgorithmParameters;
51import java.security.spec.InvalidParameterSpecException;
52import java.security.spec.InvalidKeySpecException;
53import java.security.spec.PKCS8EncodedKeySpec;
54
55import javax.crypto.Cipher;
56import javax.crypto.CipherSpi;
57import javax.crypto.SecretKey;
58import javax.crypto.NoSuchPaddingException;
59import javax.crypto.IllegalBlockSizeException;
60import javax.crypto.BadPaddingException;
61import javax.crypto.SealedObject;
62import javax.crypto.spec.*;
63import sun.security.x509.AlgorithmId;
64import sun.security.util.ObjectIdentifier;
65
66/**
67 * This class implements a protection mechanism for private keys. In JCE, we
68 * use a stronger protection mechanism than in the JDK, because we can use
69 * the <code>Cipher</code> class.
70 * Private keys are protected using the JCE mechanism, and are recovered using
71 * either the JDK or JCE mechanism, depending on how the key has been
72 * protected. This allows us to parse Sun's keystore implementation that ships
73 * with JDK 1.2.
74 *
75 * @author Jan Luehe
76 *
77 *
78 * @see JceKeyStore
79 */
80
81final class KeyProtector {
82
83 // defined by SunSoft (SKI project)
84 private static final String PBE_WITH_MD5_AND_DES3_CBC_OID
85 = "1.3.6.1.4.1.42.2.19.1";
86
87 // JavaSoft proprietary key-protection algorithm (used to protect private
88 // keys in the keystore implementation that comes with JDK 1.2)
89 private static final String KEY_PROTECTOR_OID = "1.3.6.1.4.1.42.2.17.1.1";
90
91 private static final int SALT_LEN = 20; // the salt length
92 private static final int DIGEST_LEN = 20;
93
94 // the password used for protecting/recovering keys passed through this
95 // key protector
96 private char[] password;
97
98 private static final Provider PROV = Security.getProvider("SunJCE");
99
100 KeyProtector(char[] password) {
101 if (password == null) {
102 throw new IllegalArgumentException("password can't be null");
103 }
104 this.password = password;
105 }
106
107 /**
108 * Protects the given cleartext private key, using the password provided at
109 * construction time.
110 */
111 byte[] protect(PrivateKey key)
112 throws Exception
113 {
114 // create a random salt (8 bytes)
115 byte[] salt = new byte[8];
116 SunJCE.RANDOM.nextBytes(salt);
117
118 // create PBE parameters from salt and iteration count
119 PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, 20);
120
121 // create PBE key from password
122 PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
123 SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
124 pbeKeySpec.clearPassword();
125
126 // encrypt private key
127 PBEWithMD5AndTripleDESCipher cipher;
128 cipher = new PBEWithMD5AndTripleDESCipher();
129 cipher.engineInit(Cipher.ENCRYPT_MODE, sKey, pbeSpec, null);
130 byte[] plain = (byte[])key.getEncoded();
131 byte[] encrKey = cipher.engineDoFinal(plain, 0, plain.length);
132
133 // wrap encrypted private key in EncryptedPrivateKeyInfo
134 // (as defined in PKCS#8)
135 AlgorithmParameters pbeParams =
136 AlgorithmParameters.getInstance("PBE", PROV);
137 pbeParams.init(pbeSpec);
138
139 AlgorithmId encrAlg = new AlgorithmId
140 (new ObjectIdentifier(PBE_WITH_MD5_AND_DES3_CBC_OID), pbeParams);
141 return new EncryptedPrivateKeyInfo(encrAlg,encrKey).getEncoded();
142 }
143
144 /*
145 * Recovers the cleartext version of the given key (in protected format),
146 * using the password provided at construction time.
147 */
148 Key recover(EncryptedPrivateKeyInfo encrInfo)
149 throws UnrecoverableKeyException, NoSuchAlgorithmException
150 {
151 byte[] plain;
152
153 try {
154 String encrAlg = encrInfo.getAlgorithm().getOID().toString();
155 if (!encrAlg.equals(PBE_WITH_MD5_AND_DES3_CBC_OID)
156 && !encrAlg.equals(KEY_PROTECTOR_OID)) {
157 throw new UnrecoverableKeyException("Unsupported encryption "
158 + "algorithm");
159 }
160
161 if (encrAlg.equals(KEY_PROTECTOR_OID)) {
162 // JDK 1.2 style recovery
163 plain = recover(encrInfo.getEncryptedData());
164 } else {
165 byte[] encodedParams =
166 encrInfo.getAlgorithm().getEncodedParams();
167
168 // parse the PBE parameters into the corresponding spec
169 AlgorithmParameters pbeParams =
170 AlgorithmParameters.getInstance("PBE");
171 pbeParams.init(encodedParams);
172 PBEParameterSpec pbeSpec = (PBEParameterSpec)
173 pbeParams.getParameterSpec(PBEParameterSpec.class);
174
175 // create PBE key from password
176 PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
177 SecretKey sKey =
178 new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
179 pbeKeySpec.clearPassword();
180
181 // decrypt private key
182 PBEWithMD5AndTripleDESCipher cipher;
183 cipher = new PBEWithMD5AndTripleDESCipher();
184 cipher.engineInit(Cipher.DECRYPT_MODE, sKey, pbeSpec, null);
185 plain=cipher.engineDoFinal(encrInfo.getEncryptedData(), 0,
186 encrInfo.getEncryptedData().length);
187 }
188
189 // determine the private-key algorithm, and parse private key
190 // using the appropriate key factory
191 String oidName = new AlgorithmId
192 (new PrivateKeyInfo(plain).getAlgorithm().getOID()).getName();
193 KeyFactory kFac = KeyFactory.getInstance(oidName);
194 return kFac.generatePrivate(new PKCS8EncodedKeySpec(plain));
195
196 } catch (NoSuchAlgorithmException ex) {
197 // Note: this catch needed to be here because of the
198 // later catch of GeneralSecurityException
199 throw ex;
200 } catch (IOException ioe) {
201 throw new UnrecoverableKeyException(ioe.getMessage());
202 } catch (GeneralSecurityException gse) {
203 throw new UnrecoverableKeyException(gse.getMessage());
204 }
205 }
206
207 /*
208 * Recovers the cleartext version of the given key (in protected format),
209 * using the password provided at construction time. This method implements
210 * the recovery algorithm used by Sun's keystore implementation in
211 * JDK 1.2.
212 */
213 private byte[] recover(byte[] protectedKey)
214 throws UnrecoverableKeyException, NoSuchAlgorithmException
215 {
216 int i, j;
217 byte[] digest;
218 int numRounds;
219 int xorOffset; // offset in xorKey where next digest will be stored
220 int encrKeyLen; // the length of the encrpyted key
221
222 MessageDigest md = MessageDigest.getInstance("SHA");
223
224 // Get the salt associated with this key (the first SALT_LEN bytes of
225 // <code>protectedKey</code>)
226 byte[] salt = new byte[SALT_LEN];
227 System.arraycopy(protectedKey, 0, salt, 0, SALT_LEN);
228
229 // Determine the number of digest rounds
230 encrKeyLen = protectedKey.length - SALT_LEN - DIGEST_LEN;
231 numRounds = encrKeyLen / DIGEST_LEN;
232 if ((encrKeyLen % DIGEST_LEN) != 0)
233 numRounds++;
234
235 // Get the encrypted key portion and store it in "encrKey"
236 byte[] encrKey = new byte[encrKeyLen];
237 System.arraycopy(protectedKey, SALT_LEN, encrKey, 0, encrKeyLen);
238
239 // Set up the byte array which will be XORed with "encrKey"
240 byte[] xorKey = new byte[encrKey.length];
241
242 // Convert password to byte array, so that it can be digested
243 byte[] passwdBytes = new byte[password.length * 2];
244 for (i=0, j=0; i<password.length; i++) {
245 passwdBytes[j++] = (byte)(password[i] >> 8);
246 passwdBytes[j++] = (byte)password[i];
247 }
248
249 // Compute the digests, and store them in "xorKey"
250 for (i = 0, xorOffset = 0, digest = salt;
251 i < numRounds;
252 i++, xorOffset += DIGEST_LEN) {
253 md.update(passwdBytes);
254 md.update(digest);
255 digest = md.digest();
256 md.reset();
257 // Copy the digest into "xorKey"
258 if (i < numRounds - 1) {
259 System.arraycopy(digest, 0, xorKey, xorOffset,
260 digest.length);
261 } else {
262 System.arraycopy(digest, 0, xorKey, xorOffset,
263 xorKey.length - xorOffset);
264 }
265 }
266
267 // XOR "encrKey" with "xorKey", and store the result in "plainKey"
268 byte[] plainKey = new byte[encrKey.length];
269 for (i = 0; i < plainKey.length; i++) {
270 plainKey[i] = (byte)(encrKey[i] ^ xorKey[i]);
271 }
272
273 // Check the integrity of the recovered key by concatenating it with
274 // the password, digesting the concatenation, and comparing the
275 // result of the digest operation with the digest provided at the end
276 // of <code>protectedKey</code>. If the two digest values are
277 // different, throw an exception.
278 md.update(passwdBytes);
279 java.util.Arrays.fill(passwdBytes, (byte)0x00);
280 passwdBytes = null;
281 md.update(plainKey);
282 digest = md.digest();
283 md.reset();
284 for (i = 0; i < digest.length; i++) {
285 if (digest[i] != protectedKey[SALT_LEN + encrKeyLen + i]) {
286 throw new UnrecoverableKeyException("Cannot recover key");
287 }
288 }
289 return plainKey;
290 }
291
292 /**
293 * Seals the given cleartext key, using the password provided at
294 * construction time
295 */
296 SealedObject seal(Key key)
297 throws Exception
298 {
299 // create a random salt (8 bytes)
300 byte[] salt = new byte[8];
301 SunJCE.RANDOM.nextBytes(salt);
302
303 // create PBE parameters from salt and iteration count
304 PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, 20);
305
306 // create PBE key from password
307 PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
308 SecretKey sKey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
309 pbeKeySpec.clearPassword();
310
311 // seal key
312 Cipher cipher;
313
314 PBEWithMD5AndTripleDESCipher cipherSpi;
315 cipherSpi = new PBEWithMD5AndTripleDESCipher();
316 cipher = new CipherForKeyProtector(cipherSpi, PROV,
317 "PBEWithMD5AndTripleDES");
318 cipher.init(Cipher.ENCRYPT_MODE, sKey, pbeSpec);
319 return new SealedObjectForKeyProtector(key, cipher);
320 }
321
322 /**
323 * Unseals the sealed key.
324 */
325 Key unseal(SealedObject so)
326 throws NoSuchAlgorithmException, UnrecoverableKeyException
327 {
328 try {
329 // create PBE key from password
330 PBEKeySpec pbeKeySpec = new PBEKeySpec(this.password);
331 SecretKey skey = new PBEKey(pbeKeySpec, "PBEWithMD5AndTripleDES");
332 pbeKeySpec.clearPassword();
333
334 SealedObjectForKeyProtector soForKeyProtector = null;
335 if (!(so instanceof SealedObjectForKeyProtector)) {
336 soForKeyProtector = new SealedObjectForKeyProtector(so);
337 } else {
338 soForKeyProtector = (SealedObjectForKeyProtector)so;
339 }
340 AlgorithmParameters params = soForKeyProtector.getParameters();
341 if (params == null) {
342 throw new UnrecoverableKeyException("Cannot get " +
343 "algorithm parameters");
344 }
345 PBEWithMD5AndTripleDESCipher cipherSpi;
346 cipherSpi = new PBEWithMD5AndTripleDESCipher();
347 Cipher cipher = new CipherForKeyProtector(cipherSpi, PROV,
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 }
363}
364
365
366final class CipherForKeyProtector extends javax.crypto.Cipher {
367 /**
368 * Creates a Cipher object.
369 *
370 * @param cipherSpi the delegate
371 * @param provider the provider
372 * @param transformation the transformation
373 */
374 protected CipherForKeyProtector(CipherSpi cipherSpi,
375 Provider provider,
376 String transformation) {
377 super(cipherSpi, provider, transformation);
378 }
379}
380
381final class SealedObjectForKeyProtector extends javax.crypto.SealedObject {
382
383 static final long serialVersionUID = -3650226485480866989L;
384
385 SealedObjectForKeyProtector(Serializable object, Cipher c)
386 throws IOException, IllegalBlockSizeException {
387 super(object, c);
388 }
389
390 SealedObjectForKeyProtector(SealedObject so) {
391 super(so);
392 }
393
394 AlgorithmParameters getParameters() {
395 AlgorithmParameters params = null;
396 if (super.encodedParams != null) {
397 try {
398 params = AlgorithmParameters.getInstance("PBE", "SunJCE");
399 params.init(super.encodedParams);
400 } catch (NoSuchProviderException nspe) {
401 // eat.
402 } catch (NoSuchAlgorithmException nsae) {
403 //eat.
404 } catch (IOException ioe) {
405 //eat.
406 }
407 }
408 return params;
409 }
410}