blob: d0e72e961fd6b5acc62d9d1b48391851493e6f05 [file] [log] [blame]
// 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());
}
}