1 package org.metricshub.winrm.service.client.encryption;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import java.io.ByteArrayInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.net.URI;
28 import java.util.Arrays;
29 import java.util.List;
30 import org.apache.cxf.Bus;
31 import org.apache.cxf.io.CacheAndWriteOutputStream;
32 import org.apache.cxf.message.Message;
33 import org.apache.cxf.service.model.EndpointInfo;
34 import org.apache.cxf.transport.http.Address;
35 import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
36 import org.apache.cxf.transport.http.asyncclient.CXFHttpRequest;
37 import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
38 import org.apache.cxf.ws.addressing.EndpointReferenceType;
39 import org.apache.http.auth.Credentials;
40 import org.apache.http.client.config.AuthSchemes;
41 import org.apache.http.client.config.RequestConfig;
42 import org.apache.http.entity.BasicHttpEntity;
43
44
45
46
47
48
49
50
51
52
53
54 public class AsyncHttpEncryptionAwareConduit extends AsyncHTTPConduit {
55
56 static final byte[] PRE_AUTH_BOGUS_PAYLOAD = "AWAITING_ENCRYPTION_KEYS".getBytes();
57
58 private static final List<String> TARGET_AUTH_SCHEMES = Arrays.asList(AuthSchemes.SPNEGO, AuthSchemes.KERBEROS);
59
60 private static ContentWithType getAppropriate(final Message msg) {
61 final EncryptAndSignOutputStream encryptingStream = msg.getContent(EncryptAndSignOutputStream.class);
62 if (encryptingStream == null) {
63 throw new IllegalStateException("No SignAndEncryptOutInterceptor applied to message");
64 }
65 return encryptingStream.getAppropriate();
66 }
67
68 public AsyncHttpEncryptionAwareConduit(
69 final Bus bus,
70 final EndpointInfo endpointInfo,
71 final EndpointReferenceType endpointReferenceType,
72 final AsyncHttpEncryptionAwareConduitFactory factory
73 ) throws IOException {
74 super(bus, endpointInfo, endpointReferenceType, factory);
75 }
76
77 @Override
78 protected OutputStream createOutputStream(
79 final Message message,
80 final boolean needToCacheRequest,
81 final boolean isChunking,
82 final int chunkThreshold
83 ) throws IOException {
84 final NtlmEncryptionUtils encryptor = NtlmEncryptionUtils.of(message.get(Credentials.class));
85 if (encryptor == null) {
86 return super.createOutputStream(message, needToCacheRequest, isChunking, chunkThreshold);
87 }
88
89 if (Boolean.TRUE.equals(message.get(USE_ASYNC))) {
90
91 final CXFHttpRequest requestEntity = message.get(CXFHttpRequest.class);
92 final AsyncWrappedEncryptionAwareOutputStream out = new AsyncWrappedEncryptionAwareOutputStream(
93 message,
94 true,
95 false,
96 chunkThreshold,
97 getConduitName(),
98 requestEntity.getURI()
99 );
100
101 requestEntity.setOutputStream(out);
102 return out;
103 }
104
105 throw new IllegalStateException("Encryption only available with ASYNC at present");
106
107 }
108
109 @Override
110 protected void setupConnection(final Message message, final Address address, final HTTPClientPolicy csPolicy)
111 throws IOException {
112 super.setupConnection(message, address, csPolicy);
113
114
115
116 final CXFHttpRequest requestEntity = message.get(CXFHttpRequest.class);
117
118 final BasicHttpEntity entity = new EncryptionAwareHttpEntity() {
119 @Override
120 public boolean isRepeatable() {
121 return requestEntity.getEntity().isRepeatable();
122 }
123
124 @Override
125 protected ContentWithType getAppropriate() {
126 return AsyncHttpEncryptionAwareConduit.getAppropriate(message);
127 }
128 };
129 entity.setChunked(true);
130 entity.setContentType((String) message.get(Message.CONTENT_TYPE));
131
132 requestEntity.setEntity(entity);
133
134 requestEntity.setConfig(
135 RequestConfig.copy(requestEntity.getConfig()).setTargetPreferredAuthSchemes(TARGET_AUTH_SCHEMES).build()
136 );
137 }
138
139 private class AsyncWrappedEncryptionAwareOutputStream extends AsyncWrappedOutputStream {
140
141 public AsyncWrappedEncryptionAwareOutputStream(
142 final Message message,
143 final boolean needToCacheRequest,
144 final boolean isChunking,
145 final int chunkThreshold,
146 final String conduitName,
147 final URI uri
148 ) {
149 super(message, needToCacheRequest, isChunking, chunkThreshold, conduitName, uri);
150 }
151
152 @Override
153 protected void setupWrappedStream() throws IOException {
154 super.setupWrappedStream();
155
156 if (!(cachedStream.getFlowThroughStream() instanceof EncryptionAwareCacheAndWriteOutputStream)) {
157 cachedStream = new EncryptionAwareCacheAndWriteOutputStream(cachedStream.getFlowThroughStream());
158 wrappedStream = cachedStream;
159 }
160 }
161
162 private class EncryptionAwareCacheAndWriteOutputStream extends CacheAndWriteOutputStream {
163
164 public EncryptionAwareCacheAndWriteOutputStream(OutputStream outbufFlowThroughStream) {
165 super(outbufFlowThroughStream);
166 }
167
168 @Override
169 public byte[] getBytes() throws IOException {
170 final ContentWithType appropriate = AsyncHttpEncryptionAwareConduit.getAppropriate(outMessage);
171 return appropriate.getPayload();
172 }
173
174 @Override
175 public InputStream getInputStream() throws IOException {
176 return new ByteArrayInputStream(getBytes());
177 }
178 }
179 }
180 }