1 /*
2  * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @bug 6388456
26  * @summary Need adjustable TLS max record size for interoperability
27  *      with non-compliant stacks
28  *
29  * Helper class of SSL/TLS client/server communication.
30  *
31  * @author Xuelei Fan
32  */
33 
34 import javax.net.ssl.*;
35 
36 import java.io.*;
37 import java.security.*;
38 import java.nio.*;
39 import java.nio.channels.*;
40 
41 public class SSLEngineService {
42 
43     private static String keyStoreFile = "keystore";
44     private static String trustStoreFile = "truststore";
45     private static char[] passphrase = "passphrase".toCharArray();
46 
47     private String pathToStores;
48     private String keyFilename;
49     private String trustFilename;
50 
SSLEngineService()51     protected SSLEngineService() {
52         init("../etc");
53     }
54 
SSLEngineService(String pathToStores)55     protected SSLEngineService(String pathToStores) {
56         init(pathToStores);
57     }
58 
init(String pathToStores)59     private void init(String pathToStores) {
60         this.pathToStores = pathToStores;
61         this.keyFilename =
62             System.getProperty("test.src", "./") + "/" + pathToStores +
63                 "/" + keyStoreFile;
64         this.trustFilename =
65             System.getProperty("test.src", "./") + "/" + pathToStores +
66                 "/" + trustStoreFile;
67     }
68 
69     // deliver local application data.
deliver(SSLEngine ssle, SocketChannel sc)70     protected static void deliver(SSLEngine ssle, SocketChannel sc)
71         throws Exception {
72 
73         // create buufer.
74         int appBufferMax = ssle.getSession().getApplicationBufferSize();
75         int netBufferMax = ssle.getSession().getPacketBufferSize();
76         int length = appBufferMax * (Integer.SIZE / 8);
77 
78         // allocate more in order to check large packet
79         ByteBuffer localAppData = ByteBuffer.allocate(length);
80 
81         // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW
82         ByteBuffer localNetData = ByteBuffer.allocate(netBufferMax/2);
83 
84         // prepare local application data
85         localAppData.putInt(length);
86         for (int i = 1; i < appBufferMax; i++) {
87             localAppData.putInt(i);
88         }
89         localAppData.flip();
90 
91 
92         while (localAppData.hasRemaining()) {
93             // empty the local network packet buffer.
94             localNetData.clear();
95 
96             // generated local network packet.
97             SSLEngineResult res = ssle.wrap(localAppData, localNetData);
98 
99             // checking status
100             switch (res.getStatus()) {
101 
102             case OK :
103                 localNetData.flip();
104 
105                 // send the network packet
106                 while (localNetData.hasRemaining()) {
107                     if (sc.write(localNetData) < 0) {
108                         throw new IOException("Unable write to socket channel");
109                     }
110                 }
111 
112                 if (res.getHandshakeStatus() ==
113                         SSLEngineResult.HandshakeStatus.NEED_TASK) {
114                     Runnable runnable;
115                     while ((runnable = ssle.getDelegatedTask()) != null) {
116                         runnable.run();
117                     }
118                 }
119 
120                 // detect large buffer
121                 if (res.bytesProduced() >= Short.MAX_VALUE) {
122                     System.out.println("Generate a " +
123                         res.bytesProduced() + " bytes large packet ");
124                 }
125                 break;
126 
127             case BUFFER_OVERFLOW :
128                 // maybe need to enlarge the local network packet buffer.
129                 int size = ssle.getSession().getPacketBufferSize();
130                 if (size > localNetData.capacity()) {
131                     System.out.println("resize destination buffer upto " +
132                                 size + " bytes for BUFFER_OVERFLOW");
133                     localNetData = enlargeBuffer(localNetData, size);
134                 }
135                 break;
136 
137             default : // BUFFER_UNDERFLOW or CLOSED :
138                 throw new IOException("Received invalid" + res.getStatus() +
139                         "during transfer application data");
140             }
141         }
142     }
143 
144 
145     // receive peer application data.
receive(SSLEngine ssle, SocketChannel sc)146     protected static void receive(SSLEngine ssle, SocketChannel sc)
147         throws Exception {
148 
149         // create buufers.
150         int appBufferMax = ssle.getSession().getApplicationBufferSize();
151         int netBufferMax = ssle.getSession().getPacketBufferSize();
152 
153         // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW
154         ByteBuffer peerAppData = ByteBuffer.allocate(appBufferMax/2);
155         ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/2);
156         int received = -1;
157 
158         boolean needToReadMore = true;
159         while (received != 0) {
160             if (needToReadMore) {
161                 if (ssle.isInboundDone() || sc.read(peerNetData) < 0) {
162                     break;
163                 }
164             }
165 
166             peerNetData.flip();
167             SSLEngineResult res = ssle.unwrap(peerNetData, peerAppData);
168             peerNetData.compact();
169 
170             // checking status
171             switch (res.getStatus()) {
172 
173             case OK :
174                 if (res.getHandshakeStatus() ==
175                         SSLEngineResult.HandshakeStatus.NEED_TASK) {
176                     Runnable runnable;
177                     while ((runnable = ssle.getDelegatedTask()) != null) {
178                         runnable.run();
179                     }
180                 }
181 
182                 if (received < 0 && res.bytesProduced() < 4 ) {
183                     break;
184                 }
185 
186                 if (received < 0) {
187                     received = peerAppData.getInt(0);
188                 }
189 
190                 System.out.println("received " + peerAppData.position() +
191                         " bytes client application data");
192                 System.out.println("\tcomsumed " + res.bytesConsumed() +
193                         " byes network data");
194                 peerAppData.clear();
195 
196                 received -= res.bytesProduced();
197 
198                 // detect large buffer
199                 if (res.bytesConsumed() >= Short.MAX_VALUE) {
200                     System.out.println("Consumes a " + res.bytesConsumed() +
201                         " bytes large packet ");
202                 }
203 
204                 needToReadMore = (peerNetData.position() > 0) ? false : true;
205 
206                 break;
207 
208             case BUFFER_OVERFLOW :
209                 // maybe need to enlarge the peer application data buffer.
210                 int size = ssle.getSession().getApplicationBufferSize();
211                 if (size > peerAppData.capacity()) {
212                     System.out.println("resize destination buffer upto " +
213                         size + " bytes for BUFFER_OVERFLOW");
214                     peerAppData = enlargeBuffer(peerAppData, size);
215                 }
216                 break;
217 
218             case BUFFER_UNDERFLOW :
219                 // maybe need to enlarge the peer network packet data buffer.
220                 size = ssle.getSession().getPacketBufferSize();
221                 if (size > peerNetData.capacity()) {
222                     System.out.println("resize source buffer upto " + size +
223                         " bytes for BUFFER_UNDERFLOW");
224                     peerNetData = enlargeBuffer(peerNetData, size);
225                 }
226 
227                 needToReadMore = true;
228                 break;
229 
230             default : // CLOSED :
231                 throw new IOException("Received invalid" + res.getStatus() +
232                         "during transfer application data");
233             }
234         }
235     }
236 
handshaking(SSLEngine ssle, SocketChannel sc, ByteBuffer additional)237     protected static void handshaking(SSLEngine ssle, SocketChannel sc,
238             ByteBuffer additional) throws Exception {
239 
240         int appBufferMax = ssle.getSession().getApplicationBufferSize();
241         int netBufferMax = ssle.getSession().getPacketBufferSize();
242 
243         // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW
244         ByteBuffer localAppData = ByteBuffer.allocate(appBufferMax/10);
245         ByteBuffer peerAppData = ByteBuffer.allocate(appBufferMax/10);
246         ByteBuffer localNetData = ByteBuffer.allocate(netBufferMax/10);
247         ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/10);
248 
249         // begin handshake
250         ssle.beginHandshake();
251         SSLEngineResult.HandshakeStatus hs = ssle.getHandshakeStatus();
252 
253         // start handshaking from unwrap
254         byte[] buffer = new byte[0xFF];
255         boolean underflow = false;
256         do {
257             switch (hs) {
258 
259             case NEED_UNWRAP :
260                 if (peerNetData.position() == 0) {
261                     if (additional != null && additional.hasRemaining()) {
262                         do {
263                             int len = Math.min(buffer.length,
264                                                 peerNetData.remaining());
265                             len = Math.min(len, additional.remaining());
266                             if (len != 0) {
267                                 additional.get(buffer, 0, len);
268                                 peerNetData.put(buffer, 0, len);
269                             }
270                         } while (peerNetData.remaining() > 0 &&
271                                     additional.hasRemaining());
272                     } else {
273                         if (sc.read(peerNetData) < 0) {
274                             ssle.closeInbound();
275                             return;
276                         }
277                     }
278                 }
279 
280                 if (underflow) {
281                     if (sc.read(peerNetData) < 0) {
282                         ssle.closeInbound();
283                         return;
284                     }
285 
286                     underflow = false;
287                 }
288 
289                 peerNetData.flip();
290                 SSLEngineResult res = ssle.unwrap(peerNetData, peerAppData);
291                 peerNetData.compact();
292                 hs = res.getHandshakeStatus();
293 
294                 switch (res.getStatus()) {
295                 case OK :
296                     break;
297                 case BUFFER_UNDERFLOW :
298                     // maybe need to enlarge the peer network packet buffer.
299                     int size = ssle.getSession().getPacketBufferSize();
300                     if (size > peerNetData.capacity()) {
301                         System.out.println("resize source buffer upto " +
302                                 size + " bytes for BUFFER_UNDERFLOW");
303                         peerNetData = enlargeBuffer(peerNetData, size);
304                     }
305 
306                     underflow = true;
307                     break;
308                 case BUFFER_OVERFLOW :
309                     // maybe need to enlarge the peer application data buffer.
310                     size = ssle.getSession().getApplicationBufferSize();
311                     if (size > peerAppData.capacity()) {
312                         System.out.println("resize destination buffer upto " +
313                                 size + " bytes for BUFFER_OVERFLOW");
314                         peerAppData = enlargeBuffer(peerAppData, size);
315                     }
316                     break;
317                 default : //CLOSED
318                     throw new IOException("Received invalid" + res.getStatus() +
319                         "during initial handshaking");
320                 }
321                 break;
322 
323             case NEED_WRAP :
324                 // empty the local network packet buffer.
325                 localNetData.clear();
326 
327                 // generated local network packet.
328                 res = ssle.wrap(localAppData, localNetData);
329                 hs = res.getHandshakeStatus();
330 
331                 // checking status
332                 switch (res.getStatus()) {
333                 case OK :
334                     localNetData.flip();
335 
336                     // send the network packet
337                     while (localNetData.hasRemaining()) {
338                         if (sc.write(localNetData) < 0) {
339                             throw new IOException(
340                                 "Unable write to socket channel");
341                         }
342                     }
343                     break;
344 
345                 case BUFFER_OVERFLOW :
346                     // maybe need to enlarge the local network packet buffer.
347                     int size = ssle.getSession().getPacketBufferSize();
348                     if (size > localNetData.capacity()) {
349                         System.out.println("resize destination buffer upto " +
350                                 size + " bytes for BUFFER_OVERFLOW");
351                         localNetData = enlargeBuffer(localNetData, size);
352                     }
353                     break;
354 
355                 default : // BUFFER_UNDERFLOW or CLOSED :
356                     throw new IOException("Received invalid" + res.getStatus() +
357                         "during initial handshaking");
358                 }
359                 break;
360 
361             case NEED_TASK :
362                 Runnable runnable;
363                 while ((runnable = ssle.getDelegatedTask()) != null) {
364                     runnable.run();
365                 }
366                 hs = ssle.getHandshakeStatus();
367                 break;
368 
369             default : // FINISHED or NOT_HANDSHAKING
370                 // do nothing
371             }
372         } while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
373                 hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING);
374     }
375 
enlargeBuffer(ByteBuffer buffer, int size)376     private static ByteBuffer enlargeBuffer(ByteBuffer buffer, int size) {
377         ByteBuffer bb = ByteBuffer.allocate(size);
378         buffer.flip();
379         bb.put(buffer);
380 
381         return bb;
382     }
383 
384     /*
385      * Create an initialized SSLContext to use for this test.
386      */
createSSLEngine(boolean mode)387     protected SSLEngine createSSLEngine(boolean mode) throws Exception {
388 
389         SSLEngine ssle;
390 
391         KeyStore ks = KeyStore.getInstance("JKS");
392         KeyStore ts = KeyStore.getInstance("JKS");
393 
394         ks.load(new FileInputStream(keyFilename), passphrase);
395         ts.load(new FileInputStream(trustFilename), passphrase);
396 
397         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
398         kmf.init(ks, passphrase);
399 
400         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
401         tmf.init(ts);
402 
403         SSLContext sslCtx = SSLContext.getInstance("TLS");
404         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
405 
406         ssle = sslCtx.createSSLEngine();
407         ssle.setUseClientMode(mode);
408 
409         return ssle;
410     }
411 }
412