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+
26+package com.sun.crypto.provider;
27+
28+import java.io.UnsupportedEncodingException;
29+import java.io.IOException;
30+import java.io.Serializable;
31+import java.io.ByteArrayInputStream;
32+import java.io.ByteArrayOutputStream;
33+import java.io.ObjectInputStream;
34+import java.io.ObjectOutputStream;
35+import java.io.ObjectInputStream.GetField;
36+import java.security.Security;
37+import java.security.Key;
38+import java.security.PrivateKey;
39+import java.security.Provider;
40+import java.security.KeyFactory;
41+import java.security.MessageDigest;
42+import java.security.GeneralSecurityException;
43+import java.security.NoSuchAlgorithmException;
44+import java.security.NoSuchProviderException;
45+import java.security.SecureRandom;
46+import java.security.UnrecoverableKeyException;
47+import java.security.InvalidParameterException;
48+import java.security.InvalidAlgorithmParameterException;
49+import java.security.InvalidKeyException;
50+import java.security.AlgorithmParameters;
51+import java.security.spec.InvalidParameterSpecException;
52+import java.security.spec.InvalidKeySpecException;
53+import java.security.spec.PKCS8EncodedKeySpec;
54+
55+import javax.crypto.Cipher;
56+import javax.crypto.CipherSpi;
57+import javax.crypto.SecretKey;
58+import javax.crypto.NoSuchPaddingException;
59+import javax.crypto.IllegalBlockSizeException;
60+import javax.crypto.BadPaddingException;
61+import javax.crypto.SealedObject;
62+import javax.crypto.spec.*;
63+import sun.security.x509.AlgorithmId;
64+import 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+
81+final 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+
366+final 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+
381+final 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+}