View Javadoc
1   package org.sentrysoftware.winrm.service.client.auth.ntlm;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * WinRM Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 - 2024 Sentry Software
8    * ჻჻჻჻჻჻
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   *
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
21   */
22  
23  import java.util.Arrays;
24  
25  import org.sentrysoftware.winrm.service.client.encryption.ByteArrayUtils;
26  import org.sentrysoftware.winrm.service.client.encryption.EncryptionUtils;
27  
28  /**
29   * Code from io.cloudsoft.winrm4j.client.ntlm.NtlmKeys
30   * release 0.12.3 @link https://github.com/cloudsoft/winrm4j
31   */
32  public class NtlmKeys {
33  
34  	// adapted from python ntlm-auth
35  	// also see NTLMEngineImpl.Handle
36  
37  
38  //	# Copyright: (c) 2018, Jordan Borean (@jborean93) <jborean93@gmail.com>
39  //	# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
40  
41  	private static final byte[] CLIENT_SIGNING =
42  			"session key to client-to-server signing key magic constant\0".getBytes();
43  	private static final byte[] SERVER_SIGNING =
44  			"session key to server-to-client signing key magic constant\0".getBytes();
45  	private static final byte[] CLIENT_SEALING =
46  			"session key to client-to-server sealing key magic constant\0".getBytes();
47  	private static final byte[] SERVER_SEALING =
48  			"session key to server-to-client sealing key magic constant\0".getBytes();
49  
50  	private final byte[] exportedSessionKey;
51  	private final long negotiateFlags;
52  
53  	public NtlmKeys(final Type3Message signAndSealData) {
54  		exportedSessionKey = signAndSealData.getExportedSessionKey();
55  		negotiateFlags = signAndSealData.getType2Flags();
56  	}
57  
58  	public void apply(final NTCredentialsWithEncryption credentials) {
59  		credentials.setNegotiateFlags(negotiateFlags);
60  
61  		credentials.setClientSigningKey(getSignKey(CLIENT_SIGNING) );
62  		credentials.setServerSigningKey(getSignKey(SERVER_SIGNING) );
63  		credentials.setClientSealingKey(getSealKey(CLIENT_SEALING) );
64  		credentials.setServerSealingKey(getSealKey(SERVER_SEALING) );
65  	}
66  
67  	/**
68  	 * 
69  	 * @param magicConstant a constant value set in the MS-NLMP documentation (constants.SignSealConstants)
70  	 * 
71  	 * @return Key used to sign messages
72  	 */
73  	private byte[] getSignKey(final byte[] magicConstant) {
74  		return EncryptionUtils.md5digest(ByteArrayUtils.concat(exportedSessionKey, magicConstant));
75  	}
76  
77  	/**
78  	 * Main method to use to calculate the seal_key used to seal (encrypt) messages.
79  	 * This will determine the correct method below to use based on the compatibility flags set 
80  	 * and should be called instead of the others
81  	 * 
82  	 * @param magicConstant a constant value set in the MS-NLMP documentation (constants.SignSealConstants)
83  	 * 
84  	 * @return Key used to seal messages
85  	 */
86  	private byte[] getSealKey(final byte[] magicConstant) {
87  
88  		// This for authentication where NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY has been
89  		// negotiated. Will weaken the keys if NTLMSSP_NEGOTIATE_128 is not negotiated,
90  		// will try NEGOTIATE_56 and then will default to the 40-bit key
91  		if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) {
92  			if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_128)) {
93  				return EncryptionUtils.md5digest(ByteArrayUtils.concat(exportedSessionKey, magicConstant));
94  
95  			}
96  			if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_56)) {
97  				return EncryptionUtils.md5digest(ByteArrayUtils.concat(
98  						Arrays.copyOfRange(exportedSessionKey, 0, 7),
99  						magicConstant));
100 			}
101 			return EncryptionUtils.md5digest(ByteArrayUtils.concat(
102 					Arrays.copyOfRange(exportedSessionKey, 0, 5),
103 					magicConstant));
104 		}
105 
106 		// This for authentication where NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
107 		//  has not been negotiated. Will weaken the keys if NTLMSSP_NEGOTIATE_56 is not negotiated it will default
108 		//  to the 40-bit key.
109 		if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_LM_KEY)) {
110 			throw new UnsupportedOperationException(
111 					"LM KEY negotiate mode not implemented; use extended session security instead");
112 		}
113 
114 		return exportedSessionKey;
115 	}
116 
117 	private boolean hasNegotiateFlag(long flag) {
118 		return (negotiateFlags & flag)==flag;
119 	}
120 }