1 /* SSLEngineImpl.java -- implementation of SSLEngine.
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is a part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.javax.net.ssl.provider;
41 import gnu.classpath.debug.Component;
42 import gnu.classpath.debug.SystemLogger;
44 import gnu.java.security.util.ByteBufferOutputStream;
45 import gnu.javax.net.ssl.Session;
46 import gnu.javax.net.ssl.SSLRecordHandler;
48 import java.nio.BufferOverflowException;
49 import java.nio.ByteBuffer;
50 import java.nio.ByteOrder;
52 import java.security.NoSuchAlgorithmException;
53 import java.util.ArrayList;
54 import java.util.List;
55 import java.util.zip.DataFormatException;
57 import javax.crypto.IllegalBlockSizeException;
58 import javax.crypto.ShortBufferException;
59 import javax.net.ssl.SSLEngine;
60 import javax.net.ssl.SSLEngineResult;
61 import javax.net.ssl.SSLException;
62 import javax.net.ssl.SSLSession;
63 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
64 import javax.net.ssl.SSLEngineResult.Status;
66 public final class SSLEngineImpl extends SSLEngine
68 final SSLContextImpl contextImpl;
69 private SSLRecordHandler[] handlers;
70 private static final SystemLogger logger = SystemLogger.SYSTEM;
71 private SessionImpl session;
72 private InputSecurityParameters insec;
73 private OutputSecurityParameters outsec;
74 private boolean inClosed;
75 private boolean outClosed;
76 private boolean createSessions;
77 private boolean needClientAuth;
78 private boolean wantClientAuth;
79 private boolean initialHandshakeDone;
80 private AbstractHandshake handshake;
81 private Alert lastAlert;
82 private SSLEngineResult.HandshakeStatus handshakeStatus;
83 private boolean changeCipherSpec;
85 private String[] enabledSuites;
86 private String[] enabledProtocols;
89 * We can receive any message chunked across multiple records,
90 * including alerts, even though all alert messages are only two
91 * bytes long. Handshake messages are de-chunked in the handshake
92 * handler, change-cipher-spec messages are always empty, and we
93 * don't care about chunking of application messages.
95 * This buffer will hold the incomplete alert that we receive, if
98 private final ByteBuffer alertBuffer;
102 private enum Mode { SERVER, CLIENT };
104 SSLEngineImpl (SSLContextImpl contextImpl, String host, int port)
107 this.contextImpl = contextImpl;
108 handlers = new SSLRecordHandler[256];
109 session = new SessionImpl();
110 session.suite = CipherSuite.TLS_NULL_WITH_NULL_NULL;
111 session.version = ProtocolVersion.TLS_1_1;
112 byte[] sid = new byte[32];
113 contextImpl.random.nextBytes(sid);
114 session.setId(new Session.ID(sid));
115 session.setRandom(contextImpl.random);
118 logger.logv(Component.SSL_RECORD_LAYER, "generated session ID {0} with random {1}",
119 session.id(), contextImpl.random);
121 // Begin with no encryption.
122 insec = new InputSecurityParameters (null, null, null, session,
123 CipherSuite.TLS_NULL_WITH_NULL_NULL);
124 outsec = new OutputSecurityParameters (null, null, null, session,
125 CipherSuite.TLS_NULL_WITH_NULL_NULL);
128 needClientAuth = false;
129 wantClientAuth = false;
130 createSessions = true;
131 initialHandshakeDone = false;
132 alertBuffer = ByteBuffer.wrap (new byte[2]);
135 handshakeStatus = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
136 changeCipherSpec = false;
138 // Set up default protocols and suites.
139 enabledProtocols = new String[] {
140 ProtocolVersion.TLS_1_1.toString(),
141 ProtocolVersion.TLS_1.toString(),
142 ProtocolVersion.SSL_3.toString()
144 enabledSuites = defaultSuites();
147 static String[] defaultSuites()
149 return new String[] {
150 CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA.toString(),
151 CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA.toString(),
152 CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA.toString(),
153 CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA.toString(),
154 CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA.toString(),
155 CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA.toString(),
156 CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA.toString(),
157 CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA.toString(),
158 CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA.toString(),
159 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.toString(),
160 CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
161 CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
162 CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA.toString(),
163 CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
164 CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA.toString(),
165 CipherSuite.TLS_RSA_WITH_RC4_128_MD5.toString(),
166 CipherSuite.TLS_RSA_WITH_RC4_128_SHA.toString(),
167 CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA.toString(),
168 CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA.toString(),
169 CipherSuite.TLS_DH_DSS_WITH_DES_CBC_SHA.toString(),
170 CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.toString(),
171 CipherSuite.TLS_RSA_WITH_DES_CBC_SHA.toString(),
172 CipherSuite.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
173 CipherSuite.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
174 CipherSuite.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
175 CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.toString(),
176 CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(),
177 CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(),
178 CipherSuite.TLS_RSA_WITH_NULL_MD5.toString(),
179 CipherSuite.TLS_RSA_WITH_NULL_SHA.toString()
184 /*public void registerHandler (final int contentType,
185 SSLRecordHandler handler)
188 if (type.equals (ContentType.CHANGE_CIPHER_SPEC)
189 || type.equals (ContentType.ALERT)
190 || type.equals (ContentType.HANDSHAKE)
191 || type.equals (ContentType.APPLICATION_DATA))
192 throw new SSLException ("can't override handler for content type " + type);
193 int i = type.getValue ();
194 if (i < 0 || i > 255)
195 throw new SSLException ("illegal content type: " + type);
196 handlers[i] = handler;
200 public void beginHandshake () throws SSLException
203 logger.log(Component.SSL_HANDSHAKE, "{0} handshake begins", mode);
206 throw new IllegalStateException("setUseClientMode was never used");
211 if (getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
212 throw new SSLException("handshake already in progress");
215 handshake = new ServerHandshake(initialHandshakeDone, this);
217 catch (NoSuchAlgorithmException nsae)
219 throw new SSLException(nsae);
226 handshake = new ClientHandshake(this);
228 catch (NoSuchAlgorithmException nsae)
230 throw new SSLException(nsae);
237 public void closeInbound()
243 public void closeOutbound()
245 lastAlert = new Alert(Alert.Level.WARNING, Alert.Description.CLOSE_NOTIFY);
249 public Runnable getDelegatedTask()
251 if (handshake == null)
253 return handshake.getTask();
257 public String[] getEnabledCipherSuites()
259 return (String[]) enabledSuites.clone();
263 public String[] getEnabledProtocols()
265 return (String[]) enabledProtocols.clone();
269 public boolean getEnableSessionCreation()
271 return createSessions;
275 public HandshakeStatus getHandshakeStatus()
277 if (handshake == null)
278 return HandshakeStatus.NOT_HANDSHAKING;
279 return handshake.status();
283 public boolean getNeedClientAuth()
285 return needClientAuth;
289 public SSLSession getSession()
295 public boolean getUseClientMode ()
297 return (mode == Mode.CLIENT);
301 public boolean getWantClientAuth()
303 return wantClientAuth;
307 public boolean isInboundDone()
313 public boolean isOutboundDone()
319 public void setEnableSessionCreation(final boolean createSessions)
321 this.createSessions = createSessions;
325 public void setEnabledCipherSuites(final String[] suites)
327 if (suites.length == 0)
328 throw new IllegalArgumentException("need at least one suite");
329 enabledSuites = (String[]) suites.clone();
333 public void setEnabledProtocols(final String[] protocols)
335 if (protocols.length == 0)
336 throw new IllegalArgumentException("need at least one protocol");
337 enabledProtocols = (String[]) protocols.clone();
341 public String[] getSupportedCipherSuites()
343 // XXX if we ever want to support "pluggable" cipher suites, we'll need
344 // to figure this out.
346 return CipherSuite.availableSuiteNames().toArray(new String[0]);
350 public String[] getSupportedProtocols()
352 return new String[] { ProtocolVersion.SSL_3.toString(),
353 ProtocolVersion.TLS_1.toString(),
354 ProtocolVersion.TLS_1_1.toString() };
358 public void setNeedClientAuth(final boolean needClientAuth)
360 this.needClientAuth = needClientAuth;
364 public void setUseClientMode (final boolean clientMode)
372 public @Override void setWantClientAuth(final boolean wantClientAuth)
374 this.wantClientAuth = wantClientAuth;
377 public @Override SSLEngineResult unwrap (final ByteBuffer source,
378 final ByteBuffer[] sinks,
379 final int offset, final int length)
383 throw new IllegalStateException ("setUseClientMode was never called");
386 return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
387 handshakeStatus, 0, 0);
389 if (source.remaining() < 5)
391 return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW,
392 handshakeStatus, 0, 0);
395 Record record = null;
396 boolean helloV2 = false;
398 // XXX: messages may be chunked across multiple records; does this
399 // include the SSLv2 message? I don't think it does, but we should
401 if (!getUseClientMode() && (source.get(source.position()) & 0x80) == 0x80)
403 if (handshake == null)
405 int hellolen = source.getShort(source.position()) & 0x7FFF;
406 this.handshake.handleV2Hello(source.slice());
407 if (!insec.cipherSuite().equals (CipherSuite.TLS_NULL_WITH_NULL_NULL))
408 throw new SSLException ("received SSLv2 client hello in encrypted "
409 + "session; this is invalid.");
411 logger.log (Component.SSL_RECORD_LAYER,
412 "converting SSLv2 client hello to version 3 hello");
414 source.getShort(); // skip length
415 ClientHelloV2 v2 = new ClientHelloV2(source.slice());
418 logger.log(Component.SSL_RECORD_LAYER, "v2 hello: {0}", v2);
420 List<CipherSuite> suites = v2.cipherSpecs();
422 ClientHelloBuilder hello = new ClientHelloBuilder();
423 hello.setVersion(v2.version ());
425 Random random = hello.random();
426 byte[] challenge = v2.challenge();
427 if (challenge.length < 32)
429 byte[] b = new byte[32];
430 System.arraycopy(challenge, 0, b, b.length - challenge.length,
434 random.setGmtUnixTime((challenge[0] & 0xFF) << 24
435 | (challenge[1] & 0xFF) << 16
436 | (challenge[2] & 0xFF) << 8
437 | (challenge[3] & 0xFF));
438 random.setRandomBytes(challenge, 4);
440 byte[] sessionId = v2.sessionId();
441 hello.setSessionId(sessionId, 0, sessionId.length);
442 hello.setCipherSuites(suites);
443 ArrayList<CompressionMethod> comps = new ArrayList<CompressionMethod>(1);
444 comps.add(CompressionMethod.NULL);
445 hello.setCompressionMethods(comps);
447 record = new Record(ByteBuffer.allocate(hello.length() + 9));
448 record.setContentType(ContentType.HANDSHAKE);
449 record.setVersion(v2.version());
450 record.setLength(hello.length() + 4);
452 Handshake handshake = new Handshake(record.fragment());
453 handshake.setLength(hello.length());
454 handshake.setType(Handshake.Type.CLIENT_HELLO);
456 handshake.bodyBuffer().put(hello.buffer());
457 source.position(source.position() + hellolen);
461 record = new Record(source);
463 ContentType type = record.contentType ();
466 logger.log(Component.SSL_RECORD_LAYER, "input record:\n{0}", record);
468 if (record.length() > session.getPacketBufferSize() - 5)
470 lastAlert = new Alert(Alert.Level.FATAL,
471 Alert.Description.RECORD_OVERFLOW);
472 throw new AlertException(lastAlert);
475 ByteBufferOutputStream sysMsg = null;
476 ByteBuffer msg = null;
481 // Application data will get decrypted directly into the user's
483 if (record.contentType() == ContentType.APPLICATION_DATA)
484 produced = insec.decrypt(record, sinks, offset, length);
487 if (insec.cipherSuite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
488 msg = record.fragment();
491 sysMsg = new ByteBufferOutputStream();
492 insec.decrypt(record, sysMsg);
496 // Advance the input buffer past the record we just read.
498 source.position(source.position() + record.length() + 5);
500 catch (BufferOverflowException boe)
502 // We throw this if the output buffers are not large enough; signal
503 // the caller about this.
504 logger.log(Component.SSL_RECORD_LAYER, "buffer overflow when decrypting", boe);
505 return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW,
506 handshakeStatus, 0, 0);
508 catch (IllegalBlockSizeException ibse)
510 lastAlert = new Alert(Alert.Level.FATAL,
511 Alert.Description.BAD_RECORD_MAC);
512 throw new AlertException(lastAlert, ibse);
514 catch (DataFormatException dfe)
516 lastAlert = new Alert(Alert.Level.FATAL,
517 Alert.Description.DECOMPRESSION_FAILURE);
518 throw new AlertException(lastAlert, dfe);
520 catch (MacException me)
522 lastAlert = new Alert(Alert.Level.FATAL,
523 Alert.Description.BAD_RECORD_MAC);
524 throw new AlertException(lastAlert, me);
526 catch (ShortBufferException sbe)
528 // We've messed up if this happens.
529 lastAlert = new Alert(Alert.Level.FATAL,
530 Alert.Description.INTERNAL_ERROR);
531 throw new AlertException(lastAlert, sbe);
534 SSLEngineResult result = null;
536 // If we need to handle the output here, do it. Otherwise, the output
537 // has been stored in the supplied output buffers.
541 logger.logv(Component.SSL_RECORD_LAYER, "sysmessage {0}", sysMsg);
542 msg = sysMsg.buffer();
545 if (type == ContentType.CHANGE_CIPHER_SPEC)
547 // We *may* get a partial message, even though the message is only
549 if (msg.remaining() == 0)
551 result = new SSLEngineResult (SSLEngineResult.Status.OK,
553 record.length() + 5, 0);
559 throw new SSLException ("unknown ChangeCipherSpec value: " + (b & 0xFF));
560 InputSecurityParameters params = handshake.getInputParams();
561 logger.log (Component.SSL_RECORD_LAYER,
562 "switching to input security parameters {0}",
563 params.cipherSuite());
565 result = new SSLEngineResult (SSLEngineResult.Status.OK,
567 record.length() + 5, 0);
570 else if (type == ContentType.ALERT)
573 if (alertBuffer.position() > 0)
575 alertBuffer.put(msg.get());
579 logger.logv(Component.SSL_RECORD_LAYER, "processing alerts {0}",
580 Util.wrapBuffer(msg));
581 len += msg.remaining() / 2;
582 Alert[] alerts = new Alert[len];
584 if (alertBuffer.position() > 0)
587 alerts[0] = new Alert(alertBuffer);
590 while (i < alerts.length)
592 alerts[i++] = new Alert(msg.duplicate());
593 msg.position(msg.position() + 2);
596 logger.logv(Component.SSL_RECORD_LAYER, "alerts: {0}", alerts.length);
598 for (i = 0; i < alerts.length; i++)
600 if (alerts[i].level() == Alert.Level.FATAL)
601 throw new AlertException(alerts[i], false);
602 if (alerts[i].description() != Alert.Description.CLOSE_NOTIFY)
603 logger.log(java.util.logging.Level.WARNING,
604 "received alert: {0}", alerts[i]);
605 if (alerts[i].description() == Alert.Description.CLOSE_NOTIFY)
609 if (msg.hasRemaining())
610 alertBuffer.position(0).limit(2);
612 result = new SSLEngineResult (SSLEngineResult.Status.OK,
614 record.length() + 5, 0);
616 else if (type == ContentType.HANDSHAKE)
618 if (handshake == null)
622 handshakeStatus = handshake.handleInput(msg);
624 catch (AlertException ae)
626 lastAlert = ae.alert();
627 return new SSLEngineResult(SSLEngineResult.Status.OK,
628 SSLEngineResult.HandshakeStatus.NEED_WRAP,
632 logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}", handshakeStatus);
633 result = new SSLEngineResult(SSLEngineResult.Status.OK,
637 if (handshakeStatus == HandshakeStatus.FINISHED)
640 handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
643 else if (type == ContentType.APPLICATION_DATA)
645 // Do nothing more; the application data has been put into
646 // the output buffers.
647 result = new SSLEngineResult(SSLEngineResult.Status.OK,
654 SSLRecordHandler handler = handlers[type.getValue()];
657 result = new SSLEngineResult(SSLEngineResult.Status.OK,
663 throw new SSLException ("unknown content type: " + type);
667 logger.logv(Component.SSL_RECORD_LAYER, "return result: {0}", result);
672 public @Override SSLEngineResult wrap (ByteBuffer[] sources, int offset, int length,
677 throw new IllegalStateException ("setUseClientMode was never called");
680 return new SSLEngineResult(SSLEngineResult.Status.CLOSED,
681 handshakeStatus, 0, 0);
683 ContentType type = null;
684 ByteBuffer sysMessage = null;
686 logger.logv(Component.SSL_RECORD_LAYER, "wrap {0} {1} {2} {3} / {4}",
687 sources, offset, length, sink, getHandshakeStatus());
688 if (lastAlert != null)
690 type = ContentType.ALERT;
691 sysMessage = ByteBuffer.allocate(2);
692 Alert alert = new Alert(sysMessage);
693 alert.setDescription(lastAlert.description());
694 alert.setLevel(lastAlert.level());
695 if (lastAlert.description() == Alert.Description.CLOSE_NOTIFY)
698 else if (changeCipherSpec)
700 type = ContentType.CHANGE_CIPHER_SPEC;
701 sysMessage = ByteBuffer.allocate(1);
702 sysMessage.put(0, (byte) 1);
704 else if (getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP)
706 // If we are not encrypting, optimize the handshake to fill
707 // the buffer directly.
708 if (outsec.suite() == CipherSuite.TLS_NULL_WITH_NULL_NULL)
710 int orig = sink.position();
711 sink.order(ByteOrder.BIG_ENDIAN);
712 sink.put((byte) ContentType.HANDSHAKE.getValue());
713 sink.putShort((short) session.version.rawValue());
714 sink.putShort((short) 0);
715 handshakeStatus = handshake.handleOutput(sink);
716 int produced = sink.position() - orig;
717 sink.putShort(orig + 3, (short) (produced - 5));
719 logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
720 new Record((ByteBuffer) sink.duplicate().position(orig)));
721 SSLEngineResult result = new SSLEngineResult(SSLEngineResult.Status.OK,
722 handshakeStatus, 0, produced);
724 // Note, this will only happen if we transition from
725 // TLS_NULL_WITH_NULL_NULL *to* TLS_NULL_WITH_NULL_NULL, which
726 // doesn't make a lot of sense, but we support it anyway.
727 if (handshakeStatus == HandshakeStatus.FINISHED)
729 handshake = null; // finished with it.
730 handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
735 // Rough guideline; XXX.
736 sysMessage = ByteBuffer.allocate(sink.remaining() - 2048);
737 type = ContentType.HANDSHAKE;
740 handshakeStatus = handshake.handleOutput(sysMessage);
742 catch (AlertException ae)
744 lastAlert = ae.alert();
745 return new SSLEngineResult(Status.OK,
746 HandshakeStatus.NEED_WRAP, 0, 0);
750 logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}",
759 int orig = sink.position();
761 if (sysMessage != null)
764 logger.logv(Component.SSL_RECORD_LAYER, "encrypt system message {0} to {1}", sysMessage, sink);
765 inout = outsec.encrypt(new ByteBuffer[] { sysMessage }, 0, 1,
771 inout = outsec.encrypt(sources, offset, length,
772 ContentType.APPLICATION_DATA, sink);
778 logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
779 new Record((ByteBuffer) sink.duplicate().position(orig).limit(produced)));
781 catch (ShortBufferException sbe)
783 // We don't expect this to happen, except for bugs; signal an
785 lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
786 return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
788 catch (IllegalBlockSizeException ibse)
790 // We don't expect this to happen, except for bugs; signal an
792 lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
793 return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
795 catch (DataFormatException dfe)
797 // We don't expect this to happen; signal an internal error.
798 lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR);
799 return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0);
802 if (lastAlert != null && lastAlert.level() == Alert.Level.FATAL)
804 AlertException ae = new AlertException(lastAlert);
809 if (changeCipherSpec)
811 outsec = handshake.getOutputParams();
812 changeCipherSpec = false;
814 SSLEngineResult result
815 = new SSLEngineResult(outClosed ? SSLEngineResult.Status.CLOSED
816 : SSLEngineResult.Status.OK,
817 handshakeStatus, consumed, produced);
818 if (handshakeStatus == HandshakeStatus.FINISHED)
820 handshake = null; // done with it.
821 handshakeStatus = HandshakeStatus.NOT_HANDSHAKING;
826 // Package-private methods.
828 SessionImpl session ()
833 void setSession(SessionImpl session)
835 this.session = session;
838 void changeCipherSpec()
840 changeCipherSpec = true;