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 java.util.Objects;
25  import java.util.concurrent.atomic.AtomicLong;
26  import javax.crypto.Cipher;
27  import org.apache.http.HttpEntityEnclosingRequest;
28  import org.apache.http.HttpRequest;
29  import org.apache.http.auth.NTCredentials;
30  import org.metricshub.winrm.service.client.encryption.EncryptionAwareHttpEntity;
31  import org.metricshub.winrm.service.client.encryption.EncryptionUtils;
32  
33  /**
34   * NTCredentials with encryption.
35   * Code from io.cloudsoft.winrm4j.client.ntlm.NTCredentialsWithEncryption
36   * release 0.12.3 @link https://github.com/cloudsoft/winrm4j
37   */
38  public class NTCredentialsWithEncryption extends NTCredentials {
39  
40  	private static final long serialVersionUID = 1L;
41  
42  	private boolean isAuthenticated = false;
43  	private long negotiateFlags;
44  	private byte[] clientSigningKey;
45  	private byte[] serverSigningKey;
46  	private byte[] clientSealingKey;
47  	private byte[] serverSealingKey;
48  	private AtomicLong sequenceNumberIncoming = new AtomicLong(-1);
49  	private AtomicLong sequenceNumberOutgoing = new AtomicLong(-1);
50  
51  	public NTCredentialsWithEncryption(
52  		final String userName,
53  		final String password,
54  		final String workstation,
55  		final String domain
56  	) {
57  		super(userName, password, workstation, domain);
58  	}
59  
60  	public boolean isAuthenticated() {
61  		return isAuthenticated;
62  	}
63  
64  	public void setIsAuthenticated(boolean isAuthenticated) {
65  		this.isAuthenticated = isAuthenticated;
66  	}
67  
68  	public void setClientSigningKey(byte[] clientSigningKey) {
69  		this.clientSigningKey = clientSigningKey;
70  	}
71  
72  	public void setServerSigningKey(byte[] serverSigningKey) {
73  		this.serverSigningKey = serverSigningKey;
74  	}
75  
76  	public byte[] getClientSigningKey() {
77  		return clientSigningKey;
78  	}
79  
80  	public byte[] getServerSigningKey() {
81  		return serverSigningKey;
82  	}
83  
84  	public void setClientSealingKey(byte[] clientSealingKey) {
85  		this.clientSealingKey = clientSealingKey;
86  	}
87  
88  	public void setServerSealingKey(byte[] serverSealingKey) {
89  		this.serverSealingKey = serverSealingKey;
90  	}
91  
92  	public byte[] getClientSealingKey() {
93  		return clientSealingKey;
94  	}
95  
96  	public byte[] getServerSealingKey() {
97  		return serverSealingKey;
98  	}
99  
100 	public long getNegotiateFlags() {
101 		return negotiateFlags;
102 	}
103 
104 	public boolean hasNegotiateFlag(long flag) {
105 		return (getNegotiateFlags() & flag) == flag;
106 	}
107 
108 	public void setNegotiateFlags(long negotiateFlags) {
109 		this.negotiateFlags = negotiateFlags;
110 	}
111 
112 	public AtomicLong getSequenceNumberIncoming() {
113 		return sequenceNumberIncoming;
114 	}
115 
116 	public AtomicLong getSequenceNumberOutgoing() {
117 		return sequenceNumberOutgoing;
118 	}
119 
120 	private transient Cipher encryptor;
121 
122 	public Cipher getStatefulEncryptor() {
123 		if (encryptor == null) {
124 			encryptor = EncryptionUtils.arc4(getClientSealingKey());
125 		}
126 		return encryptor;
127 	}
128 
129 	private transient Cipher decryptor;
130 
131 	public Cipher getStatefulDecryptor() {
132 		if (decryptor == null) {
133 			decryptor = EncryptionUtils.arc4(getServerSealingKey());
134 		}
135 		return decryptor;
136 	}
137 
138 	void resetEncryption(final HttpRequest request) {
139 		setIsAuthenticated(false);
140 		clientSealingKey = null;
141 		clientSigningKey = null;
142 		serverSealingKey = null;
143 		serverSigningKey = null;
144 		encryptor = null;
145 		decryptor = null;
146 		sequenceNumberIncoming.set(-1);
147 		sequenceNumberOutgoing.set(-1);
148 
149 		if (
150 			request instanceof HttpEntityEnclosingRequest &&
151 			((HttpEntityEnclosingRequest) request).getEntity() instanceof EncryptionAwareHttpEntity
152 		) {
153 			((EncryptionAwareHttpEntity) ((HttpEntityEnclosingRequest) request).getEntity()).refreshHeaders(
154 					(HttpEntityEnclosingRequest) request
155 				);
156 		}
157 	}
158 
159 	void initEncryption(final Type3Message signAndSealData, final HttpRequest request) {
160 		setIsAuthenticated(true);
161 		if (signAndSealData != null && signAndSealData.getExportedSessionKey() != null) {
162 			new NtlmKeys(signAndSealData).apply(this);
163 		}
164 		if (
165 			request instanceof HttpEntityEnclosingRequest &&
166 			((HttpEntityEnclosingRequest) request).getEntity() instanceof EncryptionAwareHttpEntity
167 		) {
168 			((EncryptionAwareHttpEntity) ((HttpEntityEnclosingRequest) request).getEntity()).refreshHeaders(
169 					(HttpEntityEnclosingRequest) request
170 				);
171 		}
172 	}
173 
174 	@Override
175 	public String toString() {
176 		return getClass().getSimpleName() + super.toString() + "{auth=" + isAuthenticated() + "}";
177 	}
178 
179 	@Override
180 	public int hashCode() {
181 		final int prime = 31;
182 		int result = super.hashCode();
183 		result = prime * result + Arrays.hashCode(clientSealingKey);
184 		result = prime * result + Arrays.hashCode(clientSigningKey);
185 		result = prime * result + Arrays.hashCode(serverSealingKey);
186 		result = prime * result + Arrays.hashCode(serverSigningKey);
187 		result =
188 			prime * result + Objects.hash(isAuthenticated, negotiateFlags, sequenceNumberIncoming, sequenceNumberOutgoing);
189 		return result;
190 	}
191 
192 	@Override
193 	public boolean equals(Object obj) {
194 		if (this == obj) return true;
195 		if (!super.equals(obj)) return false;
196 		if (!(obj instanceof NTCredentialsWithEncryption)) return false;
197 		NTCredentialsWithEncryption other = (NTCredentialsWithEncryption) obj;
198 		return (
199 			Arrays.equals(clientSealingKey, other.clientSealingKey) &&
200 			Arrays.equals(clientSigningKey, other.clientSigningKey) &&
201 			isAuthenticated == other.isAuthenticated &&
202 			negotiateFlags == other.negotiateFlags &&
203 			Objects.equals(sequenceNumberIncoming, other.sequenceNumberIncoming) &&
204 			Objects.equals(sequenceNumberOutgoing, other.sequenceNumberOutgoing) &&
205 			Arrays.equals(serverSealingKey, other.serverSealingKey) &&
206 			Arrays.equals(serverSigningKey, other.serverSigningKey)
207 		);
208 	}
209 }