Package com.lixia.rdp

Source Code of com.lixia.rdp.Secure

/* Secure.java
* Component: ProperJavaRDP
*
* Revision: $Revision: 1.1.1.1 $
* Author: $Author: suvarov $
* Date: $Date: 2007/03/08 00:26:35 $
*
* Copyright (c) 2005 Propero Limited
*
* Purpose: Secure layer of communication
*/
package com.lixia.rdp;

import com.alssoftrd.utils.RDPConnection;
import java.io.*;
import java.util.*;
import java.net.*;
import java.math.*;

import com.lixia.rdp.crypto.*;
import com.lixia.rdp.Package.RdpPackage;
import com.lixia.rdp.rdp5.VChannels;

public class Secure {

    boolean readCert = false;
//    static Logger logger = Logger.getLogger("Secure");
    private final Licence licence = new Licence(this);
    /* constants for the secure layer */
    public static final int SEC_ENCRYPT = 0x0008;
    public static final int SEC_INFO_PKT = 0x0040;

    static final int SEC_RANDOM_SIZE = 32;
    static final int SEC_MODULUS_SIZE = 64;
    static final int SEC_MAX_MODULUS_SIZE = 256;
    static final int SEC_PADDING_SIZE = 8;
    private static final int SEC_EXPONENT_SIZE = 4;

    private static final int SEC_CLIENT_RANDOM = 0x0001;
    static final int SEC_LICENCE_NEG = 0x0080;

    private static final int SEC_TAG_SRV_INFO = 0x0c01;
    private static final int SEC_TAG_SRV_CRYPT = 0x0c02;
    private static final int SEC_TAG_SRV_3 = 0x0c03;
    private static final int SEC_TAG_SRV_CHANNELS = 0x0c03;

    private static final int SEC_TAG_CLI_INFO = 0xc001;
    private static final int SEC_TAG_CLI_CRYPT = 0xc002;
    private static final int SEC_TAG_CLI_CHANNELS = 0xc003;
    private static final int SEC_TAG_CLI_4 = 0xc004;

    private static final int SEC_TAG_PUBKEY = 0x0006;
    private static final int SEC_TAG_KEYSIG = 0x0008;

    private static final int SEC_RSA_MAGIC = 0x31415352; /* RSA1 */

    private MCS McsLayer = null;
    //   private String hostname=null;
    // private String username=null;
    boolean licenceIssued = false;
    private RC4 rc4_enc = null;
    private RC4 rc4_dec = null;
    private RC4 rc4_update = null;
    private BlockMessageDigest sha1 = null;
    private BlockMessageDigest md5 = null;
    private int keylength = 0;
    private int enc_count = 0;
    private int dec_count = 0;

    private byte[] sec_sign_key = null;

    private byte[] sec_decrypt_key = null;

    private byte[] sec_encrypt_key = null;

    private byte[] sec_decrypt_update_key = null;

    private byte[] sec_encrypt_update_key = null;
    private byte[] sec_crypted_random = null;

    private byte[] exponent = null;
    private byte[] modulus = null;
    private byte[] server_random = null;
    private byte[] client_random = new byte[SEC_RANDOM_SIZE];

    private static final byte[] pad_54 = {
        54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
        54, 54, 54,
        54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
        54, 54, 54
    };

    private static final byte[] pad_92 = {
        92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
        92, 92, 92, 92, 92, 92, 92,
        92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
        92, 92, 92, 92, 92, 92, 92
    };

    private VChannels channels;

    public int server_public_key_len = SEC_MODULUS_SIZE;

    /**
     * Initialise Secure layer of communications
     *
     * @param channels Virtual channels for this connection
     */
    public Secure(VChannels channels) {
        this.channels = channels;
        McsLayer = new MCS(channels);
        RDPConnection.conf.mcs = McsLayer;
        rc4_dec = new RC4();
        rc4_enc = new RC4();
        rc4_update = new RC4();
        sha1 = new SHA1();
        md5 = new MD5();
        sec_sign_key = new byte[16]; // changed from 8 - rdesktop 1.2.0
        sec_decrypt_key = new byte[16];
        sec_encrypt_key = new byte[16];
        sec_decrypt_update_key = new byte[16]; // changed from 8 - rdesktop 1.2.0
        sec_encrypt_update_key = new byte[16]; // changed from 8 - rdesktop 1.2.0
        sec_crypted_random = new byte[64];

    }

