699 lines · 24.9 KB · Java
RawDownload
/* * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright (C) 1998 by the FundsXpress, INC. * * All rights reserved. * * Export of this software from the United States of America may require * a specific license from the United States Government.  It is the * responsibility of any person or organization contemplating export to * obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of FundsXpress. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission.  FundsXpress makes no representations about the suitability of * this software for any purpose.  It is provided "as is" without express * or implied warranty. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ package sun.security.krb5.internal.crypto.dk; import javax.crypto.Cipher;import javax.crypto.Mac;import java.security.GeneralSecurityException;import java.io.UnsupportedEncodingException;import java.util.Arrays;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.nio.charset.Charset;import java.nio.CharBuffer;import java.nio.ByteBuffer;import sun.misc.HexDumpEncoder;import sun.security.krb5.Confounder;import sun.security.krb5.internal.crypto.KeyUsage;import sun.security.krb5.KrbCryptoException; /** * Implements Derive Key cryptography functionality as defined in RFC 3961. * http://www.ietf.org/rfc/rfc3961.txt * * This is an abstract class. Concrete subclasses need to implement * the abstract methods. */ public abstract class DkCrypto {     protected static final boolean debug = false;     // These values correspond to the ASCII encoding for the string "kerberos"    static final byte[] KERBEROS_CONSTANT =        {0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73};     protected abstract int getKeySeedLength();  // in bits     protected abstract byte[] randomToKey(byte[] in);     protected abstract Cipher getCipher(byte[] key, byte[] ivec, int mode)        throws GeneralSecurityException;     public abstract int getChecksumLength();  // in bytes     protected abstract byte[] getHmac(byte[] key, byte[] plaintext)        throws GeneralSecurityException;     /**     * From RFC 3961.     *     * encryption function       conf = random string of length c     *                     pad = shortest string to bring confounder     *                           and plaintext to a length that's a     *                           multiple of m     *                     (C1, newIV) = E(Ke, conf | plaintext | pad,     *                                     oldstate.ivec)     *                    H1 = HMAC(Ki, conf | plaintext | pad)     *                     ciphertext =  C1 | H1[1..h]     *                     newstate.ivec = newIV     *     * @param ivec initial vector to use when initializing the cipher; if null,     *     then blocksize number of zeros are used,     * @param new_ivec if non-null, it is updated upon return to be the     *       new ivec to use when calling encrypt next time     */    public byte[] encrypt(byte[] baseKey, int usage,        byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)        throws GeneralSecurityException, KrbCryptoException {         if (!KeyUsage.isValid(usage)) {            throw new GeneralSecurityException("Invalid key usage number: "                                                + usage);        }         byte[] Ke = null;        byte[] Ki = null;         try {            // Derive encryption key             byte[] constant = new byte[5];            constant[0] = (byte) ((usage>>24)&0xff);            constant[1] = (byte) ((usage>>16)&0xff);            constant[2] = (byte) ((usage>>8)&0xff);            constant[3] = (byte) (usage&0xff);             constant[4] = (byte) 0xaa;             Ke = dk(baseKey, constant);            if (debug) {                System.err.println("usage: " + usage);                if (ivec != null) {                    traceOutput("old_state.ivec", ivec, 0, ivec.length);                }                traceOutput("plaintext", plaintext, start, Math.min(len, 32));                traceOutput("constant", constant, 0, constant.length);                traceOutput("baseKey", baseKey, 0, baseKey.length);                traceOutput("Ke", Ke, 0, Ke.length);            }             // Encrypt            // C1 = E(Ke, conf | plaintext | pad, oldivec)            Cipher encCipher = getCipher(Ke, ivec, Cipher.ENCRYPT_MODE);            int blockSize = encCipher.getBlockSize();            byte[] confounder = Confounder.bytes(blockSize);             int plainSize = roundup(confounder.length + len, blockSize);            if (debug) {                System.err.println("confounder = " + confounder.length +                    "; plaintext = " + len + "; padding = " +                    (plainSize - confounder.length - len) + "; total = " +                        plainSize);                traceOutput("confounder", confounder, 0, confounder.length);            }             byte[] toBeEncrypted = new byte[plainSize];            System.arraycopy(confounder, 0, toBeEncrypted,                                0, confounder.length);            System.arraycopy(plaintext, start, toBeEncrypted,                                confounder.length, len);             // Set padding bytes to zero            Arrays.fill(toBeEncrypted, confounder.length + len, plainSize,                        (byte)0);             int cipherSize = encCipher.getOutputSize(plainSize);            int ccSize =  cipherSize + getChecksumLength();  // cipher | hmac             byte[] ciphertext = new byte[ccSize];             encCipher.doFinal(toBeEncrypted, 0, plainSize, ciphertext, 0);             // Update ivec for next operation            // (last blockSize bytes of ciphertext)            // newstate.ivec = newIV            if (new_ivec != null && new_ivec.length == blockSize) {                System.arraycopy(ciphertext,  cipherSize - blockSize,                    new_ivec, 0, blockSize);                if (debug) {                    traceOutput("new_ivec", new_ivec, 0, new_ivec.length);                }            }             // Derive integrity key            constant[4] = (byte) 0x55;            Ki = dk(baseKey, constant);            if (debug) {                traceOutput("constant", constant, 0, constant.length);                traceOutput("Ki", Ki, 0, Ke.length);            }             // Generate checksum            // H1 = HMAC(Ki, conf | plaintext | pad)            byte[] hmac = getHmac(Ki, toBeEncrypted);             if (debug) {                traceOutput("hmac", hmac, 0, hmac.length);                traceOutput("ciphertext", ciphertext, 0,                                Math.min(ciphertext.length, 32));            }             // C1 | H1[1..h]            System.arraycopy(hmac, 0, ciphertext, cipherSize,                                getChecksumLength());            return ciphertext;        } finally {            if (Ke != null) {                Arrays.fill(Ke, 0, Ke.length, (byte) 0);            }            if (Ki != null) {                Arrays.fill(Ki, 0, Ki.length, (byte) 0);            }        }    }     /**     * Performs encryption using given key only; does not add     * confounder, padding, or checksum. Incoming data to be encrypted     * assumed to have the correct blocksize.     * Ignore key usage.     */    public byte[] encryptRaw(byte[] baseKey, int usage,        byte[] ivec, byte[] plaintext, int start, int len)        throws GeneralSecurityException, KrbCryptoException {         if (debug) {            System.err.println("usage: " + usage);            if (ivec != null) {                traceOutput("old_state.ivec", ivec, 0, ivec.length);            }            traceOutput("plaintext", plaintext, start, Math.min(len, 32));            traceOutput("baseKey", baseKey, 0, baseKey.length);        }         // Encrypt        Cipher encCipher = getCipher(baseKey, ivec, Cipher.ENCRYPT_MODE);        int blockSize = encCipher.getBlockSize();         if ((len % blockSize) != 0) {            throw new GeneralSecurityException(                "length of data to be encrypted (" + len +                ") is not a multiple of the blocksize (" + blockSize + ")");        }         int cipherSize = encCipher.getOutputSize(len);        byte[] ciphertext = new byte[cipherSize];         encCipher.doFinal(plaintext, 0, len, ciphertext, 0);        return ciphertext;    }     /**     * Decrypts data using specified key and initial vector.     * @param baseKey encryption key to use     * @param ciphertext  encrypted data to be decrypted     * @param usage ignored     */    public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec,        byte[] ciphertext, int start, int len)        throws GeneralSecurityException {         if (debug) {            System.err.println("usage: " + usage);            if (ivec != null) {                traceOutput("old_state.ivec", ivec, 0, ivec.length);            }            traceOutput("ciphertext", ciphertext, start, Math.min(len, 32));            traceOutput("baseKey", baseKey, 0, baseKey.length);        }         Cipher decCipher = getCipher(baseKey, ivec, Cipher.DECRYPT_MODE);         int blockSize = decCipher.getBlockSize();         if ((len % blockSize) != 0) {            throw new GeneralSecurityException(                "length of data to be decrypted (" + len +                ") is not a multiple of the blocksize (" + blockSize + ")");        }         byte[] decrypted = decCipher.doFinal(ciphertext, start, len);         if (debug) {            traceOutput("decrypted", decrypted, 0,                Math.min(decrypted.length, 32));        }         return decrypted;    }     /**     * @param baseKey key from which keys are to be derived using usage     * @param ciphertext  E(Ke, conf | plaintext | padding, ivec) | H1[1..h]     */    public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec,        byte[] ciphertext, int start, int len) throws GeneralSecurityException {         if (!KeyUsage.isValid(usage)) {            throw new GeneralSecurityException("Invalid key usage number: "                                                + usage);        }         byte[] Ke = null;        byte[] Ki = null;         try {            // Derive encryption key            byte[] constant = new byte[5];            constant[0] = (byte) ((usage>>24)&0xff);            constant[1] = (byte) ((usage>>16)&0xff);            constant[2] = (byte) ((usage>>8)&0xff);            constant[3] = (byte) (usage&0xff);             constant[4] = (byte) 0xaa;             Ke = dk(baseKey, constant);  // Encryption key             if (debug) {                System.err.println("usage: " + usage);                if (ivec != null) {                    traceOutput("old_state.ivec", ivec, 0, ivec.length);                }                traceOutput("ciphertext", ciphertext, start, Math.min(len, 32));                traceOutput("constant", constant, 0, constant.length);                traceOutput("baseKey", baseKey, 0, baseKey.length);                traceOutput("Ke", Ke, 0, Ke.length);            }             Cipher decCipher = getCipher(Ke, ivec, Cipher.DECRYPT_MODE);            int blockSize = decCipher.getBlockSize();             // Decrypt [confounder | plaintext | padding] (without checksum)            int cksumSize = getChecksumLength();            int cipherSize = len - cksumSize;            byte[] decrypted = decCipher.doFinal(ciphertext, start, cipherSize);             if (debug) {                traceOutput("decrypted", decrypted, 0,                                Math.min(decrypted.length, 32));            }             // decrypted = [confounder | plaintext | padding]             // Derive integrity key            constant[4] = (byte) 0x55;            Ki = dk(baseKey, constant);  // Integrity key            if (debug) {                traceOutput("constant", constant, 0, constant.length);                traceOutput("Ki", Ki, 0, Ke.length);            }             // Verify checksum            // H1 = HMAC(Ki, conf | plaintext | pad)            byte[] calculatedHmac = getHmac(Ki, decrypted);             if (debug) {                traceOutput("calculated Hmac", calculatedHmac, 0,                    calculatedHmac.length);                traceOutput("message Hmac", ciphertext, cipherSize,                    cksumSize);            }             boolean cksumFailed = false;            if (calculatedHmac.length >= cksumSize) {                for (int i = 0; i < cksumSize; i++) {                    if (calculatedHmac[i] != ciphertext[cipherSize+i]) {                        cksumFailed = true;                        break;                    }                }            }             if (cksumFailed) {                throw new GeneralSecurityException("Checksum failed");            }             // Prepare decrypted msg and ivec to be returned            // Last blockSize bytes of ciphertext without checksum            if (ivec != null && ivec.length == blockSize) {                System.arraycopy(ciphertext,  start + cipherSize - blockSize,                    ivec, 0, blockSize);                if (debug) {                    traceOutput("new_state.ivec", ivec, 0, ivec.length);                }            }             // Get rid of confounder            // [plaintext | padding]            byte[] plaintext = new byte[decrypted.length - blockSize];            System.arraycopy(decrypted, blockSize, plaintext,                                0, plaintext.length);            return plaintext; // padding still there        } finally {            if (Ke != null) {                Arrays.fill(Ke, 0, Ke.length, (byte) 0);            }            if (Ki != null) {                Arrays.fill(Ki, 0, Ki.length, (byte) 0);            }        }    }     // Round up to the next blocksize    int roundup(int n, int blocksize) {        return (((n + blocksize - 1) / blocksize) * blocksize);    }     public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input,        int start, int len) throws GeneralSecurityException {         if (!KeyUsage.isValid(usage)) {            throw new GeneralSecurityException("Invalid key usage number: "                                                + usage);        }         // Derive keys        byte[] constant = new byte[5];        constant[0] = (byte) ((usage>>24)&0xff);        constant[1] = (byte) ((usage>>16)&0xff);        constant[2] = (byte) ((usage>>8)&0xff);        constant[3] = (byte) (usage&0xff);         constant[4] = (byte) 0x99;         byte[] Kc = dk(baseKey, constant);  // Checksum key        if (debug) {            System.err.println("usage: " + usage);            traceOutput("input", input, start, Math.min(len, 32));            traceOutput("constant", constant, 0, constant.length);            traceOutput("baseKey", baseKey, 0, baseKey.length);            traceOutput("Kc", Kc, 0, Kc.length);        }         try {            // Generate checksum            // H1 = HMAC(Kc, input)            byte[] hmac = getHmac(Kc, input);            if (debug) {                traceOutput("hmac", hmac, 0, hmac.length);            }            if (hmac.length == getChecksumLength()) {                return hmac;            } else if (hmac.length > getChecksumLength()) {                byte[] buf = new byte[getChecksumLength()];                System.arraycopy(hmac, 0, buf, 0, buf.length);                return buf;            } else {                throw new GeneralSecurityException("checksum size too short: " +                    hmac.length + "; expecting : " + getChecksumLength());            }        } finally {            Arrays.fill(Kc, 0, Kc.length, (byte)0);        }    }     // DK(Key, Constant) = random-to-key(DR(Key, Constant))    byte[] dk(byte[] key, byte[] constant)        throws GeneralSecurityException {        return randomToKey(dr(key, constant));    }     /*     * From RFC 3961.     *     * DR(Key, Constant) = k-truncate(E(Key, Constant,     *                                  initial-cipher-state))     *     * Here DR is the random-octet generation function described below, and     * DK is the key-derivation function produced from it.  In this     * construction, E(Key, Plaintext, CipherState) is a cipher, Constant is     * a well-known constant determined by the specific usage of this     * function, and k-truncate truncates its argument by taking the first k     * bits.  Here, k is the key generation seed length needed for the     * encryption system.     *     * The output of the DR function is a string of bits; the actual key is     * produced by applying the cryptosystem's random-to-key operation on     * this bitstring.     *     * If the Constant is smaller than the cipher block size of E, then it     * must be expanded with n-fold() so it can be encrypted.  If the output     * of E is shorter than k bits it is fed back into the encryption as     * many times as necessary.  The construct is as follows (where |     * indicates concatentation):     *     * K1 = E(Key, n-fold(Constant), initial-cipher-state)     * K2 = E(Key, K1, initial-cipher-state)     * K3 = E(Key, K2, initial-cipher-state)     * K4 = ...     *     * DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)     */    private byte[] dr(byte[] key, byte[] constant)        throws GeneralSecurityException {         Cipher encCipher = getCipher(key, null, Cipher.ENCRYPT_MODE);        int blocksize = encCipher.getBlockSize();         if (constant.length != blocksize) {            constant = nfold(constant, blocksize * 8);        }        byte[] toBeEncrypted = constant;         int keybytes = (getKeySeedLength()>>3);  // from bits to bytes        byte[] rawkey = new byte[keybytes];        int posn = 0;         /* loop encrypting the blocks until enough key bytes are generated */        int n = 0, len;        while (n < keybytes) {            if (debug) {                System.err.println("Encrypting: " +

Lines 1–500 of 699.