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.IOException;
24 import java.io.OutputStream;
25 import java.util.Objects;
26 import org.apache.cxf.io.CachedOutputStream;
27 import org.apache.cxf.message.Message;
28 import org.apache.http.auth.Credentials;
29 import org.metricshub.winrm.service.client.auth.ntlm.NTCredentialsWithEncryption;
30
31
32
33
34
35 class EncryptAndSignOutputStream extends CachedOutputStream {
36
37 private final CachedOutputStream unencrypted;
38 private ContentWithType unencryptedResult = null;
39 private ContentWithType encrypted = null;
40 private final Message message;
41
42 private OutputStream wrapped;
43
44 private NTCredentialsWithEncryption credentials;
45
46 public EncryptAndSignOutputStream(final Message message, final OutputStream outputStream) {
47 super();
48 this.message = message;
49 wrapped = outputStream;
50 unencrypted = new CachedOutputStream();
51
52 final Object creds = message.get(Credentials.class.getName());
53 if (creds instanceof NTCredentialsWithEncryption) {
54 credentials = (NTCredentialsWithEncryption) creds;
55 }
56 }
57
58 @Override
59 public void resetOut(final OutputStream outputStream, final boolean copyOldContent) throws IOException {
60 super.resetOut(outputStream, copyOldContent);
61 }
62
63 @Override
64 public void close() throws IOException {
65 super.close();
66 unencrypted.write(getBytes());
67 currentStream = NullOutputStream.NULL_OUTPUT_STREAM;
68
69 if (wrapped != null) {
70 processAndShip(wrapped);
71 wrapped.close();
72 }
73 }
74
75 private synchronized ContentWithType getEncrypted() {
76 try {
77 if (encrypted == null) {
78 final byte[] bytesEncryptedAndSigned = NtlmEncryptionUtils
79 .of(credentials)
80 .encryptAndSign(message, unencrypted.getBytes());
81
82 encrypted = ContentWithType.of(message, bytesEncryptedAndSigned);
83 }
84 return encrypted;
85 } catch (final IOException e) {
86 throw new IllegalStateException(e);
87 }
88 }
89
90 private byte[] getUnencrypted() {
91 try {
92 return unencrypted.getBytes();
93 } catch (final IOException e) {
94 throw new IllegalStateException(e);
95 }
96 }
97
98 synchronized ContentWithType getAppropriate() {
99 if (unencryptedResult == null) {
100 unencryptedResult = ContentWithType.of(message, null);
101 }
102
103 if (credentials == null || !credentials.isAuthenticated()) {
104 if (encrypted != null) {
105
106 encrypted = null;
107 }
108
109 return credentials != null && !credentials.isAuthenticated()
110 ? unencryptedResult.with(AsyncHttpEncryptionAwareConduit.PRE_AUTH_BOGUS_PAYLOAD)
111 : unencryptedResult.with(getUnencrypted());
112 }
113
114 return getEncrypted();
115 }
116
117 private void processAndShip(final OutputStream output) throws IOException {
118 output.write(getAppropriate().getPayload());
119 output.close();
120 }
121
122 @Override
123 public int hashCode() {
124 final int prime = 31;
125 int result = super.hashCode();
126 result = prime * result + Objects.hash(credentials, encrypted, message, unencrypted, unencryptedResult, wrapped);
127 return result;
128 }
129
130 @Override
131 public boolean equals(Object obj) {
132 if (this == obj) return true;
133 if (!super.equals(obj)) return false;
134 if (!(obj instanceof EncryptAndSignOutputStream)) return false;
135 EncryptAndSignOutputStream other = (EncryptAndSignOutputStream) obj;
136 return (
137 Objects.equals(credentials, other.credentials) &&
138 Objects.equals(encrypted, other.encrypted) &&
139 Objects.equals(message, other.message) &&
140 Objects.equals(unencrypted, other.unencrypted) &&
141 Objects.equals(unencryptedResult, other.unencryptedResult) &&
142 Objects.equals(wrapped, other.wrapped)
143 );
144 }
145 }