View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.client5.http.impl.auth;
28  
29  import java.io.ByteArrayInputStream;
30  import java.nio.charset.StandardCharsets;
31  import java.security.cert.Certificate;
32  import java.security.cert.CertificateFactory;
33  import java.util.Random;
34  
35  import org.junit.jupiter.api.Assertions;
36  import org.junit.jupiter.api.Test;
37  
38  @SuppressWarnings("deprecation")
39  public class TestNTLMEngineImpl {
40  
41      @Test
42      public void testMD4() throws Exception {
43          checkMD4("", "31d6cfe0d16ae931b73c59d7e0c089c0");
44          checkMD4("a", "bde52cb31de33e46245e05fbdbd6fb24");
45          checkMD4("abc", "a448017aaf21d8525fc10ae87aa6729d");
46          checkMD4("message digest", "d9130a8164549fe818874806e1c7014b");
47          checkMD4("abcdefghijklmnopqrstuvwxyz", "d79e1c308aa5bbcdeea8ed63df412da9");
48          checkMD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
49                  "043f8582f241db351ce627e153e7f0e4");
50          checkMD4(
51                  "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
52                  "e33b4ddc9c38f2199c3e7b164fcc0536");
53      }
54  
55      /* Test suite helper */
56      static byte toNibble(final char c) {
57          if (c >= 'a' && c <= 'f') {
58              return (byte) (c - 'a' + 0x0a);
59          }
60          if (c >= 'A' && c <= 'F') {
61              return (byte) (c - 'A' + 0x0a);
62          }
63          return (byte) (c - '0');
64      }
65  
66      /* Test suite helper */
67      static byte[] toBytes(final String hex) {
68          final byte[] rval = new byte[hex.length() / 2];
69          int i = 0;
70          while (i < rval.length) {
71              rval[i] = (byte) ((toNibble(hex.charAt(i * 2)) << 4) | (toNibble(hex
72                      .charAt(i * 2 + 1))));
73              i++;
74          }
75          return rval;
76      }
77  
78      /* Test suite MD4 helper */
79      static void checkMD4(final String input, final String hexOutput) throws Exception {
80          final NTLMEngineImpl.MD4 md4;
81          md4 = new NTLMEngineImpl.MD4();
82          md4.update(input.getBytes(StandardCharsets.US_ASCII));
83          final byte[] answer = md4.getOutput();
84          final byte[] correctAnswer = toBytes(hexOutput);
85          if (answer.length != correctAnswer.length) {
86              throw new Exception("Answer length disagrees for MD4('" + input + "')");
87          }
88          int i = 0;
89          while (i < answer.length) {
90              if (answer[i] != correctAnswer[i]) {
91                  throw new Exception("Answer value for MD4('" + input + "') disagrees at position "
92                          + i);
93              }
94              i++;
95          }
96      }
97  
98      @Test
99      public void testLMResponse() throws Exception {
100         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
101             new Random(1234),
102             1234L,
103             null,
104             null,
105             "SecREt01".toCharArray(),
106             toBytes("0123456789abcdef"),
107             null,
108             null,
109             null,
110             null,
111             null,
112             null);
113 
114         checkArraysMatch(toBytes("c337cd5cbd44fc9782a667af6d427c6de67c20c2d3e77c56"),
115             gen.getLMResponse());
116     }
117 
118     @Test
119     public void testNTLMResponse() throws Exception {
120         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
121             new Random(1234),
122             1234L,
123             null,
124             null,
125             "SecREt01".toCharArray(),
126             toBytes("0123456789abcdef"),
127             null,
128             null,
129             null,
130             null,
131             null,
132             null);
133 
134         checkArraysMatch(toBytes("25a98c1c31e81847466b29b2df4680f39958fb8c213a9cc6"),
135             gen.getNTLMResponse());
136     }
137 
138     @Test
139     public void testLMv2Response() throws Exception {
140         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
141             new Random(1234),
142             1234L,
143             "DOMAIN",
144             "user",
145             "SecREt01".toCharArray(),
146             toBytes("0123456789abcdef"),
147             "DOMAIN",
148             null,
149             toBytes("ffffff0011223344"),
150             toBytes("ffffff0011223344"),
151             null,
152             null);
153 
154         checkArraysMatch(toBytes("d6e6152ea25d03b7c6ba6629c2d6aaf0ffffff0011223344"),
155             gen.getLMv2Response());
156     }
157 
158     @Test
159     public void testNTLMv2Response() throws Exception {
160         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
161             new Random(1234),
162             1234L,
163             "DOMAIN",
164             "user",
165             "SecREt01".toCharArray(),
166             toBytes("0123456789abcdef"),
167             "DOMAIN",
168             toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"),
169             toBytes("ffffff0011223344"),
170             toBytes("ffffff0011223344"),
171             null,
172             toBytes("0090d336b734c301"));
173 
174         checkArraysMatch(toBytes("01010000000000000090d336b734c301ffffff00112233440000000002000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d000000000000000000"),
175             gen.getNTLMv2Blob());
176         checkArraysMatch(toBytes("cbabbca713eb795d04c97abc01ee498301010000000000000090d336b734c301ffffff00112233440000000002000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d000000000000000000"),
177             gen.getNTLMv2Response());
178     }
179 
180     @Test
181     public void testLM2SessionResponse() throws Exception {
182         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
183             new Random(1234),
184             1234L,
185             "DOMAIN",
186             "user",
187             "SecREt01".toCharArray(),
188             toBytes("0123456789abcdef"),
189             "DOMAIN",
190             toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"),
191             toBytes("ffffff0011223344"),
192             toBytes("ffffff0011223344"),
193             null,
194             toBytes("0090d336b734c301"));
195 
196         checkArraysMatch(toBytes("ffffff001122334400000000000000000000000000000000"),
197             gen.getLM2SessionResponse());
198     }
199 
200     @Test
201     public void testNTLM2SessionResponse() throws Exception {
202         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
203             new Random(1234),
204             1234L,
205             "DOMAIN",
206             "user",
207             "SecREt01".toCharArray(),
208             toBytes("0123456789abcdef"),
209             "DOMAIN",
210             toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"),
211             toBytes("ffffff0011223344"),
212             toBytes("ffffff0011223344"),
213             null,
214             toBytes("0090d336b734c301"));
215 
216         checkArraysMatch(toBytes("10d550832d12b2ccb79d5ad1f4eed3df82aca4c3681dd455"),
217             gen.getNTLM2SessionResponse());
218     }
219 
220     @Test
221     public void testNTLMUserSessionKey() throws Exception {
222         final NTLMEngineImpl.CipherGen gen = new NTLMEngineImpl.CipherGen(
223             new Random(1234),
224             1234L,
225             "DOMAIN",
226             "user",
227             "SecREt01".toCharArray(),
228             toBytes("0123456789abcdef"),
229             "DOMAIN",
230             toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"),
231             toBytes("ffffff0011223344"),
232             toBytes("ffffff0011223344"),
233             null,
234             toBytes("0090d336b734c301"));
235 
236         checkArraysMatch(toBytes("3f373ea8e4af954f14faa506f8eebdc4"),
237             gen.getNTLMUserSessionKey());
238     }
239 
240     @Test
241     public void testType1Message() throws Exception {
242         final byte[] bytes = new NTLMEngineImpl.Type1Message("myhost", "mydomain").getBytes();
243         final byte[] bytes2 = toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400");
244         checkArraysMatch(bytes2, bytes);
245     }
246 
247     @Test
248     public void testType3Message() throws Exception {
249         final byte[] bytes = new NTLMEngineImpl.Type3Message(
250                 new Random(1234),
251                 1234L,
252                 "me", "mypassword", "myhost", "mydomain".toCharArray(),
253                 toBytes("0001020304050607"),
254                 0xffffffff,
255                 null,null).getBytes();
256         checkArraysMatch(toBytes("4E544C4D53535000030000001800180048000000180018006000000004000400780000000C000C007C0000001400140088000000100010009C000000FFFFFFFF0501280A0000000FA86886A5D297814200000000000000000000000000000000EEC7568E00798491244959B9C942F4F367C5CBABEEF546F74D0045006D00790068006F00730074006D007900700061007300730077006F007200640094DDAB1EBB82C9A1AB914CAE6F199644"),
257             bytes);
258         final byte[] bytes2 = new NTLMEngineImpl.Type3Message(
259                 new Random(1234),
260                 1234L,
261                 "me", "mypassword", "myhost", "mydomain".toCharArray(),
262                 toBytes("0001020304050607"),
263                 0xffffffff,
264                 "mytarget",
265                 toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000")).getBytes();
266         checkArraysMatch(toBytes("4E544C4D53535000030000001800180048000000920092006000000004000400F20000000C000C00F600000014001400020100001000100016010000FFFFFFFF0501280A0000000F3695F1EA7B164788A437892FA7504320DA2D8CF378EBC83CE856A8FB985BF7783545828A91A13AE8010100000000000020CBFAD5DEB19D01A86886A5D29781420000000002000C0044004F004D00410049004E0001000C005300450052005600450052000400140064006F006D00610069006E002E0063006F006D00030022007300650072007600650072002E0064006F006D00610069006E002E0063006F006D0000000000000000004D0045006D00790068006F00730074006D007900700061007300730077006F0072006400BB1AAD36F11631CC7CBC8800CEEE1C99"),
267             bytes2);
268     }
269 
270     private static final String cannedCert =
271         "-----BEGIN CERTIFICATE-----\n"+
272         "MIIDIDCCAgigAwIBAgIEOqKaWTANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzEQMA4GA1UEBxMH\n"+
273         "TXkgQ2l0eTEYMBYGA1UEChMPTXkgT3JnYW5pemF0aW9uMRcwFQYDVQQDEw5NeSBBcHBsaWNhdGlvbjAe\n"+
274         "Fw0xNzAzMTcxNDAyMzRaFw0yNzAzMTUxNDAyMzRaMFIxCzAJBgNVBAYTAlVTMRAwDgYDVQQHEwdNeSBD\n"+
275         "aXR5MRgwFgYDVQQKEw9NeSBPcmdhbml6YXRpb24xFzAVBgNVBAMTDk15IEFwcGxpY2F0aW9uMIIBIjAN\n"+
276         "BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArc+mbViBaHeRSt82KrJ5IF+62b/Qob95Lca4DJIislTY\n"+
277         "vLPIo0R1faBV8BkEeUQwo01srkf3RaGLCHNZnFal4KEzbtiUy6W+n08G5E9w9YG+WSwW2dmjvEI7k2a2\n"+
278         "xqlaM4NdMKL4ONPXcxfZsMDqxDgpdkaNPKpZ10NDq6rmBTkQw/OSG0z1KLtwLkF1ZQ/3mXdjVzvP83V2\n"+
279         "g17AqBazb0Z1YHsVKmkGjPqnq3niJH/6Oke4N+5k/1cE5lSJcQNGP0nqeGdJfvqQZ+gk6gH/sOngZL9X\n"+
280         "hPVkpseAwHa+xuPneDSjibLgLmMt3XGDK6jGfjdp5FWqFvAD5E3LHbW9gwIDAQABMA0GCSqGSIb3DQEB\n"+
281         "CwUAA4IBAQCpUXUHhl5LyMSO5Q0OktEc9AaFjZtVfknpPde6Zeh35Pqd2354ErvJSBWgzFAphda0oh2s\n"+
282         "OIAFkM6LJQEnVDTbXDXN+YY8e3gb9ryfh85hkhC0XI9qp17WPSkmw8XgDfvRd6YQgKm1AnLxjOCwG2jg\n"+
283         "i09iZBIWkW3ZeRAMvWPHHjvq44iZB5ZrEl0apgumS6MxpUzKOr5Pcq0jxJDw2UCj5YloFMNl+UINv2vV\n"+
284         "aL/DR6ivc61dOfN1E/VNBGkkCk/AogNyucGiFMCq9hd25Y9EbkBBqObYTH1XMX+ufsJh+6hG7KDQ1e/F\n"+
285         "nRrlhKwM2uRe+aSH0D6/erjDBT7tXvwn\n"+
286         "-----END CERTIFICATE-----";
287 
288     @Test
289     public void testType3MessageWithCert() throws Exception {
290         final ByteArrayInputStream fis = new ByteArrayInputStream(cannedCert.getBytes(StandardCharsets.US_ASCII));
291 
292         final CertificateFactory cf = CertificateFactory.getInstance("X.509");
293 
294         final Certificate cert = cf.generateCertificate(fis);
295 
296         final byte[] bytes = new NTLMEngineImpl.Type3Message(
297                 new Random(1234),
298                 1234L,
299                 "me", "mypassword", "myhost", "mydomain".toCharArray(),
300                 toBytes("0001020304050607"),
301                 0xffffffff,
302                 "mytarget",
303                 toBytes("02000c0044004f004d00410049004e0001000c005300450052005600450052000400140064006f006d00610069006e002e0063006f006d00030022007300650072007600650072002e0064006f006d00610069006e002e0063006f006d0000000000"),
304                 cert,
305                 toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400"),
306                 toBytes("4E544C4D5353500001000000018208A20C000C003800000010001000280000000501280A0000000F6D00790064006F006D00610069006E004D00590048004F0053005400FFFEFDFCFBFA")).getBytes();
307 
308         checkArraysMatch(toBytes("4E544C4D53535000030000001800180058000000AE00AE0070000000040004001E0100000C000C0022010000140014002E0100001000100042010000FFFFFFFF0501280A0000000FEEFCCE4187D6CDF1F91C686C4E571D943695F1EA7B164788A437892FA7504320DA2D8CF378EBC83C59D7A3B2951929079B66621D7CF4326B010100000000000020CBFAD5DEB19D01A86886A5D29781420000000002000C0044004F004D00410049004E0001000C005300450052005600450052000400140064006F006D00610069006E002E0063006F006D00030022007300650072007600650072002E0064006F006D00610069006E002E0063006F006D0006000400020000000A00100038EDC0B7EF8D8FE9E1E6A83F6DFEB8FF00000000000000004D0045006D00790068006F00730074006D007900700061007300730077006F0072006400BB1AAD36F11631CC7CBC8800CEEE1C99"),
309             bytes);
310     }
311 
312     @Test
313     public void testRC4() throws Exception {
314         checkArraysMatch(toBytes("e37f97f2544f4d7e"),
315             NTLMEngineImpl.RC4(toBytes("0a003602317a759a"),
316                 toBytes("2785f595293f3e2813439d73a223810d")));
317     }
318 
319     /* Byte array check helper */
320     static void checkArraysMatch(final byte[] a1, final byte[] a2)
321         throws Exception {
322         Assertions.assertEquals(a1.length,a2.length);
323         for (int i = 0; i < a1.length; i++) {
324             Assertions.assertEquals(a1[i],a2[i]);
325         }
326     }
327 
328 }