| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.krb5.internal.crypto.dk;  | 
 | 
 | 
 | 
import javax.crypto.Cipher;  | 
 | 
import javax.crypto.Mac;  | 
 | 
import javax.crypto.SecretKeyFactory;  | 
 | 
import javax.crypto.SecretKey;  | 
 | 
import javax.crypto.spec.SecretKeySpec;  | 
 | 
import javax.crypto.spec.DESKeySpec;  | 
 | 
import javax.crypto.spec.DESedeKeySpec;  | 
 | 
import javax.crypto.spec.IvParameterSpec;  | 
 | 
import java.security.spec.KeySpec;  | 
 | 
import java.security.GeneralSecurityException;  | 
 | 
import java.security.InvalidKeyException;  | 
 | 
import java.util.Arrays;  | 
 | 
 | 
 | 
public class Des3DkCrypto extends DkCrypto { | 
 | 
 | 
 | 
    private static final byte[] ZERO_IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; | 
 | 
 | 
 | 
    public Des3DkCrypto() { | 
 | 
    }  | 
 | 
 | 
 | 
    protected int getKeySeedLength() { | 
 | 
        return 168;     | 
 | 
    }  | 
 | 
 | 
 | 
    public byte[] stringToKey(char[] salt) throws GeneralSecurityException { | 
 | 
        byte[] saltUtf8 = null;  | 
 | 
        try { | 
 | 
            saltUtf8 = charToUtf8(salt);  | 
 | 
            return stringToKey(saltUtf8, null);  | 
 | 
        } finally { | 
 | 
            if (saltUtf8 != null) { | 
 | 
                Arrays.fill(saltUtf8, (byte)0);  | 
 | 
            }  | 
 | 
            // Caller responsible for clearing its own salt  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private byte[] stringToKey(byte[] secretAndSalt, byte[] opaque)  | 
 | 
        throws GeneralSecurityException { | 
 | 
 | 
 | 
        if (opaque != null && opaque.length > 0) { | 
 | 
            throw new RuntimeException("Invalid parameter to stringToKey"); | 
 | 
        }  | 
 | 
 | 
 | 
        byte[] tmpKey = randomToKey(nfold(secretAndSalt, getKeySeedLength()));  | 
 | 
        return dk(tmpKey, KERBEROS_CONSTANT);  | 
 | 
    }  | 
 | 
 | 
 | 
    public byte[] parityFix(byte[] value)  | 
 | 
        throws GeneralSecurityException { | 
 | 
          | 
 | 
        setParityBit(value);  | 
 | 
        return value;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected byte[] randomToKey(byte[] in) { | 
 | 
        if (in.length != 21) { | 
 | 
            throw new IllegalArgumentException("input must be 168 bits"); | 
 | 
        }  | 
 | 
 | 
 | 
        byte[] one = keyCorrection(des3Expand(in, 0, 7));  | 
 | 
        byte[] two = keyCorrection(des3Expand(in, 7, 14));  | 
 | 
        byte[] three = keyCorrection(des3Expand(in, 14, 21));  | 
 | 
 | 
 | 
        byte[] key = new byte[24];  | 
 | 
        System.arraycopy(one, 0, key, 0, 8);  | 
 | 
        System.arraycopy(two, 0, key, 8, 8);  | 
 | 
        System.arraycopy(three, 0, key, 16, 8);  | 
 | 
 | 
 | 
        return key;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static byte[] keyCorrection(byte[] key) { | 
 | 
          | 
 | 
        try { | 
 | 
            if (DESKeySpec.isWeak(key, 0)) { | 
 | 
                key[7] = (byte)(key[7] ^ 0xF0);  | 
 | 
            }  | 
 | 
        } catch (InvalidKeyException ex) { | 
 | 
            // swallow, since it should never happen  | 
 | 
        }  | 
 | 
        return key;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static byte[] des3Expand(byte[] input, int start, int end) { | 
 | 
        if ((end - start) != 7)  | 
 | 
            throw new IllegalArgumentException(  | 
 | 
                "Invalid length of DES Key Value:" + start + "," + end);  | 
 | 
 | 
 | 
        byte[] result = new byte[8];  | 
 | 
        byte last = 0;  | 
 | 
        System.arraycopy(input, start, result, 0, 7);  | 
 | 
        byte posn = 0;  | 
 | 
 | 
 | 
          | 
 | 
        for (int i = start; i < end; i++) { | 
 | 
            byte bit = (byte) (input[i]&0x01);  | 
 | 
            if (debug) { | 
 | 
                System.out.println(i + ": " + Integer.toHexString(input[i]) +  | 
 | 
                    " bit= " + Integer.toHexString(bit));  | 
 | 
            }  | 
 | 
            ++posn;  | 
 | 
            if (bit != 0) { | 
 | 
                last |= (bit<<posn);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (debug) { | 
 | 
            System.out.println("last: " + Integer.toHexString(last)); | 
 | 
        }  | 
 | 
        result[7] = last;  | 
 | 
        setParityBit(result);  | 
 | 
        return result;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static void setParityBit(byte[] key) { | 
 | 
        for (int i = 0; i < key.length; i++) { | 
 | 
            int b = key[i] & 0xfe;  | 
 | 
            b |= (Integer.bitCount(b) & 1) ^ 1;  | 
 | 
            key[i] = (byte) b;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    protected Cipher getCipher(byte[] key, byte[] ivec, int mode)  | 
 | 
        throws GeneralSecurityException { | 
 | 
          | 
 | 
        SecretKeyFactory factory = SecretKeyFactory.getInstance("desede"); | 
 | 
 | 
 | 
          | 
 | 
        KeySpec spec = new DESedeKeySpec(key, 0);  | 
 | 
 | 
 | 
          | 
 | 
        SecretKey secretKey = factory.generateSecret(spec);  | 
 | 
 | 
 | 
          | 
 | 
        if (ivec == null) { | 
 | 
            ivec = ZERO_IV;  | 
 | 
        }  | 
 | 
 | 
 | 
        // NoSuchAlgorithmException, NoSuchPaddingException  | 
 | 
          | 
 | 
        Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); | 
 | 
        IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);  | 
 | 
 | 
 | 
          | 
 | 
        cipher.init(mode, secretKey, encIv);  | 
 | 
 | 
 | 
        return cipher;  | 
 | 
    }  | 
 | 
 | 
 | 
    public int getChecksumLength() { | 
 | 
        return 20;    | 
 | 
    }  | 
 | 
 | 
 | 
    protected byte[] getHmac(byte[] key, byte[] msg)  | 
 | 
        throws GeneralSecurityException { | 
 | 
 | 
 | 
        SecretKey keyKi = new SecretKeySpec(key, "HmacSHA1");  | 
 | 
        Mac m = Mac.getInstance("HmacSHA1"); | 
 | 
        m.init(keyKi);  | 
 | 
        return m.doFinal(msg);  | 
 | 
    }  | 
 | 
}  |