0N/A/*
4102N/A * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A *
0N/A * (C) Copyright IBM Corp. 1999 All Rights Reserved.
0N/A * Copyright 1997 The Open Group Research Institute. All rights reserved.
0N/A */
0N/A
0N/Apackage sun.security.krb5;
0N/A
0N/Aimport sun.security.util.*;
0N/Aimport sun.security.krb5.internal.*;
0N/Aimport sun.security.krb5.internal.crypto.*;
0N/Aimport java.io.IOException;
0N/Aimport java.security.GeneralSecurityException;
0N/Aimport java.util.Arrays;
0N/Aimport sun.security.krb5.internal.ktab.KeyTab;
0N/Aimport sun.security.krb5.internal.ccache.CCacheOutputStream;
0N/Aimport javax.crypto.spec.DESKeySpec;
0N/Aimport javax.crypto.spec.DESedeKeySpec;
0N/A
0N/A/**
0N/A * This class encapsulates the concept of an EncryptionKey. An encryption
0N/A * key is defined in RFC 4120 as:
0N/A *
0N/A * EncryptionKey ::= SEQUENCE {
0N/A * keytype [0] Int32 -- actually encryption type --,
0N/A * keyvalue [1] OCTET STRING
0N/A * }
0N/A *
0N/A * keytype
0N/A * This field specifies the encryption type of the encryption key
0N/A * that follows in the keyvalue field. Although its name is
0N/A * "keytype", it actually specifies an encryption type. Previously,
0N/A * multiple cryptosystems that performed encryption differently but
0N/A * were capable of using keys with the same characteristics were
0N/A * permitted to share an assigned number to designate the type of
0N/A * key; this usage is now deprecated.
0N/A *
0N/A * keyvalue
0N/A * This field contains the key itself, encoded as an octet string.
0N/A */
0N/A
0N/Apublic class EncryptionKey
0N/A implements Cloneable {
0N/A
0N/A public static final EncryptionKey NULL_KEY =
0N/A new EncryptionKey(new byte[] {}, EncryptedData.ETYPE_NULL, null);
0N/A
0N/A private int keyType;
0N/A private byte[] keyValue;
0N/A private Integer kvno; // not part of ASN1 encoding;
0N/A
0N/A private static final boolean DEBUG = Krb5.DEBUG;
0N/A
0N/A public synchronized int getEType() {
0N/A return keyType;
0N/A }
0N/A
0N/A public final Integer getKeyVersionNumber() {
0N/A return kvno;
0N/A }
0N/A
0N/A /**
0N/A * Returns the raw key bytes, not in any ASN.1 encoding.
0N/A */
0N/A public final byte[] getBytes() {
0N/A // This method cannot be called outside sun.security, hence no
0N/A // cloning. getEncoded() calls this method.
0N/A return keyValue;
0N/A }
0N/A
0N/A public synchronized Object clone() {
0N/A return new EncryptionKey(keyValue, keyType, kvno);
0N/A }
0N/A
0N/A /**
0N/A * Obtains the latest version of the secret key of
0N/A * the principal from a keytab.
0N/A *
0N/A * @param princ the principal whose secret key is desired
0N/A * @param keytab the path to the keytab file. A value of null
0N/A * will be accepted to indicate that the default path should be
0N/A * searched.
0N/A * @returns the secret key or null if none was found.
0N/A */
0N/A /*
0N/A // Replaced by acquireSecretKeys
0N/A public static EncryptionKey acquireSecretKey(PrincipalName princ,
0N/A String keytab)
0N/A throws KrbException, IOException {
0N/A
0N/A if (princ == null) {
0N/A throw new IllegalArgumentException(
0N/A "Cannot have null pricipal name to look in keytab.");
0N/A }
0N/A
0N/A KeyTab ktab = KeyTab.getInstance(keytab);
0N/A
0N/A if (ktab == null)
0N/A return null;
0N/A
0N/A return ktab.readServiceKey(princ);
0N/A }
0N/A */
0N/A
0N/A /**
0N/A * Obtains all versions of the secret key of the principal from a
0N/A * keytab.
0N/A *
0N/A * @Param princ the principal whose secret key is desired
0N/A * @param keytab the path to the keytab file. A value of null
0N/A * will be accepted to indicate that the default path should be
0N/A * searched.
0N/A * @returns an array of secret keys or null if none were found.
0N/A */
0N/A public static EncryptionKey[] acquireSecretKeys(PrincipalName princ,
4102N/A String keytab) {
0N/A
0N/A if (princ == null)
0N/A throw new IllegalArgumentException(
0N/A "Cannot have null pricipal name to look in keytab.");
0N/A
0N/A // KeyTab getInstance(keytab) will call KeyTab.getInstance()
0N/A // if keytab is null
0N/A KeyTab ktab = KeyTab.getInstance(keytab);
0N/A return ktab.readServiceKeys(princ);
0N/A }
0N/A
0N/A /**
4391N/A * Obtains a key for a given etype of a principal with possible new salt
4391N/A * and s2kparams
4391N/A * @param cname NOT null
4391N/A * @param password NOT null
4391N/A * @param etype
4391N/A * @param snp can be NULL
4391N/A * @returns never null
4391N/A */
4391N/A public static EncryptionKey acquireSecretKey(PrincipalName cname,
4391N/A char[] password, int etype, PAData.SaltAndParams snp)
4391N/A throws KrbException {
4391N/A String salt;
4391N/A byte[] s2kparams;
4391N/A if (snp != null) {
4391N/A salt = snp.salt != null ? snp.salt : cname.getSalt();
4391N/A s2kparams = snp.params;
4391N/A } else {
4391N/A salt = cname.getSalt();
4391N/A s2kparams = null;
4391N/A }
4391N/A return acquireSecretKey(password, salt, etype, s2kparams);
4391N/A }
4391N/A
4391N/A /**
3054N/A * Obtains a key for a given etype with salt and optional s2kparams
3054N/A * @param password NOT null
3054N/A * @param salt NOT null
3054N/A * @param etype
3054N/A * @param s2kparams can be NULL
4391N/A * @returns never null
3054N/A */
3054N/A public static EncryptionKey acquireSecretKey(char[] password,
3054N/A String salt, int etype, byte[] s2kparams)
3054N/A throws KrbException {
3054N/A
3054N/A return new EncryptionKey(
3054N/A stringToKey(password, salt, s2kparams, etype),
3054N/A etype, null);
3054N/A }
3054N/A
3054N/A /**
0N/A * Generate a list of keys using the given principal and password.
0N/A * Construct a key for each configured etype.
0N/A * Caller is responsible for clearing password.
0N/A */
0N/A /*
0N/A * Usually, when keyType is decoded from ASN.1 it will contain a
0N/A * value indicating what the algorithm to be used is. However, when
0N/A * converting from a password to a key for the AS-EXCHANGE, this
0N/A * keyType will not be available. Use builtin list of default etypes
0N/A * as the default in that case. If default_tkt_enctypes was set in
0N/A * the libdefaults of krb5.conf, then use that sequence.
0N/A */
0N/A public static EncryptionKey[] acquireSecretKeys(char[] password,
3054N/A String salt) throws KrbException {
0N/A
0N/A int[] etypes = EType.getDefaults("default_tkt_enctypes");
0N/A if (etypes == null) {
0N/A etypes = EType.getBuiltInDefaults();
0N/A }
0N/A
0N/A EncryptionKey[] encKeys = new EncryptionKey[etypes.length];
0N/A for (int i = 0; i < etypes.length; i++) {
0N/A if (EType.isSupported(etypes[i])) {
0N/A encKeys[i] = new EncryptionKey(
3054N/A stringToKey(password, salt, null, etypes[i]),
0N/A etypes[i], null);
0N/A } else {
0N/A if (DEBUG) {
0N/A System.out.println("Encryption Type " +
0N/A EType.toString(etypes[i]) +
0N/A " is not supported/enabled");
0N/A }
0N/A }
0N/A }
0N/A return encKeys;
0N/A }
0N/A
0N/A // Used in Krb5AcceptCredential, self
0N/A public EncryptionKey(byte[] keyValue,
0N/A int keyType,
0N/A Integer kvno) {
0N/A
0N/A if (keyValue != null) {
0N/A this.keyValue = new byte[keyValue.length];
0N/A System.arraycopy(keyValue, 0, this.keyValue, 0, keyValue.length);
0N/A } else {
0N/A throw new IllegalArgumentException("EncryptionKey: " +
0N/A "Key bytes cannot be null!");
0N/A }
0N/A this.keyType = keyType;
0N/A this.kvno = kvno;
0N/A }
0N/A
0N/A /**
0N/A * Constructs an EncryptionKey by using the specified key type and key
0N/A * value. It is used to recover the key when retrieving data from
0N/A * credential cache file.
0N/A *
0N/A */
0N/A // Used in JSSE (KerberosWrapper), Credentials,
0N/A // javax.security.auth.kerberos.KeyImpl
0N/A public EncryptionKey(int keyType,
0N/A byte[] keyValue) {
0N/A this(keyValue, keyType, null);
0N/A }
0N/A
0N/A private static byte[] stringToKey(char[] password, String salt,
0N/A byte[] s2kparams, int keyType) throws KrbCryptoException {
0N/A
0N/A char[] slt = salt.toCharArray();
0N/A char[] pwsalt = new char[password.length + slt.length];
0N/A System.arraycopy(password, 0, pwsalt, 0, password.length);
0N/A System.arraycopy(slt, 0, pwsalt, password.length, slt.length);
0N/A Arrays.fill(slt, '0');
0N/A
0N/A try {
0N/A switch (keyType) {
0N/A case EncryptedData.ETYPE_DES_CBC_CRC:
0N/A case EncryptedData.ETYPE_DES_CBC_MD5:
0N/A return Des.string_to_key_bytes(pwsalt);
0N/A
0N/A case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
0N/A return Des3.stringToKey(pwsalt);
0N/A
0N/A case EncryptedData.ETYPE_ARCFOUR_HMAC:
0N/A return ArcFourHmac.stringToKey(password);
0N/A
0N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
0N/A return Aes128.stringToKey(password, salt, s2kparams);
0N/A
0N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
0N/A return Aes256.stringToKey(password, salt, s2kparams);
0N/A
0N/A default:
0N/A throw new IllegalArgumentException("encryption type " +
0N/A EType.toString(keyType) + " not supported");
0N/A }
0N/A
0N/A } catch (GeneralSecurityException e) {
0N/A KrbCryptoException ke = new KrbCryptoException(e.getMessage());
0N/A ke.initCause(e);
0N/A throw ke;
0N/A } finally {
0N/A Arrays.fill(pwsalt, '0');
0N/A }
0N/A }
0N/A
0N/A // Used in javax.security.auth.kerberos.KeyImpl
0N/A public EncryptionKey(char[] password,
0N/A String salt,
0N/A String algorithm) throws KrbCryptoException {
0N/A
0N/A if (algorithm == null || algorithm.equalsIgnoreCase("DES")) {
0N/A keyType = EncryptedData.ETYPE_DES_CBC_MD5;
0N/A } else if (algorithm.equalsIgnoreCase("DESede")) {
0N/A keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;
0N/A } else if (algorithm.equalsIgnoreCase("AES128")) {
0N/A keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;
0N/A } else if (algorithm.equalsIgnoreCase("ArcFourHmac")) {
0N/A keyType = EncryptedData.ETYPE_ARCFOUR_HMAC;
0N/A } else if (algorithm.equalsIgnoreCase("AES256")) {
0N/A keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;
0N/A // validate if AES256 is enabled
0N/A if (!EType.isSupported(keyType)) {
0N/A throw new IllegalArgumentException("Algorithm " + algorithm +
0N/A " not enabled");
0N/A }
0N/A } else {
0N/A throw new IllegalArgumentException("Algorithm " + algorithm +
0N/A " not supported");
0N/A }
0N/A
0N/A keyValue = stringToKey(password, salt, null, keyType);
0N/A kvno = null;
0N/A }
0N/A
0N/A /**
0N/A * Generates a sub-sessionkey from a given session key.
0N/A */
0N/A // Used in KrbApRep, KrbApReq
0N/A EncryptionKey(EncryptionKey key) throws KrbCryptoException {
0N/A // generate random sub-session key
0N/A keyValue = Confounder.bytes(key.keyValue.length);
0N/A for (int i = 0; i < keyValue.length; i++) {
0N/A keyValue[i] ^= key.keyValue[i];
0N/A }
0N/A keyType = key.keyType;
0N/A
0N/A // check for key parity and weak keys
0N/A try {
0N/A // check for DES key
0N/A if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) ||
0N/A (keyType == EncryptedData.ETYPE_DES_CBC_CRC)) {
0N/A // fix DES key parity
0N/A if (!DESKeySpec.isParityAdjusted(keyValue, 0)) {
0N/A keyValue = Des.set_parity(keyValue);
0N/A }
0N/A // check for weak key
0N/A if (DESKeySpec.isWeak(keyValue, 0)) {
0N/A keyValue[7] = (byte)(keyValue[7] ^ 0xF0);
0N/A }
0N/A }
0N/A // check for 3DES key
0N/A if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
0N/A // fix 3DES key parity
0N/A if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) {
0N/A keyValue = Des3.parityFix(keyValue);
0N/A }
0N/A // check for weak keys
0N/A byte[] oneKey = new byte[8];
0N/A for (int i=0; i<keyValue.length; i+=8) {
0N/A System.arraycopy(keyValue, i, oneKey, 0, 8);
0N/A if (DESKeySpec.isWeak(oneKey, 0)) {
0N/A keyValue[i+7] = (byte)(keyValue[i+7] ^ 0xF0);
0N/A }
0N/A }
0N/A }
0N/A } catch (GeneralSecurityException e) {
0N/A KrbCryptoException ke = new KrbCryptoException(e.getMessage());
0N/A ke.initCause(e);
0N/A throw ke;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Constructs an instance of EncryptionKey type.
0N/A * @param encoding a single DER-encoded value.
0N/A * @exception Asn1Exception if an error occurs while decoding an ASN1
0N/A * encoded data.
0N/A * @exception IOException if an I/O error occurs while reading encoded
0N/A * data.
0N/A *
0N/A *
0N/A */
0N/A // Used in javax.security.auth.kerberos.KeyImpl
0N/A public EncryptionKey(DerValue encoding) throws Asn1Exception, IOException {
0N/A DerValue der;
0N/A if (encoding.getTag() != DerValue.tag_Sequence) {
0N/A throw new Asn1Exception(Krb5.ASN1_BAD_ID);
0N/A }
0N/A der = encoding.getData().getDerValue();
0N/A if ((der.getTag() & (byte)0x1F) == (byte)0x00) {
0N/A keyType = der.getData().getBigInteger().intValue();
0N/A }
0N/A else
0N/A throw new Asn1Exception(Krb5.ASN1_BAD_ID);
0N/A der = encoding.getData().getDerValue();
0N/A if ((der.getTag() & (byte)0x1F) == (byte)0x01) {
0N/A keyValue = der.getData().getOctetString();
0N/A }
0N/A else
0N/A throw new Asn1Exception(Krb5.ASN1_BAD_ID);
0N/A if (der.getData().available() > 0) {
0N/A throw new Asn1Exception(Krb5.ASN1_BAD_ID);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the ASN.1 encoding of this EncryptionKey.
0N/A *
0N/A * <xmp>
0N/A * EncryptionKey ::= SEQUENCE {
0N/A * keytype[0] INTEGER,
0N/A * keyvalue[1] OCTET STRING }
0N/A * </xmp>
0N/A *
0N/A * <p>
0N/A * This definition reflects the Network Working Group RFC 4120
0N/A * specification available at
0N/A * <a href="http://www.ietf.org/rfc/rfc4120.txt">
0N/A * http://www.ietf.org/rfc/rfc4120.txt</a>.
0N/A *
0N/A * @return byte array of encoded EncryptionKey object.
0N/A * @exception Asn1Exception if an error occurs while decoding an ASN1
0N/A * encoded data.
0N/A * @exception IOException if an I/O error occurs while reading encoded
0N/A * data.
0N/A *
0N/A */
0N/A public synchronized byte[] asn1Encode() throws Asn1Exception, IOException {
0N/A DerOutputStream bytes = new DerOutputStream();
0N/A DerOutputStream temp = new DerOutputStream();
0N/A temp.putInteger(keyType);
0N/A bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
0N/A (byte)0x00), temp);
0N/A temp = new DerOutputStream();
0N/A temp.putOctetString(keyValue);
0N/A bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,
0N/A (byte)0x01), temp);
0N/A temp = new DerOutputStream();
0N/A temp.write(DerValue.tag_Sequence, bytes);
0N/A return temp.toByteArray();
0N/A }
0N/A
0N/A public synchronized void destroy() {
0N/A if (keyValue != null)
0N/A for (int i = 0; i < keyValue.length; i++)
0N/A keyValue[i] = 0;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Parse (unmarshal) an Encryption key from a DER input stream. This form
0N/A * parsing might be used when expanding a value which is part of
0N/A * a constructed sequence and uses explicitly tagged type.
0N/A *
0N/A * @param data the Der input stream value, which contains one or more
0N/A * marshaled value.
0N/A * @param explicitTag tag number.
0N/A * @param optional indicate if this data field is optional
0N/A * @exception Asn1Exception if an error occurs while decoding an ASN1
0N/A * encoded data.
0N/A * @exception IOException if an I/O error occurs while reading encoded
0N/A * data.
0N/A * @return an instance of EncryptionKey.
0N/A *
0N/A */
0N/A public static EncryptionKey parse(DerInputStream data, byte
0N/A explicitTag, boolean optional) throws
0N/A Asn1Exception, IOException {
0N/A if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=
0N/A explicitTag)) {
0N/A return null;
0N/A }
0N/A DerValue der = data.getDerValue();
0N/A if (explicitTag != (der.getTag() & (byte)0x1F)) {
0N/A throw new Asn1Exception(Krb5.ASN1_BAD_ID);
0N/A } else {
0N/A DerValue subDer = der.getData().getDerValue();
0N/A return new EncryptionKey(subDer);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Writes key value in FCC format to a <code>CCacheOutputStream</code>.
0N/A *
0N/A * @param cos a <code>CCacheOutputStream</code> to be written to.
0N/A * @exception IOException if an I/O exception occurs.
0N/A * @see sun.security.krb5.internal.ccache.CCacheOutputStream
0N/A *
0N/A */
0N/A public synchronized void writeKey(CCacheOutputStream cos)
0N/A throws IOException {
0N/A
0N/A cos.write16(keyType);
0N/A // we use KRB5_FCC_FVNO_3
0N/A cos.write16(keyType); // key type is recorded twice.
0N/A cos.write32(keyValue.length);
0N/A for (int i = 0; i < keyValue.length; i++) {
0N/A cos.write8(keyValue[i]);
0N/A }
0N/A }
0N/A
0N/A public String toString() {
0N/A return new String("EncryptionKey: keyType=" + keyType
0N/A + " kvno=" + kvno
0N/A + " keyValue (hex dump)="
0N/A + (keyValue == null || keyValue.length == 0 ?
2341N/A " Empty Key" : '\n'
2341N/A + Krb5.hexDumper.encodeBuffer(keyValue)
2341N/A + '\n'));
0N/A }
0N/A
1802N/A /**
1802N/A * Find a key with given etype
1802N/A */
0N/A public static EncryptionKey findKey(int etype, EncryptionKey[] keys)
1802N/A throws KrbException {
1802N/A return findKey(etype, null, keys);
1802N/A }
1802N/A
1802N/A /**
2035N/A * Determines if a kvno matches another kvno. Used in the method
2035N/A * findKey(type, kvno, keys). Always returns true if either input
2035N/A * is null or zero, in case any side does not have kvno info available.
2035N/A *
2035N/A * Note: zero is included because N/A is not a legal value for kvno
2035N/A * in javax.security.auth.kerberos.KerberosKey. Therefore, the info
2035N/A * that the kvno is N/A might be lost when converting between this
2035N/A * class and KerberosKey.
2035N/A */
2035N/A private static boolean versionMatches(Integer v1, Integer v2) {
2035N/A if (v1 == null || v1 == 0 || v2 == null || v2 == 0) {
2035N/A return true;
2035N/A }
2035N/A return v1.equals(v2);
2035N/A }
2035N/A
2035N/A /**
1802N/A * Find a key with given etype and kvno
1802N/A * @param kvno if null, return any (first?) key
1802N/A */
1802N/A public static EncryptionKey findKey(int etype, Integer kvno, EncryptionKey[] keys)
0N/A throws KrbException {
0N/A
0N/A // check if encryption type is supported
0N/A if (!EType.isSupported(etype)) {
0N/A throw new KrbException("Encryption type " +
0N/A EType.toString(etype) + " is not supported/enabled");
0N/A }
0N/A
0N/A int ktype;
2035N/A boolean etypeFound = false;
0N/A for (int i = 0; i < keys.length; i++) {
0N/A ktype = keys[i].getEType();
0N/A if (EType.isSupported(ktype)) {
1802N/A Integer kv = keys[i].getKeyVersionNumber();
2035N/A if (etype == ktype) {
2035N/A etypeFound = true;
2035N/A if (versionMatches(kvno, kv)) {
2035N/A return keys[i];
2035N/A }
0N/A }
0N/A }
0N/A }
2035N/A
0N/A // Key not found.
0N/A // allow DES key to be used for the DES etypes
0N/A if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||
0N/A etype == EncryptedData.ETYPE_DES_CBC_MD5)) {
0N/A for (int i = 0; i < keys.length; i++) {
0N/A ktype = keys[i].getEType();
0N/A if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||
1802N/A ktype == EncryptedData.ETYPE_DES_CBC_MD5) {
1802N/A Integer kv = keys[i].getKeyVersionNumber();
2035N/A etypeFound = true;
2035N/A if (versionMatches(kvno, kv)) {
1802N/A return new EncryptionKey(etype, keys[i].getBytes());
1802N/A }
0N/A }
0N/A }
0N/A }
2035N/A if (etypeFound) {
2035N/A throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);
2035N/A }
0N/A return null;
0N/A }
0N/A}