ntlm.java

来自「这是linux下ssl vpn的实现程序」· Java 代码 · 共 496 行 · 第 1/2 页

JAVA
496
字号
package com.maverick.http;

import com.maverick.crypto.engines.DESEngine;
import java.io.IOException;
import com.maverick.crypto.encoders.Base64;

class NTLM {

    /** Character encoding */
    public static final String DEFAULT_CHARSET = "ASCII";

    /** The current response */
    private byte[] currentResponse;

    /** The current position */
    private int currentPosition = 0;

    /** The character set to use for encoding the credentials */
    private String credentialCharset = DEFAULT_CHARSET;

    /**
     * Returns the response for the given message.
     *
     * @param message the message that was received from the server.
     * @param username the username to authenticate with.
     * @param password the password to authenticate with.
     * @param host The host.
     * @param domain the NT domain to authenticate in.
     * @return The response.
     * @throws ProxyException If the messages cannot be retrieved.
     */
    public String getResponseFor(String message,
            String username, String password, String host, String domain)
            throws IOException {

        final String response;
        if (message == null || message.trim().equals("")) {
            response = getType1Message(host, domain);
        } else {
            response = getType3Message(username, password, host, domain,
                    parseType2Message(message));
        }
        return response;
    }

    /**
     * Return the cipher for the specified key.
     * @param key The key.
     * @return Cipher The cipher.
     * @throws AuthenticationException If the cipher cannot be retrieved.
     */
    private DESEngine getCipher(byte[] key) throws IOException {

            DESEngine cipher = new DESEngine();
            key = setupKey(key);
            cipher.init(true, key);
            return cipher;

    }

    /**
     * Adds parity bits to the key.
     * @param key56 The key
     * @return The modified key.
     */
    private byte[] setupKey(byte[] key56) {
        byte[] key = new byte[8];
        key[0] = (byte) ((key56[0] >> 1) & 0xff);
        key[1] = (byte) ((((key56[0] & 0x01) << 6)
            | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff);
        key[2] = (byte) ((((key56[1] & 0x03) << 5)
            | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff);
        key[3] = (byte) ((((key56[2] & 0x07) << 4)
            | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff);
        key[4] = (byte) ((((key56[3] & 0x0f) << 3)
            | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff);
        key[5] = (byte) ((((key56[4] & 0x1f) << 2)
            | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff);
        key[6] = (byte) ((((key56[5] & 0x3f) << 1)
            | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff);
        key[7] = (byte) (key56[6] & 0x7f);

        for (int i = 0; i < key.length; i++) {
            key[i] = (byte) (key[i] << 1);
        }
        return key;
    }

    /**
     * Encrypt the data.
     * @param key The key.
     * @param bytes The data
     * @return byte[] The encrypted data
     * @throws HttpException If {@link Cipher.doFinal(byte[])} fails
     */
    private byte[] encrypt(byte[] key, byte[] bytes)
        throws IOException {


        DESEngine cipher = getCipher(key);
        byte[] enc = cipher.doFinal(bytes);
        return enc;

    }

    /**
     * Prepares the object to create a response of the given length.
     * @param length the length of the response to prepare.
     */
    private void prepareResponse(int length) {
        currentResponse = new byte[length];
        currentPosition = 0;
    }

    /**
     * Adds the given byte to the response.
     * @param b the byte to add.
     */
    private void addByte(byte b) {
        currentResponse[currentPosition] = b;
        currentPosition++;
    }

    /**
     * Adds the given bytes to the response.
     * @param bytes the bytes to add.
     */
    private void addBytes(byte[] bytes) {
        for (int i = 0; i < bytes.length; i++) {
            currentResponse[currentPosition] = bytes[i];
            currentPosition++;
        }
    }

    /**
     * Returns the response that has been generated after shrinking the array if
     * required and base64 encodes the response.
     * @return The response as above.
     */
    private String getResponse() {
        byte[] resp;
        if (currentResponse.length > currentPosition) {
            byte[] tmp = new byte[currentPosition];
            for (int i = 0; i < currentPosition; i++) {
                tmp[i] = currentResponse[i];
            }
            resp = tmp;
        } else {
            resp = currentResponse;
        }
        return new String(Base64.encode(resp));
    }

    /**
     * Creates the first message (type 1 message) in the NTLM authentication sequence.
     * This message includes the user name, domain and host for the authentication session.
     *
     * @param host the computer name of the host requesting authentication.
     * @param domain The domain to authenticate with.
     * @return String the message to add to the HTTP request header.
     */
    public String getType1Message(String host, String domain) {
        host = host.toUpperCase();
        domain = domain.toUpperCase();
        byte[] hostBytes = host.getBytes();
        byte[] domainBytes = domain.getBytes();

        int finalLength = 32 + hostBytes.length + domainBytes.length;
        prepareResponse(finalLength);

        // The initial id string.
        byte[] protocol = "NTLMSSP".getBytes();
        addBytes(protocol);
        addByte((byte) 0);

        // Type
        addByte((byte) 1);
        addByte((byte) 0);
        addByte((byte) 0);
        addByte((byte) 0);

        // Flags
        addByte((byte) 6);
        addByte((byte) 82);
        addByte((byte) 0);
        addByte((byte) 0);

        // Domain length (first time).
        int iDomLen = domainBytes.length;
        byte[] domLen = convertShort(iDomLen);
        addByte(domLen[0]);
        addByte(domLen[1]);

        // Domain length (second time).
        addByte(domLen[0]);
        addByte(domLen[1]);

        // Domain offset.
        byte[] domOff = convertShort(hostBytes.length + 32);
        addByte(domOff[0]);
        addByte(domOff[1]);
        addByte((byte) 0);
        addByte((byte) 0);

        // Host length (first time).
        byte[] hostLen = convertShort(hostBytes.length);
        addByte(hostLen[0]);
        addByte(hostLen[1]);

        // Host length (second time).
        addByte(hostLen[0]);
        addByte(hostLen[1]);

        // Host offset (always 32).
        byte[] hostOff = convertShort(32);
        addByte(hostOff[0]);
        addByte(hostOff[1]);
        addByte((byte) 0);
        addByte((byte) 0);

        // Host String.
        addBytes(hostBytes);

        // Domain String.
        addBytes(domainBytes);

        return getResponse();
    }

    /**
     * Extracts the server nonce out of the given message type 2.
     *
     * @param message the String containing the base64 encoded message.
     * @return an array of 8 bytes that the server sent to be used when
     * hashing the password.
     */
    public byte[] parseType2Message(String message) {
        // Decode the message first.
        byte[] msg = Base64.decode(message);
        byte[] nonce = new byte[8];
        // The nonce is the 8 bytes starting from the byte in position 24.
        for (int i = 0; i < 8; i++) {
            nonce[i] = msg[i + 24];
        }
        return nonce;
    }

    /**

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?