    /**
     * Connect to server
     *
     * @param host Address of server to connect to
     * @param port Port to connect to
     * @throws UnknownHostException
     * @throws IOException
     * @throws RdesktopException
     * @throws SocketException
     * @throws CryptoException
     * @throws OrderException
     */
    public void connect(InetAddress host, int port) throws UnknownHostException, IOException, RdesktopException, SocketException, CryptoException, OrderException {
        if ("".equals(RDPConnection.conf.hostname)) {
            InetAddress localhost = InetAddress.getLocalHost();
            String name = localhost.getHostName();
            StringTokenizer tok = new StringTokenizer(name, ".");
            RDPConnection.conf.hostname = tok.nextToken();
            RDPConnection.conf.hostname.trim();
        }

        RdpPackage mcs_data = this.sendMcsData();
        McsLayer.connect(host, port, mcs_data);

        this.processMcsData(mcs_data);

        if (RDPConnection.conf.encryption) {
            this.establishKey();
        }
    }

    /**
     * Connect to server on default port
     *
     * @param host Server to connect to
     * @throws IOException
     * @throws RdesktopException
     * @throws OrderException
     * @throws CryptoException
     */
    public void connect(InetAddress host) throws IOException, RdesktopException, OrderException, CryptoException {
        this.connect(host, RDPConnection.conf.port);
    }

    /**
     * Close connection
     */
    public void disconnect() {
        McsLayer.disconnect();
    }

    /**
     * Construct MCS data, including channel, encryption and display options
     *
     * @return Packet populated with MCS data
     */
    public RdpPackage sendMcsData() {
//        logger.debug("Secure.sendMcsData");

        RdpPackage buffer = new RdpPackage(512);
        int hostlen = 2 * (RDPConnection.conf.hostname == null ? 0 : RDPConnection.conf.hostname.length());
        if (hostlen > 30) {
            hostlen = 30;
        }

        int length = 158;
        if (RDPConnection.conf.use_rdp5) {
            length += 76 + 12 + 4;
        }

        if (RDPConnection.conf.use_rdp5 && (channels.num_channels() > 0)) {
            length += channels.num_channels() * 12 + 8;
        }

        buffer.setBigEndian16(5)/* unknown */

        buffer.setBigEndian16(0x14);
        buffer.set8(0x7c);
        buffer.setBigEndian16(1);

        buffer.setBigEndian16(length | 0x8000)// remaining length

        buffer.setBigEndian16(8)// length?
        buffer.setBigEndian16(16);
        buffer.set8(0);
        buffer.setLittleEndian16(0xc001);
        buffer.set8(0);

        buffer.setLittleEndian32(0x61637544)// "Duca" ?!
        buffer.setBigEndian16(length - 14 | 0x8000)// remaining length

        // Client information
        buffer.setLittleEndian16(SEC_TAG_CLI_INFO);
        buffer.setLittleEndian16(RDPConnection.conf.use_rdp5 ? 212 : 136)// length
        buffer.setLittleEndian16(RDPConnection.conf.use_rdp5 ? 4 : 1);
        buffer.setLittleEndian16(8);
        buffer.setLittleEndian16(RDPConnection.conf.width);
        buffer.setLittleEndian16(RDPConnection.conf.height);
        buffer.setLittleEndian16(0xca01);
        buffer.setLittleEndian16(0xaa03);
        buffer.setLittleEndian32(RDPConnection.conf.keylayout);
        buffer.setLittleEndian32(RDPConnection.conf.use_rdp5 ? 2600 : 419); // or 0ece  // client build? we are 2600 compatible :-)

        /* Unicode name of client, padded to 32 bytes */
        buffer.outUnicodeString(RDPConnection.conf.hostname.toUpperCase(), hostlen);
        buffer.incrementPosition(30 - hostlen);

        buffer.setLittleEndian32(4);
        buffer.setLittleEndian32(0);
        buffer.setLittleEndian32(12);
        buffer.incrementPosition(64)/* reserved? 4 + 12 doublewords */

        buffer.setLittleEndian16(0xca01); // out_uint16_le(s, 0xca01);
        buffer.setLittleEndian16(RDPConnection.conf.use_rdp5 ? 1 : 0);

        if (RDPConnection.conf.use_rdp5) {
            buffer.setLittleEndian32(0); // out_uint32(s, 0);
            buffer.set8(RDPConnection.conf.server_bpp); // out_uint8(s, g_server_bpp);
            buffer.setLittleEndian16(0x0700); // out_uint16_le(s, 0x0700);
            buffer.set8(0); // out_uint8(s, 0);
            buffer.setLittleEndian32(1); // out_uint32_le(s, 1);

            buffer.incrementPosition(64);

            buffer.setLittleEndian16(SEC_TAG_CLI_4); // out_uint16_le(s, SEC_TAG_CLI_4);
            buffer.setLittleEndian16(12); // out_uint16_le(s, 12);
            buffer.setLittleEndian32(RDPConnection.conf.console_session ? 0xb : 0xd); // out_uint32_le(s, g_console_session ? 0xb : 9);
            buffer.setLittleEndian32(0); // out_uint32(s, 0);
        }

        // Client encryption settings //
        buffer.setLittleEndian16(SEC_TAG_CLI_CRYPT);
        buffer.setLittleEndian16(RDPConnection.conf.use_rdp5 ? 12 : 8)// length

        //if(RDPConnection.conf.use_rdp5) buffer.setLittleEndian32(RDPConnection.conf.encryption ? 0x1b : 0);  // 128-bit encryption supported
        //else
        buffer.setLittleEndian32(RDPConnection.conf.encryption ? (RDPConnection.conf.console_session ? 0xb : 0x3) : 0);

        if (RDPConnection.conf.use_rdp5) {
            buffer.setLittleEndian32(0); // unknown
        }
        if (RDPConnection.conf.use_rdp5 && (channels.num_channels() > 0)) {
//            logger.debug(("num_channels is " + channels.num_channels()));
            buffer.setLittleEndian16(SEC_TAG_CLI_CHANNELS); // out_uint16_le(s, SEC_TAG_CLI_CHANNELS);
            buffer.setLittleEndian16(channels.num_channels() * 12 + 8); // out_uint16_le(s, g_num_channels * 12 + 8);  // length
            buffer.setLittleEndian32(channels.num_channels()); // out_uint32_le(s, g_num_channels);  // number of virtual channels
            for (int i = 0; i < channels.num_channels(); i++) {
//                logger.debug(("Requesting channel " + channels.channel(i).name()));
                buffer.out_uint8p(channels.channel(i).name(), 8); // out_uint8a(s, g_channels[i].name, 8);
                buffer.setBigEndian32(channels.channel(i).flags()); // out_uint32_be(s, g_channels[i].flags);
            }
        }

        buffer.markEnd();
        return buffer;
    }

