blob: f7a8591646a9c15e918002b3430cac338ee4a8a8 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Portions Copyright 2000-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26/*
27 *
28 * (C) Copyright IBM Corp. 1999 All Rights Reserved.
29 * Copyright 1997 The Open Group Research Institute. All rights reserved.
30 */
31
32package sun.security.krb5;
33
34import sun.security.util.*;
35import sun.security.krb5.internal.*;
36import sun.security.krb5.internal.crypto.*;
37import java.io.IOException;
38import java.security.GeneralSecurityException;
39import java.util.Arrays;
40import sun.security.krb5.internal.ktab.KeyTab;
41import sun.security.krb5.internal.ccache.CCacheOutputStream;
42import javax.crypto.spec.DESKeySpec;
43import javax.crypto.spec.DESedeKeySpec;
44
45/**
46 * This class encapsulates the concept of an EncryptionKey. An encryption
47 * key is defined in RFC 4120 as:
48 *
49 * EncryptionKey ::= SEQUENCE {
50 * keytype [0] Int32 -- actually encryption type --,
51 * keyvalue [1] OCTET STRING
52 * }
53 *
54 * keytype
55 * This field specifies the encryption type of the encryption key
56 * that follows in the keyvalue field. Although its name is
57 * "keytype", it actually specifies an encryption type. Previously,
58 * multiple cryptosystems that performed encryption differently but
59 * were capable of using keys with the same characteristics were
60 * permitted to share an assigned number to designate the type of
61 * key; this usage is now deprecated.
62 *
63 * keyvalue
64 * This field contains the key itself, encoded as an octet string.
65 */
66
67public class EncryptionKey
68 implements Cloneable {
69
70 public static final EncryptionKey NULL_KEY =
71 new EncryptionKey(new byte[] {}, EncryptedData.ETYPE_NULL, null);
72
73 private int keyType;
74 private byte[] keyValue;
75 private Integer kvno; // not part of ASN1 encoding;
76
77 private static final boolean DEBUG = Krb5.DEBUG;
78
79 public synchronized int getEType() {
80 return keyType;
81 }
82
83 public final Integer getKeyVersionNumber() {
84 return kvno;
85 }
86
87 /**
88 * Returns the raw key bytes, not in any ASN.1 encoding.
89 */
90 public final byte[] getBytes() {
91 // This method cannot be called outside sun.security, hence no
92 // cloning. getEncoded() calls this method.
93 return keyValue;
94 }
95
96 public synchronized Object clone() {
97 return new EncryptionKey(keyValue, keyType, kvno);
98 }
99
100 /**
101 * Obtains the latest version of the secret key of
102 * the principal from a keytab.
103 *
104 * @param princ the principal whose secret key is desired
105 * @param keytab the path to the keytab file. A value of null
106 * will be accepted to indicate that the default path should be
107 * searched.
108 * @returns the secret key or null if none was found.
109 */
110 /*
111 // Replaced by acquireSecretKeys
112 public static EncryptionKey acquireSecretKey(PrincipalName princ,
113 String keytab)
114 throws KrbException, IOException {
115
116 if (princ == null) {
117 throw new IllegalArgumentException(
118 "Cannot have null pricipal name to look in keytab.");
119 }
120
121 KeyTab ktab = KeyTab.getInstance(keytab);
122
123 if (ktab == null)
124 return null;
125
126 return ktab.readServiceKey(princ);
127 }
128 */
129
130 /**
131 * Obtains all versions of the secret key of the principal from a
132 * keytab.
133 *
134 * @Param princ the principal whose secret key is desired
135 * @param keytab the path to the keytab file. A value of null
136 * will be accepted to indicate that the default path should be
137 * searched.
138 * @returns an array of secret keys or null if none were found.
139 */
140 public static EncryptionKey[] acquireSecretKeys(PrincipalName princ,
141 String keytab)
142 throws KrbException, IOException {
143
144 if (princ == null)
145 throw new IllegalArgumentException(
146 "Cannot have null pricipal name to look in keytab.");
147
148 // KeyTab getInstance(keytab) will call KeyTab.getInstance()
149 // if keytab is null
150 KeyTab ktab = KeyTab.getInstance(keytab);
151
152 if (ktab == null) {
153 return null;
154 }
155
156 return ktab.readServiceKeys(princ);
157 }
158
159 /**
160 * Generate a list of keys using the given principal and password.
161 * Construct a key for each configured etype.
162 * Caller is responsible for clearing password.
163 */
164 /*
165 * Usually, when keyType is decoded from ASN.1 it will contain a
166 * value indicating what the algorithm to be used is. However, when
167 * converting from a password to a key for the AS-EXCHANGE, this
168 * keyType will not be available. Use builtin list of default etypes
169 * as the default in that case. If default_tkt_enctypes was set in
170 * the libdefaults of krb5.conf, then use that sequence.
171 */
172 // Used in Krb5LoginModule
173 public static EncryptionKey[] acquireSecretKeys(char[] password,
174 String salt) throws KrbException {
175 return (acquireSecretKeys(password, salt, false, 0, null));
176 }
177
178 /**
179 * Generates a list of keys using the given principal, password,
180 * and the pre-authentication values.
181 */
182 public static EncryptionKey[] acquireSecretKeys(char[] password,
183 String salt, boolean pa_exists, int pa_etype, byte[] pa_s2kparams)
184 throws KrbException {
185
186 int[] etypes = EType.getDefaults("default_tkt_enctypes");
187 if (etypes == null) {
188 etypes = EType.getBuiltInDefaults();
189 }
190
191 // set the preferred etype for preauth
192 if ((pa_exists) && (pa_etype != EncryptedData.ETYPE_NULL)) {
193 if (DEBUG) {
194 System.out.println("Pre-Authentication: " +
195 "Set preferred etype = " + pa_etype);
196 }
197 if (EType.isSupported(pa_etype)) {
198 // reset etypes to preferred value
199 etypes = new int[1];
200 etypes[0] = pa_etype;
201 }
202 }
203
204 EncryptionKey[] encKeys = new EncryptionKey[etypes.length];
205 for (int i = 0; i < etypes.length; i++) {
206 if (EType.isSupported(etypes[i])) {
207 encKeys[i] = new EncryptionKey(
208 stringToKey(password, salt, pa_s2kparams, etypes[i]),
209 etypes[i], null);
210 } else {
211 if (DEBUG) {
212 System.out.println("Encryption Type " +
213 EType.toString(etypes[i]) +
214 " is not supported/enabled");
215 }
216 }
217 }
218 return encKeys;
219 }
220
221 // Used in Krb5AcceptCredential, self
222 public EncryptionKey(byte[] keyValue,
223 int keyType,
224 Integer kvno) {
225
226 if (keyValue != null) {
227 this.keyValue = new byte[keyValue.length];
228 System.arraycopy(keyValue, 0, this.keyValue, 0, keyValue.length);
229 } else {
230 throw new IllegalArgumentException("EncryptionKey: " +
231 "Key bytes cannot be null!");
232 }
233 this.keyType = keyType;
234 this.kvno = kvno;
235 }
236
237 /**
238 * Constructs an EncryptionKey by using the specified key type and key
239 * value. It is used to recover the key when retrieving data from
240 * credential cache file.
241 *
242 */
243 // Used in JSSE (KerberosWrapper), Credentials,
244 // javax.security.auth.kerberos.KeyImpl
245 public EncryptionKey(int keyType,
246 byte[] keyValue) {
247 this(keyValue, keyType, null);
248 }
249
250 private static byte[] stringToKey(char[] password, String salt,
251 byte[] s2kparams, int keyType) throws KrbCryptoException {
252
253 char[] slt = salt.toCharArray();
254 char[] pwsalt = new char[password.length + slt.length];
255 System.arraycopy(password, 0, pwsalt, 0, password.length);
256 System.arraycopy(slt, 0, pwsalt, password.length, slt.length);
257 Arrays.fill(slt, '0');
258
259 try {
260 switch (keyType) {
261 case EncryptedData.ETYPE_DES_CBC_CRC:
262 case EncryptedData.ETYPE_DES_CBC_MD5:
263 return Des.string_to_key_bytes(pwsalt);
264
265 case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
266 return Des3.stringToKey(pwsalt);
267
268 case EncryptedData.ETYPE_ARCFOUR_HMAC:
269 return ArcFourHmac.stringToKey(password);
270
271 case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
272 return Aes128.stringToKey(password, salt, s2kparams);
273
274 case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
275 return Aes256.stringToKey(password, salt, s2kparams);
276
277 default:
278 throw new IllegalArgumentException("encryption type " +
279 EType.toString(keyType) + " not supported");
280 }
281
282 } catch (GeneralSecurityException e) {
283 KrbCryptoException ke = new KrbCryptoException(e.getMessage());
284 ke.initCause(e);
285 throw ke;
286 } finally {
287 Arrays.fill(pwsalt, '0');
288 }
289 }
290
291 // Used in javax.security.auth.kerberos.KeyImpl
292 public EncryptionKey(char[] password,
293 String salt,
294 String algorithm) throws KrbCryptoException {
295
296 if (algorithm == null || algorithm.equalsIgnoreCase("DES")) {
297 keyType = EncryptedData.ETYPE_DES_CBC_MD5;
298 } else if (algorithm.equalsIgnoreCase("DESede")) {
299 keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;
300 } else if (algorithm.equalsIgnoreCase("AES128")) {
301 keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;
302 } else if (algorithm.equalsIgnoreCase("ArcFourHmac")) {
303 keyType = EncryptedData.ETYPE_ARCFOUR_HMAC;
304 } else if (algorithm.equalsIgnoreCase("AES256")) {
305 keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;
306 // validate if AES256 is enabled
307 if (!EType.isSupported(keyType)) {
308 throw new IllegalArgumentException("Algorithm " + algorithm +
309 " not enabled");
310 }
311 } else {
312 throw new IllegalArgumentException("Algorithm " + algorithm +
313 " not supported");
314 }
315
316 keyValue = stringToKey(password, salt, null, keyType);
317 kvno = null;
318 }
319
320 /**
321 * Generates a sub-sessionkey from a given session key.
322 */
323 // Used in KrbApRep, KrbApReq
324 EncryptionKey(EncryptionKey key) throws KrbCryptoException {
325 // generate random sub-session key
326 keyValue = Confounder.bytes(key.keyValue.length);
327 for (int i = 0; i < keyValue.length; i++) {
328 keyValue[i] ^= key.keyValue[i];
329 }
330 keyType = key.keyType;
331
332 // check for key parity and weak keys
333 try {
334 // check for DES key
335 if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) ||
336 (keyType == EncryptedData.ETYPE_DES_CBC_CRC)) {
337 // fix DES key parity
338 if (!DESKeySpec.isParityAdjusted(keyValue, 0)) {
339 keyValue = Des.set_parity(keyValue);
340 }
341 // check for weak key
342 if (DESKeySpec.isWeak(keyValue, 0)) {
343 keyValue[7] = (byte)(keyValue[7] ^ 0xF0);
344 }
345 }
346 // check for 3DES key
347 if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
348 // fix 3DES key parity
349 if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) {
350 keyValue = Des3.parityFix(keyValue);
351 }
352 // check for weak keys
353 byte[] oneKey = new byte[8];
354 for (int i=0; i<keyValue.length; i+=8) {
355 System.arraycopy(keyValue, i, oneKey, 0, 8);
356 if (DESKeySpec.isWeak(oneKey, 0)) {
357 keyValue[i+7] = (byte)(keyValue[i+7] ^ 0xF0);
358 }
359 }
360 }
361 } catch (GeneralSecurityException e) {
362 KrbCryptoException ke = new KrbCryptoException(e.getMessage());
363 ke.initCause(e);
364 throw ke;
365 }
366 }
367
368 /**
369 * Constructs an instance of EncryptionKey type.
370 * @param encoding a single DER-encoded value.
371 * @exception Asn1Exception if an error occurs while decoding an ASN1
372 * encoded data.
373 * @exception IOException if an I/O error occurs while reading encoded
374 * data.
375 *
376 *
377 */
378 // Used in javax.security.auth.kerberos.KeyImpl
379 public EncryptionKey(DerValue encoding) throws Asn1Exception, IOException {
380 DerValue der;
381 if (encoding.getTag() != DerValue.tag_Sequence) {
382 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
383 }
384 der = encoding.getData().getDerValue();
385 if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
386 keyType = der.getData().getBigInteger().intValue();
387 }
388 else
389 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
390 der = encoding.getData().getDerValue();
391 if ((der.getTag() & (byte)0x1F) == (byte)0x01) {
392 keyValue = der.getData().getOctetString();
393 }
394 else
395 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
396 if (der.getData().available() > 0) {
397 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
398 }
399 }
400
401 /**
402 * Returns the ASN.1 encoding of this EncryptionKey.
403 *
404 * <xmp>
405 * EncryptionKey ::= SEQUENCE {
406 * keytype[0] INTEGER,
407 * keyvalue[1] OCTET STRING }
408 * </xmp>
409 *
410 * <p>
411 * This definition reflects the Network Working Group RFC 4120
412 * specification available at
413 * <a href="http://www.ietf.org/rfc/rfc4120.txt">
414 * http://www.ietf.org/rfc/rfc4120.txt</a>.
415 *
416 * @return byte array of encoded EncryptionKey object.
417 * @exception Asn1Exception if an error occurs while decoding an ASN1
418 * encoded data.
419 * @exception IOException if an I/O error occurs while reading encoded
420 * data.
421 *
422 */
423 public synchronized byte[] asn1Encode() throws Asn1Exception, IOException {
424 DerOutputStream bytes = new DerOutputStream();
425 DerOutputStream temp = new DerOutputStream();
426 temp.putInteger(keyType);
427 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
428 (byte)0x00), temp);
429 temp = new DerOutputStream();
430 temp.putOctetString(keyValue);
431 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
432 (byte)0x01), temp);
433 temp = new DerOutputStream();
434 temp.write(DerValue.tag_Sequence, bytes);
435 return temp.toByteArray();
436 }
437
438 public synchronized void destroy() {
439 if (keyValue != null)
440 for (int i = 0; i < keyValue.length; i++)
441 keyValue[i] = 0;
442 }
443
444
445 /**
446 * Parse (unmarshal) an Encryption key from a DER input stream. This form
447 * parsing might be used when expanding a value which is part of
448 * a constructed sequence and uses explicitly tagged type.
449 *
450 * @param data the Der input stream value, which contains one or more
451 * marshaled value.
452 * @param explicitTag tag number.
453 * @param optional indicate if this data field is optional
454 * @exception Asn1Exception if an error occurs while decoding an ASN1
455 * encoded data.
456 * @exception IOException if an I/O error occurs while reading encoded
457 * data.
458 * @return an instance of EncryptionKey.
459 *
460 */
461 public static EncryptionKey parse(DerInputStream data, byte
462 explicitTag, boolean optional) throws
463 Asn1Exception, IOException {
464 if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=
465 explicitTag)) {
466 return null;
467 }
468 DerValue der = data.getDerValue();
469 if (explicitTag != (der.getTag() & (byte)0x1F)) {
470 throw new Asn1Exception(Krb5.ASN1_BAD_ID);
471 } else {
472 DerValue subDer = der.getData().getDerValue();
473 return new EncryptionKey(subDer);
474 }
475 }
476
477 /**
478 * Writes key value in FCC format to a <code>CCacheOutputStream</code>.
479 *
480 * @param cos a <code>CCacheOutputStream</code> to be written to.
481 * @exception IOException if an I/O exception occurs.
482 * @see sun.security.krb5.internal.ccache.CCacheOutputStream
483 *
484 */
485 public synchronized void writeKey(CCacheOutputStream cos)
486 throws IOException {
487
488 cos.write16(keyType);
489 // we use KRB5_FCC_FVNO_3
490 cos.write16(keyType); // key type is recorded twice.
491 cos.write32(keyValue.length);
492 for (int i = 0; i < keyValue.length; i++) {
493 cos.write8(keyValue[i]);
494 }
495 }
496
497 public String toString() {
498 return new String("EncryptionKey: keyType=" + keyType
499 + " kvno=" + kvno
500 + " keyValue (hex dump)="
501 + (keyValue == null || keyValue.length == 0 ?
502 " Empty Key" : '\n' + Krb5.hexDumper.encode(keyValue)
503 + '\n'));
504 }
505
506 public static EncryptionKey findKey(int etype, EncryptionKey[] keys)
507 throws KrbException {
508
509 // check if encryption type is supported
510 if (!EType.isSupported(etype)) {
511 throw new KrbException("Encryption type " +
512 EType.toString(etype) + " is not supported/enabled");
513 }
514
515 int ktype;
516 for (int i = 0; i < keys.length; i++) {
517 ktype = keys[i].getEType();
518 if (EType.isSupported(ktype)) {
519 if (etype == ktype) {
520 return keys[i];
521 }
522 }
523 }
524 // Key not found.
525 // allow DES key to be used for the DES etypes
526 if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
527 etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
528 for (int i = 0; i < keys.length; i++) {
529 ktype = keys[i].getEType();
530 if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
531 ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
532 return new EncryptionKey(etype, keys[i].getBytes());
533 }
534 }
535 }
536 return null;
537 }
538}