532 lines · 18.9 KB · Java
RawDownload
/* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.jgss.krb5; import org.ietf.jgss.*;import sun.security.jgss.*;import java.io.InputStream;import java.io.OutputStream;import java.io.IOException;import java.io.ByteArrayOutputStream;import sun.security.krb5.Confounder; /** * This class represents a token emitted by the GSSContext.wrap() * call. It is a MessageToken except that it also contains plaintext * or encrypted data at the end. A wrapToken has certain other rules * that are peculiar to it and different from a MICToken, which is * another type of MessageToken. All data in a WrapToken is prepended * by a random counfounder of 8 bytes. All data in a WrapToken is * also padded with one to eight bytes where all bytes are equal in * value to the number of bytes being padded. Thus, all application * data is replaced by (confounder || data || padding). * * @author Mayank Upadhyay */class WrapToken extends MessageToken {    /**     * The size of the random confounder used in a WrapToken.     */    static final int CONFOUNDER_SIZE = 8;     /*     * The padding used with a WrapToken. All data is padded to the     * next multiple of 8 bytes, even if its length is already     * multiple of 8.     * Use this table as a quick way to obtain padding bytes by     * indexing it with the number of padding bytes required.     */    static final byte[][] pads = {        null, // No, no one escapes padding        {0x01},        {0x02, 0x02},        {0x03, 0x03, 0x03},        {0x04, 0x04, 0x04, 0x04},        {0x05, 0x05, 0x05, 0x05, 0x05},        {0x06, 0x06, 0x06, 0x06, 0x06, 0x06},        {0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07},        {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}    };     /*     * A token may come in either in an InputStream or as a     * byte[]. Store a reference to it in either case and process     * it's data only later when getData() is called and     * decryption/copying is needed to be done. Note that JCE can     * decrypt both from a byte[] and from an InputStream.     */    private boolean readTokenFromInputStream = true;    private InputStream is = null;    private byte[] tokenBytes = null;    private int tokenOffset = 0;    private int tokenLen = 0;     /*     * Application data may come from an InputStream or from a     * byte[]. However, it will always be stored and processed as a     * byte[] since     * (a) the MessageDigest class only accepts a byte[] as input and     * (b) It allows writing to an OuputStream via a CipherOutputStream.     */    private byte[] dataBytes = null;    private int dataOffset = 0;    private int dataLen = 0;     // the len of the token data: (confounder || data || padding)    private int dataSize = 0;     // Accessed by CipherHelper    byte[] confounder = null;    byte[] padding = null;     private boolean privacy = false;     /**     * Constructs a WrapToken from token bytes obtained from the     * peer.     * @param context the mechanism context associated with this     * token     * @param tokenBytes the bytes of the token     * @param tokenOffset the offset of the token     * @param tokenLen the length of the token     * @param prop the MessageProp into which characteristics of the     * parsed token will be stored.     * @throws GSSException if the token is defective     */    public WrapToken(Krb5Context context,                     byte[] tokenBytes, int tokenOffset, int tokenLen,                     MessageProp prop)  throws GSSException {         // Just parse the MessageToken part first        super(Krb5Token.WRAP_ID, context,              tokenBytes, tokenOffset, tokenLen, prop);         this.readTokenFromInputStream = false;         // Will need the token bytes again when extracting data        this.tokenBytes = tokenBytes;        this.tokenOffset = tokenOffset;        this.tokenLen = tokenLen;        this.privacy = prop.getPrivacy();        dataSize =            getGSSHeader().getMechTokenLength() - getKrb5TokenSize();    }     /**     * Constructs a WrapToken from token bytes read on the fly from     * an InputStream.     * @param context the mechanism context associated with this     * token     * @param is the InputStream containing the token bytes     * @param prop the MessageProp into which characteristics of the     * parsed token will be stored.     * @throws GSSException if the token is defective or if there is     * a problem reading from the InputStream     */    public WrapToken(Krb5Context context,                     InputStream is, MessageProp prop)        throws GSSException {         // Just parse the MessageToken part first        super(Krb5Token.WRAP_ID, context, is, prop);         // Will need the token bytes again when extracting data        this.is = is;        this.privacy = prop.getPrivacy();        /*          debug("WrapToken Cons: gssHeader.getMechTokenLength=" +          getGSSHeader().getMechTokenLength());          debug("\n                token size="          + getTokenSize());        */         dataSize =            getGSSHeader().getMechTokenLength() - getTokenSize();        // debug("\n                dataSize=" + dataSize);        // debug("\n");    }     /**     * Obtains the application data that was transmitted in this     * WrapToken.     * @return a byte array containing the application data     * @throws GSSException if an error occurs while decrypting any     * cipher text and checking for validity     */    public byte[] getData() throws GSSException {         byte[] temp = new byte[dataSize];        getData(temp, 0);         // Remove the confounder and the padding        byte[] retVal = new byte[dataSize - confounder.length -                                padding.length];        System.arraycopy(temp, 0, retVal, 0, retVal.length);         return retVal;    }     /**     * Obtains the application data that was transmitted in this     * WrapToken, writing it into an application provided output     * array.     * @param dataBuf the output buffer into which the data must be     * written     * @param dataBufOffset the offset at which to write the data     * @return the size of the data written     * @throws GSSException if an error occurs while decrypting any     * cipher text and checking for validity     */    public int getData(byte[] dataBuf, int dataBufOffset)        throws GSSException {         if (readTokenFromInputStream)            getDataFromStream(dataBuf, dataBufOffset);        else            getDataFromBuffer(dataBuf, dataBufOffset);         return (dataSize - confounder.length - padding.length);    }     /**     * Helper routine to obtain the application data transmitted in     * this WrapToken. It is called if the WrapToken was constructed     * with a byte array as input.     * @param dataBuf the output buffer into which the data must be     * written     * @param dataBufOffset the offset at which to write the data     * @throws GSSException if an error occurs while decrypting any     * cipher text and checking for validity     */    private void getDataFromBuffer(byte[] dataBuf, int dataBufOffset)        throws GSSException {         GSSHeader gssHeader = getGSSHeader();        int dataPos = tokenOffset +            gssHeader.getLength() + getTokenSize();         if (dataPos + dataSize > tokenOffset + tokenLen)            throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,                                   "Insufficient data in "                                   + getTokenName(getTokenId()));         // debug("WrapToken cons: data is token is [" +        //      getHexBytes(tokenBytes, tokenOffset, tokenLen) + "]\n");         confounder = new byte[CONFOUNDER_SIZE];         // Do decryption if this token was privacy protected.         if (privacy) {            cipherHelper.decryptData(this,                tokenBytes, dataPos, dataSize, dataBuf, dataBufOffset);            /*            debug("\t\tDecrypted data is [" +                getHexBytes(confounder) + " " +                getHexBytes(dataBuf, dataBufOffset,                        dataSize - CONFOUNDER_SIZE - padding.length) +                getHexBytes(padding) +            "]\n");            */         } else {             // Token data is in cleartext            // debug("\t\tNo encryption was performed by peer.\n");            System.arraycopy(tokenBytes, dataPos,                             confounder, 0, CONFOUNDER_SIZE);            int padSize = tokenBytes[dataPos + dataSize - 1];            if (padSize < 0)                padSize = 0;            if (padSize > 8)                padSize %= 8;             padding = pads[padSize];            // debug("\t\tPadding applied was: " + padSize + "\n");             System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,                             dataBuf, dataBufOffset, dataSize -                             CONFOUNDER_SIZE - padSize);            // byte[] debugbuf = new byte[dataSize - CONFOUNDER_SIZE - padSize];           // System.arraycopy(tokenBytes, dataPos + CONFOUNDER_SIZE,           //                debugbuf, 0, debugbuf.length);           // debug("\t\tData is: " + getHexBytes(debugbuf, debugbuf.length));        }         /*         * Make sure sign and sequence number are not corrupt         */         if (!verifySignAndSeqNumber(confounder,                                    dataBuf, dataBufOffset,                                    dataSize - CONFOUNDER_SIZE                                    - padding.length,                                    padding))            throw new GSSException(GSSException.BAD_MIC, -1,                         "Corrupt checksum or sequence number in Wrap token");    }     /**     * Helper routine to obtain the application data transmitted in     * this WrapToken. It is called if the WrapToken was constructed     * with an Inputstream.     * @param dataBuf the output buffer into which the data must be     * written     * @param dataBufOffset the offset at which to write the data     * @throws GSSException if an error occurs while decrypting any     * cipher text and checking for validity     */    private void getDataFromStream(byte[] dataBuf, int dataBufOffset)        throws GSSException {         GSSHeader gssHeader = getGSSHeader();         // Don't check the token length. Data will be read on demand from        // the InputStream.         // debug("WrapToken cons: data will be read from InputStream.\n");         confounder = new byte[CONFOUNDER_SIZE];         try {             // Do decryption if this token was privacy protected.             if (privacy) {                cipherHelper.decryptData(this, is, dataSize,                    dataBuf, dataBufOffset);                 // debug("\t\tDecrypted data is [" +                //     getHexBytes(confounder) + " " +                //     getHexBytes(dataBuf, dataBufOffset,                // dataSize - CONFOUNDER_SIZE - padding.length) +                //     getHexBytes(padding) +                //     "]\n");             } else {                 // Token data is in cleartext                // debug("\t\tNo encryption was performed by peer.\n");                readFully(is, confounder);                 if (cipherHelper.isArcFour()) {                    padding = pads[1];                    readFully(is, dataBuf, dataBufOffset, dataSize-CONFOUNDER_SIZE-1);                } else {                    // Data is always a multiple of 8 with this GSS Mech                    // Copy all but last block as they are                    int numBlocks = (dataSize - CONFOUNDER_SIZE)/8 - 1;                    int offset = dataBufOffset;                    for (int i = 0; i < numBlocks; i++) {                        readFully(is, dataBuf, offset, 8);                        offset += 8;                    }                     byte[] finalBlock = new byte[8];                    readFully(is, finalBlock);                     int padSize = finalBlock[7];                    padding = pads[padSize];                     // debug("\t\tPadding applied was: " + padSize + "\n");                    System.arraycopy(finalBlock, 0, dataBuf, offset,                                     finalBlock.length - padSize);                }            }        } catch (IOException e) {            throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,                                   getTokenName(getTokenId())                                   + ": " + e.getMessage());        }         /*         * Make sure sign and sequence number are not corrupt         */         if (!verifySignAndSeqNumber(confounder,                                    dataBuf, dataBufOffset,                                    dataSize - CONFOUNDER_SIZE                                    - padding.length,                                    padding))            throw new GSSException(GSSException.BAD_MIC, -1,                         "Corrupt checksum or sequence number in Wrap token");    }      /**     * Helper routine to pick the right padding for a certain length     * of application data. Every application message has some     * padding between 1 and 8 bytes.     * @param len the length of the application data     * @return the padding to be applied     */    private byte[] getPadding(int len) {        int padSize = 0;        // For RC4-HMAC, all padding is rounded up to 1 byte.        // One byte is needed to say that there is 1 byte of padding.        if (cipherHelper.isArcFour()) {            padSize = 1;        } else {            padSize = len % 8;            padSize = 8 - padSize;        }        return pads[padSize];    }     public WrapToken(Krb5Context context, MessageProp prop,                     byte[] dataBytes, int dataOffset, int dataLen)        throws GSSException {         super(Krb5Token.WRAP_ID, context);         confounder = Confounder.bytes(CONFOUNDER_SIZE);         padding = getPadding(dataLen);        dataSize = confounder.length + dataLen + padding.length;        this.dataBytes = dataBytes;        this.dataOffset = dataOffset;        this.dataLen = dataLen;         /*          debug("\nWrapToken cons: data to wrap is [" +          getHexBytes(confounder) + " " +          getHexBytes(dataBytes, dataOffset, dataLen) + " " +          // padding is never null for Wrap          getHexBytes(padding) + "]\n");         */         genSignAndSeqNumber(prop,                            confounder,                            dataBytes, dataOffset, dataLen,                            padding);         /*         * If the application decides to ask for privacy when the context         * did not negotiate for it, do not provide it. The peer might not         * have support for it. The app will realize this with a call to         * pop.getPrivacy() after wrap().         */        if (!context.getConfState())            prop.setPrivacy(false);         privacy = prop.getPrivacy();    }     public void encode(OutputStream os) throws IOException, GSSException {         super.encode(os);         // debug("Writing data: [");        if (!privacy) {             // debug(getHexBytes(confounder, confounder.length));            os.write(confounder);             // debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));            os.write(dataBytes, dataOffset, dataLen);             // debug(" " + getHexBytes(padding, padding.length));            os.write(padding);         } else {             cipherHelper.encryptData(this, confounder,                dataBytes, dataOffset, dataLen, padding, os);        }        // debug("]\n");    }     public byte[] encode() throws IOException, GSSException {        // XXX Fine tune this initial size        ByteArrayOutputStream bos = new ByteArrayOutputStream(dataSize + 50);        encode(bos);        return bos.toByteArray();    }     public int encode(byte[] outToken, int offset)        throws IOException, GSSException  {         // Token header is small        ByteArrayOutputStream bos = new ByteArrayOutputStream();        super.encode(bos);        byte[] header = bos.toByteArray();        System.arraycopy(header, 0, outToken, offset, header.length);        offset += header.length;         // debug("WrapToken.encode: Writing data: [");        if (!privacy) {             // debug(getHexBytes(confounder, confounder.length));            System.arraycopy(confounder, 0, outToken, offset,                             confounder.length);            offset += confounder.length;             // debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));            System.arraycopy(dataBytes, dataOffset, outToken, offset,                             dataLen);            offset += dataLen;             // debug(" " + getHexBytes(padding, padding.length));            System.arraycopy(padding, 0, outToken, offset, padding.length);         } else {             cipherHelper.encryptData(this, confounder, dataBytes,                dataOffset, dataLen, padding, outToken, offset); 

Lines 1–500 of 532.