[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 702
  • Last Modified:

Cipher throws BadPaddingException

In the code below, the call to ci.doFinal() throws a BadPaddingException. What am I doing incorrectly? This is frustrating me to no end.


Cipher ciph = Cipher.getInstance("AES/ECB/PKCS5Padding");
ciph.init(Cipher.ENCRYPT_MODE, key); //also used for decrypting

while (true) {
      int count = in.read(inBlock, 0, blockSize);
      if (count != -1) {
            ciph.update(inBlock, 0, count); //this output gets sent
      } else {
            ciph.doFinal(); //as does this output
            break;
      }
}


(Note: In my actually program, I'm using code like this exerpt for both encrypting and decrypting. The encrypted bytes are sent to a remote computer, and decrypted there.)

0
bobwood2000
Asked:
bobwood2000
3 Solutions
 
Dejan PažinCommented:

Do you handle the bytes with InputStreams and OutputStreams, or are you using any FileReaders or FileWriters? And also are you converting these bytes to characters at any time (e.g. creating a String)?
0
 
gnoonCommented:
My experience, the BadPaddingException should be thrown while decrypting with a wrong key. Are you sure the error is thrown while encrypting?
0
 
rama_krishna580Commented:
Hi,

you may look @ here..it may help you...

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.httpclient.util.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Provides an implementation of the NTLM authentication protocol.
 *
 * This class provides methods for generating authentication
 * challenge responses for the NTLM authentication protocol.  The NTLM
 * protocol is a proprietary Microsoft protocol and as such no RFC
 * exists for it.  This class is based upon the reverse engineering
 * efforts of a wide range of people.
 *
 * Please note that an implementation of JCE must be correctly installed and configured when
 * using NTLM support.
 *
 * This class should not be used externally to HttpClient as it's API is specifically
 * designed to work with HttpClient's use case, in particular it's connection management.
 *
 * @deprecated this class will be made package access for 2.0beta2
 *
 * @author Adrian Sutton
 * @author Jeff Dever
 * @author Mike Bowler
 *
 * @version $Revision: 1.12.2.2 $ $Date: 2004/02/22 18:21:13 $
 * @since 2.0alpha2
 */
public final class NTLM {

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

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

    /** Log object for this class. */
    private static final Log LOG = LogFactory.getLog(NTLM.class);

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

    /**
     * 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 HttpException If the messages cannot be retrieved.
     */
    public final String getResponseFor(String message,
            String username, String password, String host, String domain)
            throws HttpException {
               
        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 HttpException If the cipher cannot be retrieved.
     */
    private Cipher getCipher(byte[] key) throws HttpException {
        try {
            final Cipher ecipher = Cipher.getInstance("DES/ECB/NoPadding");
            key = setupKey(key);
            ecipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "DES"));
            return ecipher;
        } catch (NoSuchAlgorithmException e) {
            throw new HttpException("DES encryption is not available.");
        } catch (InvalidKeyException e) {
            throw new HttpException("Invalid key for DES encryption.");
        } catch (NoSuchPaddingException e) {
            throw new HttpException(
                "NoPadding option for DES is not available.");
        }
    }

    /**
     * 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 HttpException {
        Cipher ecipher = getCipher(key);
        try {
            byte[] enc = ecipher.doFinal(bytes);
            return enc;
        } catch (IllegalBlockSizeException e) {
            throw new HttpException("Invalid block size for DES encryption.");
        } catch (BadPaddingException e) {
            throw new HttpException(
                    "Data not padded correctly for DES encryption.");
        }
    }

    /**
     * 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 HttpConstants.getString(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.
     */
    private String getType1Message(String host, String domain) {
        host = host.toUpperCase();
        domain = domain.toUpperCase();
        byte[] hostBytes = getBytes(host);
        byte[] domainBytes = getBytes(domain);

        int finalLength = 32 + hostBytes.length + domainBytes.length;
        prepareResponse(finalLength);
       
        // The initial id string.
        byte[] protocol = getBytes("NTLMSSP");
        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.
     */
    private byte[] parseType2Message(String message) {
        // Decode the message first.
        byte[] msg = Base64.decode(getBytes(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;
    }

    /**
     * Creates the type 3 message using the given server nonce.  The type 3 message includes all the
     * information for authentication, host, domain, username and the result of encrypting the
     * nonce sent by the server using the user's password as the key.
     *
     * @param user The user name.  This should not include the domain name.
     * @param password The password.
     * @param host The host that is originating the authentication request.
     * @param domain The domain to authenticate within.
     * @param nonce the 8 byte array the server sent.
     * @return The type 3 message.
     * @throws HttpException If {@encrypt(byte[],byte[])} fails.
     */
    private String getType3Message(String user, String password,
            String host, String domain, byte[] nonce)
    throws HttpException {

        int ntRespLen = 0;
        int lmRespLen = 24;
        domain = domain.toUpperCase();
        host = host.toUpperCase();
        user = user.toUpperCase();
        byte[] domainBytes = getBytes(domain);
        byte[] hostBytes = getBytes(host);
        byte[] userBytes = getBytes(user);
        int domainLen = domainBytes.length;
        int hostLen = hostBytes.length;
        int userLen = userBytes.length;
        int finalLength = 64 + ntRespLen + lmRespLen + domainLen
            + userLen + hostLen;
        prepareResponse(finalLength);
        byte[] ntlmssp = getBytes("NTLMSSP");
        addBytes(ntlmssp);
        addByte((byte) 0);
        addByte((byte) 3);
        addByte((byte) 0);
        addByte((byte) 0);
        addByte((byte) 0);

        // LM Resp Length (twice)
        addBytes(convertShort(24));
        addBytes(convertShort(24));

        // LM Resp Offset
        addBytes(convertShort(finalLength - 24));
        addByte((byte) 0);
        addByte((byte) 0);

        // NT Resp Length (twice)
        addBytes(convertShort(0));
        addBytes(convertShort(0));

        // NT Resp Offset
        addBytes(convertShort(finalLength));
        addByte((byte) 0);
        addByte((byte) 0);

        // Domain length (twice)
        addBytes(convertShort(domainLen));
        addBytes(convertShort(domainLen));
       
        // Domain offset.
        addBytes(convertShort(64));
        addByte((byte) 0);
        addByte((byte) 0);

        // User Length (twice)
        addBytes(convertShort(userLen));
        addBytes(convertShort(userLen));

        // User offset
        addBytes(convertShort(64 + domainLen));
        addByte((byte) 0);
        addByte((byte) 0);

        // Host length (twice)
        addBytes(convertShort(hostLen));
        addBytes(convertShort(hostLen));

        // Host offset
        addBytes(convertShort(64 + domainLen + userLen));

        for (int i = 0; i < 6; i++) {
            addByte((byte) 0);
        }

        // Message length
        addBytes(convertShort(finalLength));
        addByte((byte) 0);
        addByte((byte) 0);

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

        addBytes(domainBytes);
        addBytes(userBytes);
        addBytes(hostBytes);
        addBytes(hashPassword(password, nonce));
        return getResponse();
    }

    /**
     * Creates the LANManager and NT response for the given password using the
     * given nonce.
     * @param password the password to create a hash for.
     * @param nonce the nonce sent by the server.
     * @return The response.
     * @throws HttpException If {@link #encrypt(byte[],byte[])} fails.
     */
    private byte[] hashPassword(String password, byte[] nonce)
        throws HttpException {
        byte[] passw = getBytes(password.toUpperCase());
        byte[] lmPw1 = new byte[7];
        byte[] lmPw2 = new byte[7];

        int len = passw.length;
        if (len > 7) {
            len = 7;
        }

        int idx;
        for (idx = 0; idx < len; idx++) {
            lmPw1[idx] = passw[idx];
        }
        for (; idx < 7; idx++) {
            lmPw1[idx] = (byte) 0;
        }

        len = passw.length;
        if (len > 14) {
            len = 14;
        }
        for (idx = 7; idx < len; idx++) {
            lmPw2[idx - 7] = passw[idx];
        }
        for (; idx < 14; idx++) {
            lmPw2[idx - 7] = (byte) 0;
        }

        // Create LanManager hashed Password
        byte[] magic = {
            (byte) 0x4B, (byte) 0x47, (byte) 0x53, (byte) 0x21,
            (byte) 0x40, (byte) 0x23, (byte) 0x24, (byte) 0x25
        };

        byte[] lmHpw1;
        lmHpw1 = encrypt(lmPw1, magic);

        byte[] lmHpw2 = encrypt(lmPw2, magic);

        byte[] lmHpw = new byte[21];
        for (int i = 0; i < lmHpw1.length; i++) {
            lmHpw[i] = lmHpw1[i];
        }
        for (int i = 0; i < lmHpw2.length; i++) {
            lmHpw[i + 8] = lmHpw2[i];
        }
        for (int i = 0; i < 5; i++) {
            lmHpw[i + 16] = (byte) 0;
        }

        // Create the responses.
        byte[] lmResp = new byte[24];
        calcResp(lmHpw, nonce, lmResp);

        return lmResp;
    }

    /**
     * Takes a 21 byte array and treats it as 3 56-bit DES keys.  The 8 byte
     * plaintext is encrypted with each key and the resulting 24 bytes are
     * stored in the results array.
     *
     * @param keys The keys.
     * @param plaintext The plain text to encrypt.
     * @param results Where the results are stored.
     * @throws HttpException If {@link #encrypt(byte[],byte[])} fails.
     */
    private void calcResp(byte[] keys, byte[] plaintext, byte[] results)
        throws HttpException {
        byte[] keys1 = new byte[7];
        byte[] keys2 = new byte[7];
        byte[] keys3 = new byte[7];
        for (int i = 0; i < 7; i++) {
            keys1[i] = keys[i];
        }

        for (int i = 0; i < 7; i++) {
            keys2[i] = keys[i + 7];
        }

        for (int i = 0; i < 7; i++) {
            keys3[i] = keys[i + 14];
        }
        byte[] results1 = encrypt(keys1, plaintext);

        byte[] results2 = encrypt(keys2, plaintext);

        byte[] results3 = encrypt(keys3, plaintext);

        for (int i = 0; i < 8; i++) {
            results[i] = results1[i];
        }
        for (int i = 0; i < 8; i++) {
            results[i + 8] = results2[i];
        }
        for (int i = 0; i < 8; i++) {
            results[i + 16] = results3[i];
        }
    }

    /**
     * Converts a given number to a two byte array in little endian order.
     * @param num the number to convert.
     * @return The byte representation of num in little endian order.
     */
    private byte[] convertShort(int num) {
        byte[] val = new byte[2];
        String hex = Integer.toString(num, 16);
        while (hex.length() < 4) {
            hex = "0" + hex;
        }
        String low = hex.substring(2, 4);
        String high = hex.substring(0, 2);

        val[0] = (byte) Integer.parseInt(low, 16);
        val[1] = (byte) Integer.parseInt(high, 16);
        return val;
    }
   
    /**
     * Convert a string to a byte array.
     * @param s The string
     * @return byte[] The resulting byte array.
     */
    private static byte[] getBytes(final String s) {
        if (s == null) {
            throw new IllegalArgumentException("Parameter may not be null");
        }
        try {
            return s.getBytes(DEFAULT_CHARSET);
        } catch (UnsupportedEncodingException unexpectedEncodingException) {
            throw new RuntimeException("NTLM requires ASCII support");
        }
    }
}

http://jce.iaik.tugraz.at/mailarchive/iaik-jce/msg01575.html

R.K
0

Featured Post

Upgrade your Question Security!

Add Premium security features to your question to ensure its privacy or anonymity. Learn more about your ability to control Question Security today.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now