View Javadoc
1   package org.metricshub.winrm.service.client.auth.ntlm;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * WinRM Java Client
6    * ჻჻჻჻჻჻
7    * Copyright 2023 - 2024 Metricshub
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  import org.metricshub.winrm.service.client.encryption.ByteArrayUtils;
25  import org.metricshub.winrm.service.client.encryption.EncryptionUtils;
26  
27  /**
28   * Code from io.cloudsoft.winrm4j.client.ntlm.NtlmKeys
29   * release 0.12.3 @link https://github.com/cloudsoft/winrm4j
30   */
31  public class NtlmKeys {
32  
33  	// adapted from python ntlm-auth
34  	// also see NTLMEngineImpl.Handle
35  
36  	//	# Copyright: (c) 2018, Jordan Borean (@jborean93) <jborean93@gmail.com>
37  	//	# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
38  
39  	private static final byte[] CLIENT_SIGNING =
40  		"session key to client-to-server signing key magic constant\0".getBytes();
41  	private static final byte[] SERVER_SIGNING =
42  		"session key to server-to-client signing key magic constant\0".getBytes();
43  	private static final byte[] CLIENT_SEALING =
44  		"session key to client-to-server sealing key magic constant\0".getBytes();
45  	private static final byte[] SERVER_SEALING =
46  		"session key to server-to-client sealing key magic constant\0".getBytes();
47  
48  	private final byte[] exportedSessionKey;
49  	private final long negotiateFlags;
50  
51  	public NtlmKeys(final Type3Message signAndSealData) {
52  		exportedSessionKey = signAndSealData.getExportedSessionKey();
53  		negotiateFlags = signAndSealData.getType2Flags();
54  	}
55  
56  	public void apply(final NTCredentialsWithEncryption credentials) {
57  		credentials.setNegotiateFlags(negotiateFlags);
58  
59  		credentials.setClientSigningKey(getSignKey(CLIENT_SIGNING));
60  		credentials.setServerSigningKey(getSignKey(SERVER_SIGNING));
61  		credentials.setClientSealingKey(getSealKey(CLIENT_SEALING));
62  		credentials.setServerSealingKey(getSealKey(SERVER_SEALING));
63  	}
64  
65  	/**
66  	 *
67  	 * @param magicConstant a constant value set in the MS-NLMP documentation (constants.SignSealConstants)
68  	 *
69  	 * @return Key used to sign messages
70  	 */
71  	private byte[] getSignKey(final byte[] magicConstant) {
72  		return EncryptionUtils.md5digest(ByteArrayUtils.concat(exportedSessionKey, magicConstant));
73  	}
74  
75  	/**
76  	 * Main method to use to calculate the seal_key used to seal (encrypt) messages.
77  	 * This will determine the correct method below to use based on the compatibility flags set
78  	 * and should be called instead of the others
79  	 *
80  	 * @param magicConstant a constant value set in the MS-NLMP documentation (constants.SignSealConstants)
81  	 *
82  	 * @return Key used to seal messages
83  	 */
84  	private byte[] getSealKey(final byte[] magicConstant) {
85  		// This for authentication where NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY has been
86  		// negotiated. Will weaken the keys if NTLMSSP_NEGOTIATE_128 is not negotiated,
87  		// will try NEGOTIATE_56 and then will default to the 40-bit key
88  		if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY)) {
89  			if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_128)) {
90  				return EncryptionUtils.md5digest(ByteArrayUtils.concat(exportedSessionKey, magicConstant));
91  			}
92  			if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_56)) {
93  				return EncryptionUtils.md5digest(
94  					ByteArrayUtils.concat(Arrays.copyOfRange(exportedSessionKey, 0, 7), magicConstant)
95  				);
96  			}
97  			return EncryptionUtils.md5digest(
98  				ByteArrayUtils.concat(Arrays.copyOfRange(exportedSessionKey, 0, 5), magicConstant)
99  			);
100 		}
101 
102 		// This for authentication where NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
103 		//  has not been negotiated. Will weaken the keys if NTLMSSP_NEGOTIATE_56 is not negotiated it will default
104 		//  to the 40-bit key.
105 		if (hasNegotiateFlag(NTLMEngineUtils.NTLMSSP_NEGOTIATE_LM_KEY)) {
106 			throw new UnsupportedOperationException(
107 				"LM KEY negotiate mode not implemented; use extended session security instead"
108 			);
109 		}
110 
111 		return exportedSessionKey;
112 	}
113 
114 	private boolean hasNegotiateFlag(long flag) {
115 		return (negotiateFlags & flag) == flag;
116 	}
117 }