    /**
     * Handle MCS info from server (server info, encryption info and channel
     * information)
     *
     * @param mcs_data Data received from server
     * @throws com.lixia.rdp.RdesktopException
     * @throws com.lixia.rdp.crypto.CryptoException
     */
    public void processMcsData(RdpPackage mcs_data) throws RdesktopException, CryptoException {
//        logger.debug("Secure.processMcsData");
        int tag, len, length, nexttag;

        mcs_data.incrementPosition(21); // header (T.124 stuff, probably)
        len = mcs_data.get8();

        if ((len & 0x00000080) != 0) {
            len = mcs_data.get8();
        }

        while (mcs_data.getPosition() < mcs_data.getEnd()) {
            tag = mcs_data.getLittleEndian16();
            length = mcs_data.getLittleEndian16();

            if (length <= 4) {
                return;
            }

            nexttag = mcs_data.getPosition() + length - 4;

            switch (tag) {
                case (Secure.SEC_TAG_SRV_INFO):
                    processSrvInfo(mcs_data);
                    break;
                case (Secure.SEC_TAG_SRV_CRYPT):
                    this.processCryptInfo(mcs_data);
                    break;
                case (Secure.SEC_TAG_SRV_CHANNELS):
                    /* FIXME: We should parse this information and
                     use it to map RDP5 channels to MCS
                     channels */
                    break;
                default:
                    throw new RdesktopException("Not implemented! Tag:" + tag + "not recognized!");
            }
            mcs_data.setPosition(nexttag);
        }
    }

    /**
     * Read server info from packet, specifically the RDP version of the server
     *
     * @param mcs_data Packet to read
     */
    private void processSrvInfo(RdpPackage mcs_data) {
        RDPConnection.conf.server_rdp_version = mcs_data.getLittleEndian16(); // in_uint16_le(s, g_server_rdp_version);
//        logger.debug(("Server RDP version is " + RDPConnection.conf.server_rdp_version));
        if (1 == RDPConnection.conf.server_rdp_version) {
            RDPConnection.conf.use_rdp5 = false;
        }
    }

    public void establishKey() throws RdesktopException, IOException, CryptoException {
        int length = server_public_key_len + SEC_PADDING_SIZE;
        int flags = SEC_CLIENT_RANDOM;
        RdpPackage buffer = this.init(flags, length + 4);

        buffer.setLittleEndian32(length);

        buffer.copyFromByteArray(this.sec_crypted_random, 0, buffer.getPosition(), server_public_key_len);
        buffer.incrementPosition(server_public_key_len);
        buffer.incrementPosition(SEC_PADDING_SIZE);
        buffer.markEnd();
        this.send(buffer, flags);

    }

