| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| package rdpclient.ntlmssp; |
| |
| import java.lang.reflect.Method; |
| import java.security.Key; |
| import java.security.MessageDigest; |
| import java.security.SecureRandom; |
| import java.util.Arrays; |
| |
| import javax.crypto.Cipher; |
| import javax.crypto.Mac; |
| import javax.crypto.spec.SecretKeySpec; |
| |
| import rdpclient.rdp.RdpConstants; |
| |
| /** |
| * @see http://msdn.microsoft.com/en-us/library/cc236717.aspx |
| */ |
| public class CryptoAlgos implements NtlmConstants { |
| |
| /** |
| * Indicates the left-to-right concatenation of the string parameters, from |
| * the first string to the Nnth. Any numbers are converted to strings and all |
| * numeric conversions to strings retain all digits, even nonsignificant ones. |
| * The result is a string. For example, ConcatenationOf(0x00122, "XYZ", |
| * "Client") results in the string "00122XYZClient." |
| */ |
| public static String concatenationOf(String... args) { |
| StringBuffer sb = new StringBuffer(); |
| for (String arg : args) { |
| sb.append(arg); |
| } |
| return sb.toString(); |
| } |
| |
| /** |
| * Concatenate byte arrays. |
| */ |
| public static byte[] concatenationOf(byte[]... arrays) { |
| int length = 0; |
| for (byte[] array : arrays) { |
| length += array.length; |
| } |
| |
| byte[] result = new byte[length]; |
| int destPos = 0; |
| for (byte[] array : arrays) { |
| System.arraycopy(array, 0, result, destPos, array.length); |
| destPos += array.length; |
| } |
| |
| return result; |
| } |
| |
| /** Indicates a 32-bit CRC calculated over m. */ |
| public static byte[] CRC32(byte[] m) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Indicates the encryption of an 8-byte data item d with the 7-byte key k |
| * using the Data Encryption Standard (DES) algorithm in Electronic Codebook |
| * (ECB) mode. The result is 8 bytes in length ([FIPS46-2]). |
| */ |
| public static byte[] DES(byte[] k, byte[] d) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Indicates the encryption of an 8-byte data item D with the 16-byte key K |
| * using the Data Encryption Standard Long (DESL) algorithm. The result is 24 |
| * bytes in length. DESL(K, D) is computed as follows. |
| * |
| * <pre> |
| * ConcatenationOf( DES(K[0..6], D), |
| * DES(K[7..13], D), DES( |
| * ConcatenationOf(K[14..15], Z(5)), D)); |
| * </pre> |
| * |
| * Note K[] implies a key represented as a character array. |
| */ |
| public static byte[] DESL(byte[] k, byte[] d) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * An auxiliary function that returns an operating system version-specific |
| * value (section 2.2.2.8). |
| */ |
| public static byte[] getVersion() { |
| // Version (6.1, Build 7601), NTLM current revision: 15 |
| return new byte[] {0x06, 0x01, (byte)0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f}; |
| } |
| |
| /** |
| * Retrieve the user's LM response key from the server database (directory or |
| * local database). |
| */ |
| public static byte[] LMGETKEY(byte[] u, byte[] d) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** Retrieve the user's NT response key from the server database. */ |
| public static byte[] NTGETKEY(byte[] u, byte[] d) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Indicates the encryption of data item m with the key k using the HMAC |
| * algorithm ([RFC2104]). |
| */ |
| public static byte[] HMAC(byte[] k, byte[] m) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Indicates the computation of a 16-byte HMAC-keyed MD5 message digest of the |
| * byte string m using the key k. |
| */ |
| public static byte[] HMAC_MD5(byte[] k, byte[] m) { |
| try { |
| String algorithm = "HMacMD5"; |
| Mac hashMac = Mac.getInstance(algorithm); |
| |
| Key secretKey = new SecretKeySpec(k, 0, k.length, algorithm); |
| hashMac.init(secretKey); |
| return hashMac.doFinal(m); |
| } catch (Exception e) { |
| throw new RuntimeException("Cannot calculate HMAC-MD5.", e); |
| } |
| } |
| |
| /** |
| * Produces a key exchange key from the session base key K, LM response and |
| * server challenge SC as defined in the sections KXKEY, SIGNKEY, and SEALKEY. |
| */ |
| public static byte[] KXKEY(byte[] sessionBaseKey/*K, byte[] LM, byte[] SC*/) { |
| // Key eXchange Key is server challenge |
| /* In NTLMv2, KeyExchangeKey is the 128-bit SessionBaseKey */ |
| return Arrays.copyOf(sessionBaseKey, sessionBaseKey.length); |
| } |
| |
| /** |
| * Computes a one-way function of the user's password to use as the response |
| * key. NTLM v1 and NTLM v2 define separate LMOWF() functions in the NTLM v1 |
| * authentication and NTLM v2 authentication sections, respectively. |
| */ |
| public static byte[] LMOWF() { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Indicates the computation of an MD4 message digest of the null-terminated |
| * byte string m ([RFC1320]). |
| */ |
| public static byte[] MD4(byte[] m) { |
| try { |
| return sun.security.provider.MD4.getInstance().digest(m); |
| } catch (Exception e) { |
| throw new RuntimeException("Cannot calculate MD5.", e); |
| } |
| } |
| |
| /** |
| * Indicates the computation of an MD5 message digest of the null-terminated |
| * byte string m ([RFC1321]). |
| */ |
| public static byte[] MD5(byte[] m) { |
| try { |
| return MessageDigest.getInstance("MD5").digest(m); |
| } catch (Exception e) { |
| throw new RuntimeException("Cannot calculate MD5.", e); |
| } |
| } |
| |
| /** |
| * Indicates the computation of an MD5 message digest of a binary blob |
| * ([RFC4121] section 4.1.1.2). |
| */ |
| public static byte[] MD5_HASH(byte[] m) { |
| try { |
| return MessageDigest.getInstance("MD5").digest(m); |
| } catch (Exception e) { |
| throw new RuntimeException("Cannot calculate MD5.", e); |
| } |
| } |
| |
| /** A zero-length string. */ |
| public static final String NIL = ""; |
| |
| /** |
| * Indicates the computation of an n-byte cryptographic-strength random |
| * number. |
| * |
| * Note The NTLM Authentication Protocol does not define the statistical |
| * properties of the random number generator. It is left to the discretion of |
| * the implementation to define the strength requirements of the NONCE(n) |
| * operation. |
| */ |
| public static byte[] NONCE(int n) { |
| // Generate random nonce for LMv2 and NTv2 responses |
| byte[] nonce = new byte[n]; |
| SecureRandom random = new SecureRandom(); |
| random.nextBytes(nonce); |
| |
| // Fixed nonce for debugging purposes |
| //* DEBUG */for (int i = 0; i < N; i++) nonce[i] = (byte) (i + 1); |
| |
| return nonce; |
| } |
| |
| /** |
| * Computes a one-way function of the user's password to use as the response |
| * key. NTLM v1 and NTLM v2 define separate NTOWF() functions in the NTLM v1 |
| * authentication and NTLM v2 authentication sections, respectively. |
| */ |
| public static byte[] NTOWF() { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * The RC4 Encryption Algorithm. To obtain this stream cipher that is licensed |
| * by RSA Data Security, Inc., contact this company. |
| * |
| * Indicates the encryption of data item d with the current session or message |
| * key state, using the RC4 algorithm. h is the handle to a key state |
| * structure initialized by RC4INIT. |
| */ |
| public static byte[] RC4(Cipher h, byte[] d) { |
| return h.update(d); |
| } |
| |
| /** |
| * Indicates the encryption of data item d with the key k using the RC4 |
| * algorithm. |
| * |
| * Note The key sizes for RC4 encryption in NTLM are defined in sections |
| * KXKEY, SIGNKEY, and SEALKEY, where they are created. |
| */ |
| public static byte[] RC4K(byte[] k, byte[] d) { |
| try { |
| Cipher cipher = Cipher.getInstance("RC4"); |
| Key key = new SecretKeySpec(k, "RC4"); |
| cipher.init(Cipher.ENCRYPT_MODE, key); |
| return cipher.doFinal(d); |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * Initialization of the RC4 key and handle to a key state structure for the |
| * session. |
| */ |
| public static Cipher RC4Init(byte[] k) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Produces an encryption key from the session key as defined in sections |
| * KXKEY, SIGNKEY, and SEALKEY. |
| */ |
| public static byte[] SEALKEY(byte[] f, byte[] k, byte[] string1) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Produces a signing key from the session key as defined in sections KXKEY, |
| * SIGNKEY, and SEALKEY. |
| */ |
| public static byte[] SIGNKEY(int flag, byte[] k, byte[] string1) { |
| throw new RuntimeException("FATAL: Not implemented."); |
| } |
| |
| /** |
| * Indicates the retrieval of the current time as a 64-bit value, represented |
| * as the number of 100-nanosecond ticks elapsed since midnight of January |
| * 1st, 1601 (UTC). |
| */ |
| public static byte[] Currenttime() { |
| // (current time + milliseconds from 1.01.1601 to 1.01.1970) * |
| // 100-nanosecond ticks |
| long time = (System.currentTimeMillis() + 11644473600000L) * 10000; |
| |
| // Convert 64bit value to byte array. |
| byte[] result = new byte[8]; |
| for (int i = 0; i < 8; i++, time >>>= 8) { |
| result[i] = (byte)time; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Indicates the 2-byte little-endian byte order encoding of the Unicode |
| * UTF-16 representation of string. The Byte Order Mark (BOM) is not sent over |
| * the wire. |
| */ |
| public static byte[] UNICODE(String string) { |
| return string.getBytes(RdpConstants.CHARSET_16); |
| } |
| |
| /** Indicates the uppercase representation of string. */ |
| public static String UpperCase(String string) { |
| return string.toUpperCase(); |
| } |
| |
| /** |
| * Indicates the creation of a byte array of length N. Each byte in the array |
| * is initialized to the value zero. |
| */ |
| public static byte[] Z(int n) { |
| return new byte[n]; |
| } |
| |
| public static Cipher initRC4(byte[] key) { |
| try { |
| Cipher cipher = Cipher.getInstance("RC4"); |
| cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4")); |
| return cipher; |
| } catch (Exception e) { |
| throw new RuntimeException("Cannot initialize RC4 sealing handle with client sealing key.", e); |
| } |
| } |
| |
| /** |
| * Helper method for embedded test cases. |
| */ |
| public static void callAll(Object obj) { |
| Method[] methods = obj.getClass().getDeclaredMethods(); |
| for (Method m : methods) { |
| if (m.getName().startsWith("test")) { |
| try { |
| m.invoke(obj, (Object[])null); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| public static void main(String args[]) { |
| callAll(new CryptoAlgos()); |
| } |
| |
| } |