1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.handler.ssl;
17
18 import io.netty5.buffer.BufferUtil;
19 import io.netty5.channel.ChannelHandler;
20 import io.netty5.channel.ChannelHandlerContext;
21 import io.netty5.util.internal.ReflectionUtil;
22 import io.netty5.util.internal.SystemPropertyUtil;
23 import io.netty5.util.internal.logging.InternalLogger;
24 import io.netty5.util.internal.logging.InternalLoggerFactory;
25
26 import javax.crypto.SecretKey;
27 import javax.crypto.spec.SecretKeySpec;
28 import javax.net.ssl.SSLEngine;
29 import javax.net.ssl.SSLSession;
30 import java.lang.reflect.Field;
31
32
33
34
35
36
37
38 public abstract class SslMasterKeyHandler implements ChannelHandler {
39
40 private static final InternalLogger logger = InternalLoggerFactory.getInstance(SslMasterKeyHandler.class);
41
42
43
44
45 private static final Class<?> SSL_SESSIONIMPL_CLASS;
46
47
48
49
50 private static final Field SSL_SESSIONIMPL_MASTER_SECRET_FIELD;
51
52
53
54
55
56
57 public static final String SYSTEM_PROP_KEY = "io.netty5.ssl.masterKeyHandler";
58
59
60
61
62 private static final Throwable UNAVAILABILITY_CAUSE;
63
64 static {
65 Throwable cause;
66 Class<?> clazz = null;
67 Field field = null;
68 try {
69 clazz = Class.forName("sun.security.ssl.SSLSessionImpl");
70 field = clazz.getDeclaredField("masterSecret");
71 cause = ReflectionUtil.trySetAccessible(field, true);
72 } catch (Throwable e) {
73 cause = e;
74 if (logger.isTraceEnabled()) {
75 logger.debug("sun.security.ssl.SSLSessionImpl is unavailable.", e);
76 } else {
77 logger.debug("sun.security.ssl.SSLSessionImpl is unavailable: {}", e.getMessage());
78 }
79 }
80 UNAVAILABILITY_CAUSE = cause;
81 SSL_SESSIONIMPL_CLASS = clazz;
82 SSL_SESSIONIMPL_MASTER_SECRET_FIELD = field;
83 }
84
85
86
87
88 protected SslMasterKeyHandler() {
89 }
90
91
92
93
94
95 public static void ensureSunSslEngineAvailability() {
96 if (UNAVAILABILITY_CAUSE != null) {
97 throw new IllegalStateException(
98 "Failed to find SSLSessionImpl on classpath", UNAVAILABILITY_CAUSE);
99 }
100 }
101
102
103
104
105
106
107 public static Throwable sunSslEngineUnavailabilityCause() {
108 return UNAVAILABILITY_CAUSE;
109 }
110
111
112
113 public static boolean isSunSslEngineAvailable() {
114 return UNAVAILABILITY_CAUSE == null;
115 }
116
117
118
119
120
121
122 protected abstract void accept(SecretKey masterKey, SSLSession session);
123
124 @Override
125 public final void channelInboundEvent(ChannelHandlerContext ctx, Object evt) {
126
127 if (evt instanceof SslHandshakeCompletionEvent &&
128 ((SslHandshakeCompletionEvent) evt).isSuccess() && masterKeyHandlerEnabled()) {
129 final SslHandler handler = ctx.pipeline().get(SslHandler.class);
130 final SSLEngine engine = handler.engine();
131 final SSLSession sslSession = engine.getSession();
132
133
134 if (isSunSslEngineAvailable() && sslSession.getClass().equals(SSL_SESSIONIMPL_CLASS)) {
135 final SecretKey secretKey;
136 try {
137 secretKey = (SecretKey) SSL_SESSIONIMPL_MASTER_SECRET_FIELD.get(sslSession);
138 } catch (IllegalAccessException e) {
139 throw new IllegalArgumentException("Failed to access the field 'masterSecret' " +
140 "via reflection.", e);
141 }
142 accept(secretKey, sslSession);
143 } else if (OpenSsl.isAvailable() && engine instanceof ReferenceCountedOpenSslEngine) {
144 SecretKeySpec secretKey = ((ReferenceCountedOpenSslEngine) engine).masterKey();
145 accept(secretKey, sslSession);
146 }
147 }
148
149 ctx.fireChannelInboundEvent(evt);
150 }
151
152
153
154
155
156
157
158
159 protected boolean masterKeyHandlerEnabled() {
160 return SystemPropertyUtil.getBoolean(SYSTEM_PROP_KEY, false);
161 }
162
163
164
165
166
167
168
169
170 public static SslMasterKeyHandler newWireSharkSslMasterKeyHandler() {
171 return new WiresharkSslMasterKeyHandler();
172 }
173
174
175
176
177
178
179
180
181 private static final class WiresharkSslMasterKeyHandler extends SslMasterKeyHandler {
182
183 private static final InternalLogger wireshark_logger =
184 InternalLoggerFactory.getInstance("io.netty5.wireshark");
185
186 @Override
187 protected void accept(SecretKey masterKey, SSLSession session) {
188 if (masterKey.getEncoded().length != 48) {
189 throw new IllegalArgumentException("An invalid length master key was provided.");
190 }
191 final byte[] sessionId = session.getId();
192 wireshark_logger.warn("RSA Session-ID:{} Master-Key:{}",
193 BufferUtil.hexDump(sessionId).toLowerCase(),
194 BufferUtil.hexDump(masterKey.getEncoded()).toLowerCase());
195 }
196 }
197
198 }