    public void processCryptInfo(RdpPackage data) throws RdesktopException, CryptoException {
        int rc4_key_size;

        rc4_key_size = this.parseCryptInfo(data);
        if (rc4_key_size == 0) {
            return;
        }
        //this.client_random = this.generateRandom(SEC_RANDOM_SIZE);
//        logger.debug("readCert = " + readCert);
        if (readCert) {      /* Which means we should use
             RDP5-style encryption */
    // *** reverse the client random
            //this.reverse(this.client_random);
            // *** load the server public key into the stored data for encryption
    /* this.exponent = this.server_public_key.getPublicExponent().toByteArray();
             this.modulus = this.server_public_key.getModulus().toByteArray();
   
             System.out.println("Exponent: " + server_public_key.getPublicExponent());
             System.out.println("Modulus: " + server_public_key.getModulus());
             */
    // *** perform encryption
            //this.sec_crypted_random = RSA_public_encrypt(this.client_random, this.server_public_key);
            //this.RSAEncrypt(SEC_RANDOM_SIZE);
            //this.RSAEncrypt(SEC_RANDOM_SIZE);
            // *** reverse the random data back
            //this.reverse(this.sec_crypted_random);
        } else {
            this.generateRandom();
            this.RSAEncrypt(SEC_RANDOM_SIZE);
        }
        this.generate_keys(rc4_key_size);
    }

    /**
     * Intialise a packet at the Secure layer
     *
     * @param flags Encryption flags
     * @param length Length of packet
     * @return Intialised packet
     * @throws RdesktopException
     */
    public RdpPackage init(int flags, int length) throws RdesktopException {
        int headerlength;
        RdpPackage buffer;

        if (!this.licenceIssued) {
            headerlength = ((flags & SEC_ENCRYPT) != 0) ? 12 : 4;
        } else {
            headerlength = ((flags & SEC_ENCRYPT) != 0) ? 12 : 0;
        }

        buffer = McsLayer.init(length + headerlength);
        buffer.pushLayer(RdpPackage.SECURE_HEADER, headerlength);
        //buffer.setHeader(RdpPackage.SECURE_HEADER);
        //buffer.incrementPosition(headerlength);
        //buffer.setStart(buffer.getPosition());
        return buffer;
    }

    /**
     * Send secure data on the global channel
     *
     * @param sec_data Data to send
     * @param flags Encryption flags
     * @throws RdesktopException
     * @throws IOException
     * @throws CryptoException
     */
    public void send(RdpPackage sec_data, int flags) throws RdesktopException, IOException, CryptoException {
        send_to_channel(sec_data, flags, MCS.MCS_GLOBAL_CHANNEL);
    }

    /**
     * Prepare data as a Secure PDU and pass down to the MCS layer
     *
     * @param sec_data Data to send
     * @param flags Encryption flags
     * @param channel Channel over which to send data
     * @throws RdesktopException
     * @throws IOException
     * @throws CryptoException
     */
    public void send_to_channel(RdpPackage sec_data, int flags, int channel) throws RdesktopException, IOException, CryptoException {
        int datalength;
        byte[] signature;
        byte[] data;
        byte[] buffer;

        sec_data.setPosition(sec_data.getHeader(RdpPackage.SECURE_HEADER));

        if (this.licenceIssued == false || (flags & SEC_ENCRYPT) != 0) {
            sec_data.setLittleEndian32(flags);
        }
        if ((flags & SEC_ENCRYPT) != 0) {
            flags &= ~SEC_ENCRYPT;
            datalength = sec_data.getEnd() - sec_data.getPosition() - 8;
            data = new byte[datalength];
            sec_data.copyToByteArray(data, 0, sec_data.getPosition() + 8, datalength);
            signature = this.sign(this.sec_sign_key, 8, this.keylength, data, datalength);

            buffer = this.encrypt(data, datalength);

            sec_data.copyFromByteArray(signature, 0, sec_data.getPosition(), 8);
            sec_data.copyFromByteArray(buffer, 0, sec_data.getPosition() + 8, datalength);
        }
        //McsLayer.send(sec_data);
        McsLayer.send_to_channel(sec_data, channel);
    }

    /**
     * Generate MD5 signature
     *
     * @param session_key Key with which to sign data
     * @param length Length of signature
     * @param keylen Length of key
     * @param data Data to sign
     * @param datalength Length of data to sign
     * @return Signature for data
     * @throws CryptoException
     */
    public byte[] sign(byte[] session_key, int length, int keylen, byte[] data, int datalength) throws CryptoException {
        byte[] shasig = new byte[20];
        byte[] md5sig = new byte[16];
        byte[] lenhdr = new byte[4];
        byte[] signature = new byte[length];

        this.setLittleEndian32(lenhdr, datalength);

        sha1.engineReset();
        sha1.engineUpdate(session_key, 0, keylen/*length*/);
        sha1.engineUpdate(pad_54, 0, 40);
        sha1.engineUpdate(lenhdr, 0, 4);
        sha1.engineUpdate(data, 0, datalength);
        shasig = sha1.engineDigest();
        sha1.engineReset();

        md5.engineReset();
        md5.engineUpdate(session_key, 0, keylen/*length*/);
        md5.engineUpdate(pad_92, 0, 48);
        md5.engineUpdate(shasig, 0, 20);
        md5sig = md5.engineDigest();
        md5.engineReset();

        System.arraycopy(md5sig, 0, signature, 0, length);
        return signature;
    }

