View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.ssl;
21  
22  import java.net.InetSocketAddress;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import javax.net.ssl.SSLContext;
27  import javax.net.ssl.SSLEngine;
28  import javax.net.ssl.SSLException;
29  import javax.net.ssl.SSLHandshakeException;
30  import javax.net.ssl.SSLSession;
31  
32  import org.apache.mina.core.buffer.IoBuffer;
33  import org.apache.mina.core.filterchain.IoFilterAdapter;
34  import org.apache.mina.core.filterchain.IoFilterChain;
35  import org.apache.mina.core.future.DefaultWriteFuture;
36  import org.apache.mina.core.future.IoFuture;
37  import org.apache.mina.core.future.IoFutureListener;
38  import org.apache.mina.core.future.WriteFuture;
39  import org.apache.mina.core.service.IoHandler;
40  import org.apache.mina.core.session.AttributeKey;
41  import org.apache.mina.core.session.IoSession;
42  import org.apache.mina.core.write.WriteRequest;
43  import org.apache.mina.core.write.WriteRequestWrapper;
44  import org.apache.mina.core.write.WriteToClosedSessionException;
45  
46  /**
47   * An SSL filter that encrypts and decrypts the data exchanged in the session.
48   * Adding this filter triggers SSL handshake procedure immediately by sending
49   * a SSL 'hello' message, so you don't need to call
50   * {@link #startSsl(IoSession)} manually unless you are implementing StartTLS
51   * (see below).  If you don't want the handshake procedure to start
52   * immediately, please specify {@code false} as {@code autoStart} parameter in
53   * the constructor.
54   * <p>
55   * This filter uses an {@link SSLEngine} which was introduced in Java 5, so
56   * Java version 5 or above is mandatory to use this filter. And please note that
57   * this filter only works for TCP/IP connections.
58   * <p>
59   *
60   * <h2>Implementing StartTLS</h2>
61   * <p>
62   * You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS:
63   * <pre>
64   * public void messageReceived(IoSession session, Object message) {
65   *    if (message instanceof MyStartTLSRequest) {
66   *        // Insert SSLFilter to get ready for handshaking
67   *        session.getFilterChain().addFirst(sslFilter);
68   *
69   *        // Disable encryption temporarilly.
70   *        // This attribute will be removed by SSLFilter
71   *        // inside the Session.write() call below.
72   *        session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
73   *
74   *        // Write StartTLSResponse which won't be encrypted.
75   *        session.write(new MyStartTLSResponse(OK));
76   *
77   *        // Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
78   *        assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
79   *    }
80   * }
81   * </pre>
82   *
83   * @author The Apache MINA Project (dev@mina.apache.org)
84   * @version $Rev: 713366 $, $Date: 2008-11-12 14:39:23 +0100 (Wed, 12 Nov 2008) $
85   * @org.apache.xbean.XBean
86   */
87  public class SslFilter extends IoFilterAdapter {
88      /**
89       * A session attribute key that stores underlying {@link SSLSession}
90       * for each session.
91       */
92      public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
93  
94      /**
95       * A session attribute key that makes next one write request bypass
96       * this filter (not encrypting the data).  This is a marker attribute,
97       * which means that you can put whatever as its value. ({@link Boolean#TRUE}
98       * is preferred.)  The attribute is automatically removed from the session
99       * attribute map as soon as {@link IoSession#write(Object)} is invoked,
100      * and therefore should be put again if you want to make more messages
101      * bypass this filter.  This is especially useful when you implement
102      * StartTLS.
103      */
104     public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
105 
106     /**
107      * A session attribute key that makes this filter to emit a
108      * {@link IoHandler#messageReceived(IoSession, Object)} event with a
109      * special message ({@link #SESSION_SECURED} or {@link #SESSION_UNSECURED}).
110      * This is a marker attribute, which means that you can put whatever as its
111      * value. ({@link Boolean#TRUE} is preferred.)  By default, this filter
112      * doesn't emit any events related with SSL session flow control.
113      */
114     public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
115 
116     /**
117      * A session attribute key that should be set to an {@link InetSocketAddress}.
118      * Setting this attribute causes
119      * {@link SSLContext#createSSLEngine(String, int)} to be called passing the
120      * hostname and port of the {@link InetSocketAddress} to get an
121      * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()}
122      * will be called.<br/>
123      * Using this feature {@link SSLSession} objects may be cached and reused
124      * when in client mode.
125      *
126      * @see SSLContext#createSSLEngine(String, int)
127      */
128     public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
129 
130     /**
131      * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)}
132      * event when the session is secured and its {@link #USE_NOTIFICATION}
133      * attribute is set.
134      */
135     public static final SslFilterMessage SESSION_SECURED = new SslFilterMessage(
136             "SESSION_SECURED");
137 
138     /**
139      * A special message object which is emitted with a {@link IoHandler#messageReceived(IoSession, Object)}
140      * event when the session is not secure anymore and its {@link #USE_NOTIFICATION}
141      * attribute is set.
142      */
143     public static final SslFilterMessage SESSION_UNSECURED = new SslFilterMessage(
144             "SESSION_UNSECURED");
145 
146     private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
147     private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
148 
149     /** The SslContext used */
150     private final SSLContext sslContext;
151 
152     /** A flag used to tell the filter to start the handshake immediately */
153     private final boolean autoStart;
154     
155     /** A flag used to determinate if the handshake should start immediately */
156     private static final boolean START_HANDSHAKE = true;
157 
158     private boolean client;
159 
160     private boolean needClientAuth;
161 
162     private boolean wantClientAuth;
163 
164     private String[] enabledCipherSuites;
165 
166     private String[] enabledProtocols;
167 
168     /**
169      * Creates a new SSL filter using the specified {@link SSLContext}.
170      * The handshake will start immediately.
171      */
172     public SslFilter(SSLContext sslContext) {
173         this(sslContext, START_HANDSHAKE);
174     }
175 
176     /**
177      * Creates a new SSL filter using the specified {@link SSLContext}.
178      * If the <code>autostart</code> flag is set to <code>true</code>, the
179      * handshake will start immediately.
180      */
181     public SslFilter(SSLContext sslContext, boolean autoStart) {
182         if (sslContext == null) {
183             throw new NullPointerException("sslContext");
184         }
185 
186         this.sslContext = sslContext;
187         this.autoStart = autoStart;
188     }
189 
190     /**
191      * Returns the underlying {@link SSLSession} for the specified session.
192      *
193      * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
194      */
195     public SSLSession getSslSession(IoSession session) {
196         return (SSLSession) session.getAttribute(SSL_SESSION);
197     }
198 
199     /**
200      * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
201      * Please note that SSL session is automatically started by default, and therefore
202      * you don't need to call this method unless you've used TLS closure.
203      *
204      * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started.
205      * @throws SSLException if failed to start the SSL session
206      */
207     public boolean startSsl(IoSession session) throws SSLException {
208         SslHandler handler = getSslSessionHandler(session);
209         boolean started;
210         synchronized (handler) {
211             if (handler.isOutboundDone()) {
212                 NextFilter nextFilter = (NextFilter) session
213                         .getAttribute(NEXT_FILTER);
214                 handler.destroy();
215                 handler.init();
216                 handler.handshake(nextFilter);
217                 started = true;
218             } else {
219                 started = false;
220             }
221         }
222 
223         handler.flushScheduledEvents();
224         return started;
225     }
226 
227     /**
228      * Returns <tt>true</tt> if and only if the specified <tt>session</tt> is
229      * encrypted/decrypted over SSL/TLS currently.  This method will start
230      * to retun <tt>false</tt> after TLS <tt>close_notify</tt> message
231      * is sent and any messages written after then is not goinf to get encrypted.
232      */
233     public boolean isSslStarted(IoSession session) {
234         SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
235         
236         if (handler == null) {
237             return false;
238         }
239 
240         synchronized (handler) {
241             return !handler.isOutboundDone();
242         }
243     }
244 
245     /**
246      * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
247      * initiate TLS closure.
248      *
249      * @param session the {@link IoSession} to initiate TLS closure
250      * @throws SSLException if failed to initiate TLS closure
251      * @throws IllegalArgumentException if this filter is not managing the specified session
252      */
253     public WriteFuture stopSsl(IoSession session) throws SSLException {
254         SslHandler handler = getSslSessionHandler(session);
255         NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
256         WriteFuture future;
257         synchronized (handler) {
258             future = initiateClosure(nextFilter, session);
259         }
260 
261         handler.flushScheduledEvents();
262 
263         return future;
264     }
265 
266     /**
267      * Returns <tt>true</tt> if the engine is set to use client mode
268      * when handshaking.
269      */
270     public boolean isUseClientMode() {
271         return client;
272     }
273 
274     /**
275      * Configures the engine to use client (or server) mode when handshaking.
276      */
277     public void setUseClientMode(boolean clientMode) {
278         this.client = clientMode;
279     }
280 
281     /**
282      * Returns <tt>true</tt> if the engine will <em>require</em> client authentication.
283      * This option is only useful to engines in the server mode.
284      */
285     public boolean isNeedClientAuth() {
286         return needClientAuth;
287     }
288 
289     /**
290      * Configures the engine to <em>require</em> client authentication.
291      * This option is only useful for engines in the server mode.
292      */
293     public void setNeedClientAuth(boolean needClientAuth) {
294         this.needClientAuth = needClientAuth;
295     }
296 
297     /**
298      * Returns <tt>true</tt> if the engine will <em>request</em> client authentication.
299      * This option is only useful to engines in the server mode.
300      */
301     public boolean isWantClientAuth() {
302         return wantClientAuth;
303     }
304 
305     /**
306      * Configures the engine to <em>request</em> client authentication.
307      * This option is only useful for engines in the server mode.
308      */
309     public void setWantClientAuth(boolean wantClientAuth) {
310         this.wantClientAuth = wantClientAuth;
311     }
312 
313     /**
314      * Returns the list of cipher suites to be enabled when {@link SSLEngine}
315      * is initialized.
316      *
317      * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.'
318      */
319     public String[] getEnabledCipherSuites() {
320         return enabledCipherSuites;
321     }
322 
323     /**
324      * Sets the list of cipher suites to be enabled when {@link SSLEngine}
325      * is initialized.
326      *
327      * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s default.'
328      */
329     public void setEnabledCipherSuites(String[] cipherSuites) {
330         this.enabledCipherSuites = cipherSuites;
331     }
332 
333     /**
334      * Returns the list of protocols to be enabled when {@link SSLEngine}
335      * is initialized.
336      *
337      * @return <tt>null</tt> means 'use {@link SSLEngine}'s default.'
338      */
339     public String[] getEnabledProtocols() {
340         return enabledProtocols;
341     }
342 
343     /**
344      * Sets the list of protocols to be enabled when {@link SSLEngine}
345      * is initialized.
346      *
347      * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s default.'
348      */
349     public void setEnabledProtocols(String[] protocols) {
350         this.enabledProtocols = protocols;
351     }
352 
353     @Override
354     public void onPreAdd(IoFilterChain parent, String name,
355             NextFilter nextFilter) throws SSLException {
356         if (parent.contains(SslFilter.class)) {
357             throw new IllegalStateException(
358                     "Only one " + SslFilter.class.getName() + " is permitted.");
359         }
360 
361         IoSession session = parent.getSession();
362         session.setAttribute(NEXT_FILTER, nextFilter);
363 
364         // Create an SSL handler and start handshake.
365         SslHandler handler = new SslHandler(this, sslContext, session);
366         session.setAttribute(SSL_HANDLER, handler);
367     }
368 
369     @Override
370     public void onPostAdd(IoFilterChain parent, String name,
371             NextFilter nextFilter) throws SSLException {
372         if (autoStart) {
373             initiateHandshake(nextFilter, parent.getSession());
374         }
375     }
376 
377     @Override
378     public void onPreRemove(IoFilterChain parent, String name,
379             NextFilter nextFilter) throws SSLException {
380         IoSession session = parent.getSession();
381         stopSsl(session);
382         session.removeAttribute(NEXT_FILTER);
383         session.removeAttribute(SSL_HANDLER);
384     }
385 
386     // IoFilter impl.
387     @Override
388     public void sessionClosed(NextFilter nextFilter, IoSession session)
389             throws SSLException {
390         SslHandler handler = getSslSessionHandler(session);
391         try {
392             synchronized (handler) {
393                 // release resources
394                 handler.destroy();
395             }
396 
397             handler.flushScheduledEvents();
398         } finally {
399             // notify closed session
400             nextFilter.sessionClosed(session);
401         }
402     }
403 
404     @Override
405     public void messageReceived(NextFilter nextFilter, IoSession session,
406             Object message) throws SSLException {
407         SslHandler handler = getSslSessionHandler(session);
408         synchronized (handler) {
409             if (!isSslStarted(session) && handler.isInboundDone()) {
410                 handler.scheduleMessageReceived(nextFilter, message);
411             } else {
412                 IoBuffer buf = (IoBuffer) message;
413                 try {
414                     // forward read encrypted data to SSL handler
415                     handler.messageReceived(nextFilter, buf.buf());
416 
417                     // Handle data to be forwarded to application or written to net
418                     handleSslData(nextFilter, handler);
419 
420                     if (handler.isInboundDone()) {
421                         if (handler.isOutboundDone()) {
422                             handler.destroy();
423                         } else {
424                             initiateClosure(nextFilter, session);
425                         }
426 
427                         if (buf.hasRemaining()) {
428                             // Forward the data received after closure.
429                             handler.scheduleMessageReceived(nextFilter, buf);
430                         }
431                     }
432                 } catch (SSLException ssle) {
433                     if (!handler.isHandshakeComplete()) {
434                         SSLException newSsle = new SSLHandshakeException(
435                                 "SSL handshake failed.");
436                         newSsle.initCause(ssle);
437                         ssle = newSsle;
438                     }
439 
440                     throw ssle;
441                 }
442             }
443         }
444 
445         handler.flushScheduledEvents();
446     }
447 
448     @Override
449     public void messageSent(NextFilter nextFilter, IoSession session,
450             WriteRequest writeRequest) {
451         if (writeRequest instanceof EncryptedWriteRequest) {
452             EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
453             nextFilter.messageSent(session, wrappedRequest.getParentRequest());
454         } else {
455             // ignore extra buffers used for handshaking
456         }
457     }
458 
459     @Override
460     public void exceptionCaught(NextFilter nextFilter, IoSession session,
461             Throwable cause) throws Exception {
462 
463         if (cause instanceof WriteToClosedSessionException) {
464             // Filter out SSL close notify, which is likely to fail to flush
465             // due to disconnection.
466             WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
467             List<WriteRequest> failedRequests = e.getRequests();
468             boolean containsCloseNotify = false;
469             for (WriteRequest r: failedRequests) {
470                 if (isCloseNotify(r.getMessage())) {
471                     containsCloseNotify = true;
472                     break;
473                 }
474             }
475             
476             if (containsCloseNotify) {
477                 if (failedRequests.size() == 1) {
478                     // close notify is the only failed request; bail out.
479                     return;
480                 }
481                 
482                 List<WriteRequest> newFailedRequests =
483                     new ArrayList<WriteRequest>(failedRequests.size() - 1);
484                 for (WriteRequest r: failedRequests) {
485                     if (!isCloseNotify(r.getMessage())) {
486                         newFailedRequests.add(r);
487                     }
488                 }
489                 
490                 if (newFailedRequests.isEmpty()) {
491                     // the failedRequests were full with close notify; bail out.
492                     return;
493                 }
494                 
495                 cause = new WriteToClosedSessionException(
496                         newFailedRequests, cause.getMessage(), cause.getCause());
497             }
498         }
499         
500         nextFilter.exceptionCaught(session, cause);
501     }
502         
503     private boolean isCloseNotify(Object message) {
504         if (!(message instanceof IoBuffer)) {
505             return false;
506         }
507         
508         IoBuffer buf = (IoBuffer) message;
509         int offset = buf.position();
510         return buf.remaining() == 23 &&
511                buf.get(offset + 0) == 0x15 && buf.get(offset + 1) == 0x03 &&
512                buf.get(offset + 2) == 0x01 && buf.get(offset + 3) == 0x00 &&
513                buf.get(offset + 4) == 0x12;
514     }
515 
516     @Override
517     public void filterWrite(NextFilter nextFilter, IoSession session,
518             WriteRequest writeRequest) throws SSLException {
519         boolean needsFlush = true;
520         SslHandler handler = getSslSessionHandler(session);
521         synchronized (handler) {
522             if (!isSslStarted(session)) {
523                 handler.scheduleFilterWrite(nextFilter,
524                         writeRequest);
525             }
526             // Don't encrypt the data if encryption is disabled.
527             else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
528                 // Remove the marker attribute because it is temporary.
529                 session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
530                 handler.scheduleFilterWrite(nextFilter,
531                         writeRequest);
532             } else {
533                 // Otherwise, encrypt the buffer.
534                 IoBuffer buf = (IoBuffer) writeRequest.getMessage();
535 
536                 if (handler.isWritingEncryptedData()) {
537                     // data already encrypted; simply return buffer
538                     handler.scheduleFilterWrite(nextFilter, writeRequest);
539                 } else if (handler.isHandshakeComplete()) {
540                     // SSL encrypt
541                     int pos = buf.position();
542                     handler.encrypt(buf.buf());
543                     buf.position(pos);
544                     IoBuffer encryptedBuffer = handler.fetchOutNetBuffer();
545                     handler.scheduleFilterWrite(
546                             nextFilter,
547                             new EncryptedWriteRequest(
548                                     writeRequest, encryptedBuffer));
549                 } else {
550                     if (session.isConnected()) {
551                         // Handshake not complete yet.
552                         handler.schedulePreHandshakeWriteRequest(nextFilter,
553                                 writeRequest);
554                     }
555                     needsFlush = false;
556                 }
557             }
558         }
559 
560         if (needsFlush) {
561             handler.flushScheduledEvents();
562         }
563     }
564 
565     @Override
566     public void filterClose(final NextFilter nextFilter, final IoSession session)
567             throws SSLException {
568         SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
569         if (handler == null) {
570             // The connection might already have closed, or
571             // SSL might have not started yet.
572             nextFilter.filterClose(session);
573             return;
574         }
575 
576         WriteFuture future = null;
577         try {
578             synchronized (handler) {
579                 if (isSslStarted(session)) {
580                     future = initiateClosure(nextFilter, session);
581                     future.addListener(new IoFutureListener<IoFuture>() {
582                         public void operationComplete(IoFuture future) {
583                             nextFilter.filterClose(session);
584                         }
585                     });
586                 }
587             }
588 
589             handler.flushScheduledEvents();
590         } finally {
591             if (future == null) {
592                 nextFilter.filterClose(session);
593             }
594         }
595     }
596 
597     private void initiateHandshake(NextFilter nextFilter, IoSession session)
598             throws SSLException {
599         SslHandler handler = getSslSessionHandler(session);
600         
601         synchronized (handler) {
602             handler.handshake(nextFilter);
603         }
604         
605         handler.flushScheduledEvents();
606     }
607 
608     private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session)
609             throws SSLException {
610         SslHandler handler = getSslSessionHandler(session);
611         // if already shut down
612         if (!handler.closeOutbound()) {
613             return DefaultWriteFuture.newNotWrittenFuture(
614                     session, new IllegalStateException("SSL session is shut down already."));
615         }
616 
617         // there might be data to write out here?
618         WriteFuture future = handler.writeNetBuffer(nextFilter);
619         if (future == null) {
620             future = DefaultWriteFuture.newWrittenFuture(session);
621         }
622 
623         if (handler.isInboundDone()) {
624             handler.destroy();
625         }
626 
627         if (session.containsAttribute(USE_NOTIFICATION)) {
628             handler.scheduleMessageReceived(nextFilter, SESSION_UNSECURED);
629         }
630 
631         return future;
632     }
633 
634     // Utiliities
635 
636     private void handleSslData(NextFilter nextFilter, SslHandler handler)
637             throws SSLException {
638         // Flush any buffered write requests occurred before handshaking.
639         if (handler.isHandshakeComplete()) {
640             handler.flushPreHandshakeEvents();
641         }
642 
643         // Write encrypted data to be written (if any)
644         handler.writeNetBuffer(nextFilter);
645 
646         // handle app. data read (if any)
647         handleAppDataRead(nextFilter, handler);
648     }
649 
650     private void handleAppDataRead(NextFilter nextFilter, SslHandler handler) {
651         // forward read app data
652         IoBuffer readBuffer = handler.fetchAppBuffer();
653         if (readBuffer.hasRemaining()) {
654             handler.scheduleMessageReceived(nextFilter, readBuffer);
655         }
656     }
657 
658     private SslHandler getSslSessionHandler(IoSession session) {
659         SslHandler handler = (SslHandler) session.getAttribute(SSL_HANDLER);
660         
661         if (handler == null) {
662             throw new IllegalStateException();
663         }
664         
665         if (handler.getParent() != this) {
666             throw new IllegalArgumentException("Not managed by this filter.");
667         }
668         
669         return handler;
670     }
671 
672     /**
673      * A message that is sent from {@link SslFilter} when the connection became
674      * secure or is not secure anymore.
675      *
676      * @author The Apache MINA Project (dev@mina.apache.org)
677      * @version $Rev: 713366 $, $Date: 2008-11-12 14:39:23 +0100 (Wed, 12 Nov 2008) $
678      */
679     public static class SslFilterMessage {
680         private final String name;
681 
682         private SslFilterMessage(String name) {
683             this.name = name;
684         }
685 
686         @Override
687         public String toString() {
688             return name;
689         }
690     }
691 
692     private static class EncryptedWriteRequest extends WriteRequestWrapper {
693         private final IoBuffer encryptedMessage;
694 
695         private EncryptedWriteRequest(WriteRequest writeRequest,
696                 IoBuffer encryptedMessage) {
697             super(writeRequest);
698             this.encryptedMessage = encryptedMessage;
699         }
700 
701         @Override
702         public Object getMessage() {
703             return encryptedMessage;
704         }
705     }
706 }