File: SSLEngineService.java

package info (click to toggle)
openjdk-11 11.0.4%2B11-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 757,028 kB
  • sloc: java: 5,016,041; xml: 1,191,974; cpp: 934,731; ansic: 555,697; sh: 24,299; objc: 12,703; python: 3,602; asm: 3,415; makefile: 2,772; awk: 351; sed: 172; perl: 114; jsp: 24; csh: 3
file content (411 lines) | stat: -rw-r--r-- 15,253 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/*
 * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * @bug 6388456
 * @summary Need adjustable TLS max record size for interoperability
 *      with non-compliant stacks
 *
 * Helper class of SSL/TLS client/server communication.
 *
 * @author Xuelei Fan
 */

import javax.net.ssl.*;

import java.io.*;
import java.security.*;
import java.nio.*;
import java.nio.channels.*;

public class SSLEngineService {

    private static String keyStoreFile = "keystore";
    private static String trustStoreFile = "truststore";
    private static char[] passphrase = "passphrase".toCharArray();

    private String pathToStores;
    private String keyFilename;
    private String trustFilename;

    protected SSLEngineService() {
        init("../etc");
    }

    protected SSLEngineService(String pathToStores) {
        init(pathToStores);
    }

    private void init(String pathToStores) {
        this.pathToStores = pathToStores;
        this.keyFilename =
            System.getProperty("test.src", "./") + "/" + pathToStores +
                "/" + keyStoreFile;
        this.trustFilename =
            System.getProperty("test.src", "./") + "/" + pathToStores +
                "/" + trustStoreFile;
    }

    // deliver local application data.
    protected static void deliver(SSLEngine ssle, SocketChannel sc)
        throws Exception {

        // create buufer.
        int appBufferMax = ssle.getSession().getApplicationBufferSize();
        int netBufferMax = ssle.getSession().getPacketBufferSize();
        int length = appBufferMax * (Integer.SIZE / 8);

        // allocate more in order to check large packet
        ByteBuffer localAppData = ByteBuffer.allocate(length);

        // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW
        ByteBuffer localNetData = ByteBuffer.allocate(netBufferMax/2);

        // prepare local application data
        localAppData.putInt(length);
        for (int i = 1; i < appBufferMax; i++) {
            localAppData.putInt(i);
        }
        localAppData.flip();


        while (localAppData.hasRemaining()) {
            // empty the local network packet buffer.
            localNetData.clear();

            // generated local network packet.
            SSLEngineResult res = ssle.wrap(localAppData, localNetData);

            // checking status
            switch (res.getStatus()) {

            case OK :
                localNetData.flip();

                // send the network packet
                while (localNetData.hasRemaining()) {
                    if (sc.write(localNetData) < 0) {
                        throw new IOException("Unable write to socket channel");
                    }
                }

                if (res.getHandshakeStatus() ==
                        SSLEngineResult.HandshakeStatus.NEED_TASK) {
                    Runnable runnable;
                    while ((runnable = ssle.getDelegatedTask()) != null) {
                        runnable.run();
                    }
                }

                // detect large buffer
                if (res.bytesProduced() >= Short.MAX_VALUE) {
                    System.out.println("Generate a " +
                        res.bytesProduced() + " bytes large packet ");
                }
                break;

            case BUFFER_OVERFLOW :
                // maybe need to enlarge the local network packet buffer.
                int size = ssle.getSession().getPacketBufferSize();
                if (size > localNetData.capacity()) {
                    System.out.println("resize destination buffer upto " +
                                size + " bytes for BUFFER_OVERFLOW");
                    localNetData = enlargeBuffer(localNetData, size);
                }
                break;

            default : // BUFFER_UNDERFLOW or CLOSED :
                throw new IOException("Received invalid" + res.getStatus() +
                        "during transfer application data");
            }
        }
    }