    /**
     * Encrypt specified number of bytes from provided data using RC4 algorithm
     *
     * @param data Data to encrypt
     * @param length Number of bytes to encrypt (from start of array)
     * @return Encrypted data
     * @throws CryptoException
     */
    public byte[] encrypt(byte[] data, int length) throws CryptoException {
        byte[] buffer;
        if (this.enc_count == 4096) {
            sec_encrypt_key = this.update(this.sec_encrypt_key, this.sec_encrypt_update_key);
            byte[] key = new byte[this.keylength];
            System.arraycopy(this.sec_encrypt_key, 0, key, 0, this.keylength);
            this.rc4_enc.engineInitEncrypt(key);
//    logger.debug("Packet enc_count="+enc_count);
            this.enc_count = 0;
        }
        //this.rc4.engineInitEncrypt(this.rc4_encrypt_key);
        buffer = this.rc4_enc.crypt(data, 0, length);
        this.enc_count++;
        return buffer;
    }

    /**
     * Encrypt provided data using the RC4 algorithm
     *
     * @param data Data to encrypt
     * @return Encrypted data
     * @throws CryptoException
     */
    public byte[] encrypt(byte[] data) throws CryptoException {
        byte[] buffer;
        if (this.enc_count == 4096) {
            sec_encrypt_key = this.update(this.sec_encrypt_key, this.sec_encrypt_update_key);
            byte[] key = new byte[this.keylength];
            System.arraycopy(this.sec_encrypt_key, 0, key, 0, this.keylength);
            this.rc4_enc.engineInitEncrypt(key);
            //  logger.debug("Packet enc_count="+enc_count);
            this.enc_count = 0;
        }
//  this.rc4.engineInitEncrypt(this.rc4_encrypt_key);
        buffer = this.rc4_enc.crypt(data);
        this.enc_count++;
        return buffer;
    }

    /**
     * Decrypt specified number of bytes from provided data using RC4 algorithm
     *
     * @param data Data to decrypt
     * @param length Number of bytes to decrypt (from start of array)
     * @return Decrypted data
     * @throws CryptoException
     */
    public byte[] decrypt(byte[] data, int length) throws CryptoException {
        byte[] buffer;
        if (this.dec_count == 4096) {
            sec_decrypt_key = this.update(this.sec_decrypt_key, this.sec_decrypt_update_key);
            byte[] key = new byte[this.keylength];
            System.arraycopy(this.sec_decrypt_key, 0, key, 0, this.keylength);
            this.rc4_dec.engineInitDecrypt(key);
//    logger.debug("Packet dec_count="+dec_count);
            this.dec_count = 0;
        }
        //this.rc4.engineInitDecrypt(this.rc4_decrypt_key);
        buffer = this.rc4_dec.crypt(data, 0, length);
        this.dec_count++;
        return buffer;
    }

    /**
     * Decrypt provided data using RC4 algorithm
     *
     * @param data Data to decrypt
     * @return Decrypted data
     * @throws CryptoException
     */
    public byte[] decrypt(byte[] data) throws CryptoException {
        byte[] buffer;
        if (this.dec_count == 4096) {
            sec_decrypt_key = this.update(this.sec_decrypt_key, this.sec_decrypt_update_key);
            byte[] key = new byte[this.keylength];
            System.arraycopy(this.sec_decrypt_key, 0, key, 0, this.keylength);
            this.rc4_dec.engineInitDecrypt(key);
//    logger.debug("Packet dec_count="+dec_count);
            this.dec_count = 0;
        }
        //this.rc4.engineInitDecrypt(this.rc4_decrypt_key);

        buffer = this.rc4_dec.crypt(data);
        this.dec_count++;
        return buffer;
    }

    /**
     * Read encryption information from a Secure layer PDU, obtaining and
     * storing level of encryption and any keys received
     *
     * @param data Packet to read encryption information from
     * @return Size of RC4 key
     * @throws RdesktopException
     */
    public int parseCryptInfo(RdpPackage data) throws RdesktopException {
//        logger.debug("Secure.parseCryptInfo");
        int encryption_level, random_length, RSA_info_length;
        int tag, length;
        int next_tag, end;
        int rc4_key_size;

        rc4_key_size = data.getLittleEndian32(); // 1 = 40-Bit 2 = 128 Bit
        encryption_level = data.getLittleEndian32(); // 1 = low, 2 = medium, 3 = high
        if (encryption_level == 0) { // no encryption
            return 0;
        }
        random_length = data.getLittleEndian32();
        RSA_info_length = data.getLittleEndian32();

        if (random_length != SEC_RANDOM_SIZE) {
            throw new RdesktopException("Wrong Size of Random! Got" + random_length + "expected" + SEC_RANDOM_SIZE);
        }
        this.server_random = new byte[random_length];
        data.copyToByteArray(this.server_random, 0, data.getPosition(), random_length);
        data.incrementPosition(random_length);

        end = data.getPosition() + RSA_info_length;

        if (end > data.getEnd()) {
//            logger.debug("Reached end of crypt info prematurely ");
            return 0;
        }

        //data.incrementPosition(12); // unknown bytes
        int flags = data.getLittleEndian32(); // in_uint32_le(s, flags);  // 1 = RDP4-style, 0x80000002 = X.509
//        logger.debug("Flags = 0x" + Integer.toHexString(flags));
        if ((flags & 1) != 0) {
//            logger.debug(("We're going for the RDP4-style encryption"));
            data.incrementPosition(8); //in_uint8s(s, 8);  // unknown

            while (data.getPosition() < data.getEnd()) {
                tag = data.getLittleEndian16();
                length = data.getLittleEndian16();

                next_tag = data.getPosition() + length;

                switch (tag) {

                    case (Secure.SEC_TAG_PUBKEY):

                        if (!parsePublicKey(data)) {
                            return 0;
                        }

                        break;
                    case (Secure.SEC_TAG_KEYSIG):
                        // Microsoft issued a key but we don't care
                        break;

                    default:
                        throw new RdesktopException("Unimplemented decrypt tag " + tag);
                }
                data.setPosition(next_tag);
            }

            if (data.getPosition() == data.getEnd()) {
                return rc4_key_size;
            } else {
//                logger.warn("End not reached!");
                return 0;
            }

        } else {
            //data.incrementPosition(4); // number of certificates
            int num_certs = data.getLittleEndian32();

            int cacert_len = data.getLittleEndian32();
            data.incrementPosition(cacert_len);
            int cert_len = data.getLittleEndian32();
            data.incrementPosition(cert_len);

            readCert = true;

            return rc4_key_size;
        }

    }
    /*
     public X509Certificate readCert(int length, RdpPackage data){   
     byte[] buf = new byte[length];
     
     data.copyToByteArray(buf,0,data.getPosition(),buf.length);
     data.incrementPosition(length); 
     
     for(int i = 0; i < buf.length; i++){
     buf[i] = (byte) (buf[i] & 0xFF);
     }
     
     ByteArrayInputStream bIn = new ByteArrayInputStream(buf);
     X509Certificate cert = null;
     CertificateFactory cf = null;
     try {
     cf = CertificateFactory.getInstance("X.509");
     cert = (X509Certificate)cf.generateCertificate(bIn);
     } catch (CertificateException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
     }
     
     bIn.reset();
     return cert;
     }
     */

    public void generateRandom() {
        /*try{
         SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
         random.nextBytes(this.client_random);
         } catch(NoSuchAlgorithmException e){logger.warn("No Such Random Algorithm");}*/
    }

    public void RSAEncrypt(int length) throws RdesktopException {
        byte[] inr = new byte[length];
        //int outlength = 0;
        BigInteger mod;
        BigInteger exp;
        BigInteger x;

        this.reverse(this.exponent);
        this.reverse(this.modulus);
        System.arraycopy(this.client_random, 0, inr, 0, length);
        this.reverse(inr);

        if ((this.modulus[0] & 0x80) != 0) {
            byte[] temp = new byte[this.modulus.length + 1];
            System.arraycopy(this.modulus, 0, temp, 1, this.modulus.length);
            temp[0] = 0;
            mod = new BigInteger(temp);
        } else {
            mod = new BigInteger(this.modulus);
        }
        if ((this.exponent[0] & 0x80) != 0) {
            byte[] temp = new byte[this.exponent.length + 1];
            System.arraycopy(this.exponent, 0, temp, 1, this.exponent.length);
            temp[0] = 0;
            exp = new BigInteger(temp);
        } else {
            exp = new BigInteger(this.exponent);
        }
        if ((inr[0] & 0x80) != 0) {
            byte[] temp = new byte[inr.length + 1];
            System.arraycopy(inr, 0, temp, 1, inr.length);
            temp[0] = 0;
            x = new BigInteger(temp);
        } else {
            x = new BigInteger(inr);
        }

        BigInteger y = x.modPow(exp, mod);
        this.sec_crypted_random = y.toByteArray();

        if ((this.sec_crypted_random[0] & 0x80) != 0) {
            throw new RdesktopException("Wrong Sign! Expected positive Integer!");
        }

        if (this.sec_crypted_random.length > this.server_public_key_len) {
//            logger.warn("sec_crypted_random too big!"); /* FIXME */

        }
        this.reverse(this.sec_crypted_random);

        byte[] temp = new byte[server_public_key_len];

        if (this.sec_crypted_random.length < server_public_key_len) {
            System.arraycopy(this.sec_crypted_random, 0, temp, 0, this.sec_crypted_random.length);
            for (int i = this.sec_crypted_random.length; i < temp.length; i++) {
                temp[i] = 0;
            }
            this.sec_crypted_random = temp;

        }

    }