    // receive peer application data.
    protected static void receive(SSLEngine ssle, SocketChannel sc)
        throws Exception {

        // create buufers.
        int appBufferMax = ssle.getSession().getApplicationBufferSize();
        int netBufferMax = ssle.getSession().getPacketBufferSize();

        // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW
        ByteBuffer peerAppData = ByteBuffer.allocate(appBufferMax/2);
        ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/2);
        int received = -1;

        boolean needToReadMore = true;
        while (received != 0) {
            if (needToReadMore) {
                if (ssle.isInboundDone() || sc.read(peerNetData) < 0) {
                    break;
                }
            }

            peerNetData.flip();
            SSLEngineResult res = ssle.unwrap(peerNetData, peerAppData);
            peerNetData.compact();

            // checking status
            switch (res.getStatus()) {

            case OK :
                if (res.getHandshakeStatus() ==
                        SSLEngineResult.HandshakeStatus.NEED_TASK) {
                    Runnable runnable;
                    while ((runnable = ssle.getDelegatedTask()) != null) {
                        runnable.run();
                    }
                }

                if (received < 0 && res.bytesProduced() < 4 ) {
                    break;
                }

                if (received < 0) {
                    received = peerAppData.getInt(0);
                }

                System.out.println("received " + peerAppData.position() +
                        " bytes client application data");
                System.out.println("\tcomsumed " + res.bytesConsumed() +
                        " byes network data");
                peerAppData.clear();

                received -= res.bytesProduced();

                // detect large buffer
                if (res.bytesConsumed() >= Short.MAX_VALUE) {
                    System.out.println("Consumes a " + res.bytesConsumed() +
                        " bytes large packet ");
                }

                needToReadMore = (peerNetData.position() > 0) ? false : true;

                break;

            case BUFFER_OVERFLOW :
                // maybe need to enlarge the peer application data buffer.
                int size = ssle.getSession().getApplicationBufferSize();
                if (size > peerAppData.capacity()) {
                    System.out.println("resize destination buffer upto " +
                        size + " bytes for BUFFER_OVERFLOW");
                    peerAppData = enlargeBuffer(peerAppData, size);
                }
                break;

            case BUFFER_UNDERFLOW :
                // maybe need to enlarge the peer network packet data buffer.
                size = ssle.getSession().getPacketBufferSize();
                if (size > peerNetData.capacity()) {
                    System.out.println("resize source buffer upto " + size +
                        " bytes for BUFFER_UNDERFLOW");
                    peerNetData = enlargeBuffer(peerNetData, size);
                }

                needToReadMore = true;
                break;

            default : // CLOSED :
                throw new IOException("Received invalid" + res.getStatus() +
                        "during transfer application data");
            }
        }
    }

    protected static void handshaking(SSLEngine ssle, SocketChannel sc,
            ByteBuffer additional) throws Exception {

        int appBufferMax = ssle.getSession().getApplicationBufferSize();
        int netBufferMax = ssle.getSession().getPacketBufferSize();

        // allocate less in order to check BUFFER_OVERFLOW/BUFFER_UNDERFLOW
        ByteBuffer localAppData = ByteBuffer.allocate(appBufferMax/10);
        ByteBuffer peerAppData = ByteBuffer.allocate(appBufferMax/10);
        ByteBuffer localNetData = ByteBuffer.allocate(netBufferMax/10);
        ByteBuffer peerNetData = ByteBuffer.allocate(netBufferMax/10);

        // begin handshake
        ssle.beginHandshake();
        SSLEngineResult.HandshakeStatus hs = ssle.getHandshakeStatus();

        // start handshaking from unwrap
        byte[] buffer = new byte[0xFF];
        boolean underflow = false;
        do {
            switch (hs) {

            case NEED_UNWRAP :
                if (peerNetData.position() == 0) {
                    if (additional != null && additional.hasRemaining()) {
                        do {
                            int len = Math.min(buffer.length,
                                                peerNetData.remaining());
                            len = Math.min(len, additional.remaining());
                            if (len != 0) {
                                additional.get(buffer, 0, len);
                                peerNetData.put(buffer, 0, len);
                            }
                        } while (peerNetData.remaining() > 0 &&
                                    additional.hasRemaining());
                    } else {
                        if (sc.read(peerNetData) < 0) {
                            ssle.closeInbound();
                            return;
                        }
                    }
                }

                if (underflow) {
                    if (sc.read(peerNetData) < 0) {
                        ssle.closeInbound();
                        return;
                    }

                    underflow = false;
                }

                peerNetData.flip();
                SSLEngineResult res = ssle.unwrap(peerNetData, peerAppData);
                peerNetData.compact();
                hs = res.getHandshakeStatus();

                switch (res.getStatus()) {
                case OK :
                    break;
                case BUFFER_UNDERFLOW :
                    // maybe need to enlarge the peer network packet buffer.
                    int size = ssle.getSession().getPacketBufferSize();
                    if (size > peerNetData.capacity()) {
                        System.out.println("resize source buffer upto " +
                                size + " bytes for BUFFER_UNDERFLOW");
                        peerNetData = enlargeBuffer(peerNetData, size);
                    }

                    underflow = true;
                    break;
                case BUFFER_OVERFLOW :
                    // maybe need to enlarge the peer application data buffer.
                    size = ssle.getSession().getApplicationBufferSize();
                    if (size > peerAppData.capacity()) {
                        System.out.println("resize destination buffer upto " +
                                size + " bytes for BUFFER_OVERFLOW");
                        peerAppData = enlargeBuffer(peerAppData, size);
                    }
                    break;
                default : //CLOSED
                    throw new IOException("Received invalid" + res.getStatus() +
                        "during initial handshaking");
                }
                break;

            case NEED_WRAP :
                // empty the local network packet buffer.
                localNetData.clear();

                // generated local network packet.
                res = ssle.wrap(localAppData, localNetData);
                hs = res.getHandshakeStatus();

                // checking status
                switch (res.getStatus()) {
                case OK :
                    localNetData.flip();

                    // send the network packet
                    while (localNetData.hasRemaining()) {
                        if (sc.write(localNetData) < 0) {
                            throw new IOException(
                                "Unable write to socket channel");
                        }
                    }
                    break;

                case BUFFER_OVERFLOW :
                    // maybe need to enlarge the local network packet buffer.
                    int size = ssle.getSession().getPacketBufferSize();
                    if (size > localNetData.capacity()) {
                        System.out.println("resize destination buffer upto " +
                                size + " bytes for BUFFER_OVERFLOW");
                        localNetData = enlargeBuffer(localNetData, size);
                    }
                    break;

                default : // BUFFER_UNDERFLOW or CLOSED :
                    throw new IOException("Received invalid" + res.getStatus() +
                        "during initial handshaking");
                }
                break;

            case NEED_TASK :
                Runnable runnable;
                while ((runnable = ssle.getDelegatedTask()) != null) {
                    runnable.run();
                }
                hs = ssle.getHandshakeStatus();
                break;

            default : // FINISHED or NOT_HANDSHAKING
                // do nothing
            }
        } while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
                hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING);
    }

    private static ByteBuffer enlargeBuffer(ByteBuffer buffer, int size) {
        ByteBuffer bb = ByteBuffer.allocate(size);
        buffer.flip();
        bb.put(buffer);

        return bb;
    }

    /*
     * Create an initialized SSLContext to use for this test.
     */
    protected SSLEngine createSSLEngine(boolean mode) throws Exception {

        SSLEngine ssle;

        KeyStore ks = KeyStore.getInstance("JKS");
        KeyStore ts = KeyStore.getInstance("JKS");

        ks.load(new FileInputStream(keyFilename), passphrase);
        ts.load(new FileInputStream(trustFilename), passphrase);

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, passphrase);

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(ts);

        SSLContext sslCtx = SSLContext.getInstance("TLS");
        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        ssle = sslCtx.createSSLEngine();
        ssle.setUseClientMode(mode);

        return ssle;
    }
}