    /**
     * Read in a public key from a provided Secure layer PDU, and store in
     * this.exponent and this.modulus
     *
     * @param data Secure layer PDU containing key data
     * @return True if key successfully read
     * @throws RdesktopException
     */
    public boolean parsePublicKey(RdpPackage data) throws RdesktopException {
        int magic, modulus_length;
        magic = data.getLittleEndian32();
        if (magic != SEC_RSA_MAGIC) {
            throw new RdesktopException("Wrong magic! Expected" + SEC_RSA_MAGIC + "got:" + magic);
        }

        modulus_length = data.getLittleEndian32() - SEC_PADDING_SIZE;

        if ((modulus_length < SEC_MODULUS_SIZE) || (modulus_length > SEC_MAX_MODULUS_SIZE)) {
            throw new RdesktopException("Wrong modulus size! Expected" + SEC_MODULUS_SIZE + "+" + SEC_PADDING_SIZE + "got:" + modulus_length);
        }

        data.incrementPosition(8); //unknown modulus bits
        this.exponent = new byte[SEC_EXPONENT_SIZE];
        data.copyToByteArray(this.exponent, 0, data.getPosition(), SEC_EXPONENT_SIZE);
        data.incrementPosition(SEC_EXPONENT_SIZE);
        this.modulus = new byte[modulus_length];
        data.copyToByteArray(this.modulus, 0, data.getPosition(), modulus_length);
        data.incrementPosition(modulus_length);
        data.incrementPosition(SEC_PADDING_SIZE);
        this.server_public_key_len = modulus_length;

        if (data.getPosition() <= data.getEnd()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Reverse the values in the provided array
     *
     * @param data Array as passed reversed on return
     */
    public void reverse(byte[] data) {
        int i, j;
        byte temp;

        for (i = 0, j = data.length - 1; i < j; i++, j--) {
            temp = data[i];
            data[i] = data[j];
            data[j] = temp;
        }
    }

    public void reverse(byte[] data, int length) {
        int i, j;
        byte temp;

        for (i = 0, j = length - 1; i < j; i++, j--) {
            temp = data[i];
            data[i] = data[j];
            data[j] = temp;
        }
    }

    public byte[] hash48(byte[] in, byte[] salt1, byte[] salt2, int salt) throws CryptoException {
        byte[] shasig = new byte[20];
        byte[] pad = new byte[4];
        byte[] out = new byte[48];
        int i;

        for (i = 0; i < 3; i++) {
            for (int j = 0; j <= i; j++) {
                pad[j] = (byte) (salt + i);
            }
            sha1.engineUpdate(pad, 0, i + 1);
            sha1.engineUpdate(in, 0, 48);
            sha1.engineUpdate(salt1, 0, 32);
            sha1.engineUpdate(salt2, 0, 32);
            shasig = sha1.engineDigest();
            sha1.engineReset();

            md5.engineUpdate(in, 0, 48);
            md5.engineUpdate(shasig, 0, 20);
            System.arraycopy(md5.engineDigest(), 0, out, i * 16, 16);
        }
        return out;
    }

    public byte[] hash16(byte[] in, byte[] salt1, byte[] salt2, int in_position) throws CryptoException {
        md5.engineUpdate(in, in_position, 16);
        md5.engineUpdate(salt1, 0, 32);
        md5.engineUpdate(salt2, 0, 32);
        return md5.engineDigest();
    }

    /**
     * Generate a 40-bit key and store in the parameter key.
     *
     * @param key
     */
    public void make40bit(byte[] key) {
        key[0] = (byte) 0xd1;
        key[1] = (byte) 0x26;
        key[2] = (byte) 0x9e;
    }

    /**
     *
     * @param key
     * @param update_key
     * @return
     * @throws CryptoException
     */
    public byte[] update(byte[] key, byte[] update_key) throws CryptoException {
        byte[] shasig = new byte[20];
        byte[] update = new byte[this.keylength]; // changed from 8 - rdesktop 1.2.0
        byte[] thekey = new byte[key.length];

        sha1.engineReset();
        sha1.engineUpdate(update_key, 0, keylength);
        sha1.engineUpdate(pad_54, 0, 40);
        sha1.engineUpdate(key, 0, keylength); // changed from 8 - rdesktop 1.2.0
        shasig = sha1.engineDigest();
        sha1.engineReset();

        md5.engineReset();
        md5.engineUpdate(update_key, 0, keylength); // changed from 8 - rdesktop 1.2.0
        md5.engineUpdate(pad_92, 0, 48);
        md5.engineUpdate(shasig, 0, 20);
        thekey = md5.engineDigest();
        md5.engineReset();

        System.arraycopy(thekey, 0, update, 0, this.keylength);
        rc4_update.engineInitDecrypt(update);
        // added
        thekey = rc4_update.crypt(thekey, 0, this.keylength);

        if (this.keylength == 8) {
            this.make40bit(thekey);
        }
        return thekey;
    }

    /**
     * Write a 32-bit integer value to an array of bytes, length 4
     *
     * @param data Modified by method to be a 4-byte array representing the
     * parameter value
     * @param value Integer value to return as a little-endian 32-bit value
     */
    public void setLittleEndian32(byte[] data, int value) {

        data[3] = (byte) ((value >>> 24) & 0xff);
        data[2] = (byte) ((value >>> 16) & 0xff);
        data[1] = (byte) ((value >>> 8) & 0xff);
        data[0] = (byte) (value & 0xff);
    }

    /**
     * Receive a Secure layer PDU from the MCS layer
     *
     * @return Packet representing received Secure PDU
     * @throws RdesktopException
     * @throws IOException
     * @throws CryptoException
     * @throws OrderException
     */
    public RdpPackage receive() throws RdesktopException, IOException, CryptoException, OrderException {
        int sec_flags;
        RdpPackage buffer;
        while (true) {
            int[] channel = new int[1];
            buffer = McsLayer.receive(channel);
            if (buffer == null) {
                return null;
            }
            buffer.setHeader(RdpPackage.SECURE_HEADER);
            if (RDPConnection.conf.encryption || (!this.licenceIssued)) {

                sec_flags = buffer.getLittleEndian32();

                if ((sec_flags & SEC_LICENCE_NEG) != 0) {
                    licence.process(buffer);
                    continue;
                }
                if ((sec_flags & SEC_ENCRYPT) != 0) {
                    buffer.incrementPosition(8); //signature
                    byte[] data = new byte[buffer.size() - buffer.getPosition()];
                    buffer.copyToByteArray(data, 0, buffer.getPosition(), data.length);
                    byte[] packet = this.decrypt(data);

                    buffer.copyFromByteArray(packet, 0, buffer.getPosition(), packet.length);

                    //buffer.setStart(buffer.getPosition());
                    //return buffer;
                }
            }

            if (channel[0] != MCS.MCS_GLOBAL_CHANNEL) {
                channels.channel_process(buffer, channel[0]);
                continue;
            }

            buffer.setStart(buffer.getPosition());
            return buffer;
        }
    }

    /**
     * Generate encryption keys of applicable size for connection
     *
     * @param rc4_key_size Size of keys to generate (1 if 40-bit encryption,
     * otherwise 128-bit)
     * @throws CryptoException
     */
    public void generate_keys(int rc4_key_size) throws CryptoException {
        byte[] session_key = new byte[48];
        byte[] temp_hash = new byte[48];
        byte[] input = new byte[48];

        System.arraycopy(this.client_random, 0, input, 0, 24);
        System.arraycopy(this.server_random, 0, input, 24, 24);

        temp_hash = this.hash48(input, this.client_random, this.server_random, 65);
        session_key = this.hash48(temp_hash, this.client_random, this.server_random, 88);

        System.arraycopy(session_key, 0, this.sec_sign_key, 0, 16); // changed from 8 - rdesktop 1.2.0

        this.sec_decrypt_key = this.hash16(session_key, this.client_random, this.server_random, 16);
        this.sec_encrypt_key = this.hash16(session_key, this.client_random, this.server_random, 32);

        if (rc4_key_size == 1) {
//            logger.info("40 Bit Encryption enabled");
            this.make40bit(this.sec_sign_key);
            this.make40bit(this.sec_decrypt_key);
            this.make40bit(this.sec_encrypt_key);
            this.keylength = 8;
        } else {
//            logger.info("128 Bit Encryption enabled");
            this.keylength = 16;
        }

        System.arraycopy(this.sec_decrypt_key, 0, this.sec_decrypt_update_key, 0, 16); // changed from 8 - rdesktop 1.2.0
        System.arraycopy(this.sec_encrypt_key, 0, this.sec_encrypt_update_key, 0, 16); // changed from 8 - rdesktop 1.2.0

        byte[] key = new byte[this.keylength];
        System.arraycopy(this.sec_encrypt_key, 0, key, 0, this.keylength);
        rc4_enc.engineInitEncrypt(key);
        System.arraycopy(this.sec_decrypt_key, 0, key, 0, this.keylength);
        rc4_dec.engineInitDecrypt(key);
    }

    /**
     * @return MCS user ID
     */
    public int getUserID() {
        return McsLayer.getUserID();
    }
}
TOP

Related Classes of com.lixia.rdp.Secure

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.