snf4j / snf4j

Compare 69ddc04 ... +0 ... 6ef10ec

Coverage Reach
snf4j-core/src/main/java/org/snf4j/core/codec/zip/GzipDecoder.java snf4j-core/src/main/java/org/snf4j/core/codec/zip/ZlibDecoder.java snf4j-core/src/main/java/org/snf4j/core/codec/zip/ZlibEncoder.java snf4j-core/src/main/java/org/snf4j/core/codec/zip/GzipEncoder.java snf4j-core/src/main/java/org/snf4j/core/codec/zip/DecompressionException.java snf4j-core/src/main/java/org/snf4j/core/codec/zip/ZlibCodec.java snf4j-core/src/main/java/org/snf4j/core/codec/DefaultCodecExecutor.java snf4j-core/src/main/java/org/snf4j/core/codec/InternalCodecPipeline.java snf4j-core/src/main/java/org/snf4j/core/codec/bytes/BufferToArrayEncoder.java snf4j-core/src/main/java/org/snf4j/core/codec/bytes/BufferToArrayDecoder.java snf4j-core/src/main/java/org/snf4j/core/codec/bytes/BufferToArrayCodec.java snf4j-core/src/main/java/org/snf4j/core/codec/bytes/ArrayToBufferDecoder.java snf4j-core/src/main/java/org/snf4j/core/codec/bytes/ArrayToBufferEncoder.java snf4j-core/src/main/java/org/snf4j/core/codec/bytes/ArrayToBufferCodec.java snf4j-core/src/main/java/org/snf4j/core/codec/CompoundCodec.java snf4j-core/src/main/java/org/snf4j/core/codec/EncoderContext.java snf4j-core/src/main/java/org/snf4j/core/codec/EventDrivenCompoundDecoder.java snf4j-core/src/main/java/org/snf4j/core/codec/EventDrivenCompoundEncoder.java snf4j-core/src/main/java/org/snf4j/core/codec/CodecContext.java snf4j-core/src/main/java/org/snf4j/core/codec/DecoderContext.java snf4j-core/src/main/java/org/snf4j/core/codec/CompoundDecoder.java snf4j-core/src/main/java/org/snf4j/core/codec/CompoundEncoder.java snf4j-core/src/main/java/org/snf4j/core/proxy/HttpProxyHandler.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5CommandState.java snf4j-core/src/main/java/org/snf4j/core/proxy/AbstractSocksProxyHandler.java snf4j-core/src/main/java/org/snf4j/core/proxy/AbstractProxyHandler.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks4CommandState.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5ProxyHandler.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5PasswordAuthState.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5InitState.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5Status.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks4ProxyHandler.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks4Status.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5Reply.java snf4j-core/src/main/java/org/snf4j/core/proxy/SocksAddressType.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks4Reply.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5AuthMethod.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks5Command.java snf4j-core/src/main/java/org/snf4j/core/proxy/ProxyConnectionException.java snf4j-core/src/main/java/org/snf4j/core/proxy/AbstractSocksState.java snf4j-core/src/main/java/org/snf4j/core/proxy/Socks4Command.java snf4j-core/src/main/java/org/snf4j/core/proxy/SocksDoneState.java snf4j-core/src/main/java/org/snf4j/core/proxy/ProxyConnectionTimeoutException.java snf4j-core/src/main/java/org/snf4j/core/InternalSelectorLoop.java snf4j-core/src/main/java/org/snf4j/core/session/ssl/SSLContextBuilder.java snf4j-core/src/main/java/org/snf4j/core/session/ssl/SSLEngineBuilder.java snf4j-core/src/main/java/org/snf4j/core/session/ssl/ProtocolDefaults.java snf4j-core/src/main/java/org/snf4j/core/session/ssl/SupportedCipherProtocolFilters.java snf4j-core/src/main/java/org/snf4j/core/session/ssl/SSLContextCreateException.java snf4j-core/src/main/java/org/snf4j/core/session/ssl/DefaultCipherProtocolFilters.java snf4j-core/src/main/java/org/snf4j/core/session/ssl/ClientAuth.java snf4j-core/src/main/java/org/snf4j/core/session/DefaultSessionConfig.java snf4j-core/src/main/java/org/snf4j/core/session/AbstractSession.java snf4j-core/src/main/java/org/snf4j/core/session/UnsupportedSessionTimer.java snf4j-core/src/main/java/org/snf4j/core/session/SSLEngineCreateException.java snf4j-core/src/main/java/org/snf4j/core/session/IllegalSessionStateException.java snf4j-core/src/main/java/org/snf4j/core/session/SessionState.java snf4j-core/src/main/java/org/snf4j/core/util/NetworkUtil.java snf4j-core/src/main/java/org/snf4j/core/util/Base64Util.java snf4j-core/src/main/java/org/snf4j/core/util/PemUtil.java snf4j-core/src/main/java/org/snf4j/core/StreamSession.java snf4j-core/src/main/java/org/snf4j/core/InternalSession.java snf4j-core/src/main/java/org/snf4j/core/future/AbstractBlockingFuture.java snf4j-core/src/main/java/org/snf4j/core/future/SessionFuturesController.java snf4j-core/src/main/java/org/snf4j/core/future/AbstractFuture.java snf4j-core/src/main/java/org/snf4j/core/future/DelegatingBlockingFuture.java snf4j-core/src/main/java/org/snf4j/core/future/CompletedFuture.java snf4j-core/src/main/java/org/snf4j/core/future/DataFuture.java snf4j-core/src/main/java/org/snf4j/core/future/AbortableThresholdFuture.java snf4j-core/src/main/java/org/snf4j/core/future/EventFuture.java snf4j-core/src/main/java/org/snf4j/core/future/TaskFuture.java snf4j-core/src/main/java/org/snf4j/core/future/ThresholdFuture.java snf4j-core/src/main/java/org/snf4j/core/future/TwoThresholdFuture.java snf4j-core/src/main/java/org/snf4j/core/future/FutureLock.java snf4j-core/src/main/java/org/snf4j/core/future/FailedFuture.java snf4j-core/src/main/java/org/snf4j/core/future/FutureState.java snf4j-core/src/main/java/org/snf4j/core/future/BlockingFutureOperationException.java snf4j-core/src/main/java/org/snf4j/core/future/CancelledFuture.java snf4j-core/src/main/java/org/snf4j/core/future/RegisterFuture.java snf4j-core/src/main/java/org/snf4j/core/future/SuccessfulFuture.java snf4j-core/src/main/java/org/snf4j/core/DatagramSession.java snf4j-core/src/main/java/org/snf4j/core/allocator/CachingAllocator.java snf4j-core/src/main/java/org/snf4j/core/allocator/DefaultAllocator.java snf4j-core/src/main/java/org/snf4j/core/allocator/Cache.java snf4j-core/src/main/java/org/snf4j/core/allocator/DefaultAllocatorMetric.java snf4j-core/src/main/java/org/snf4j/core/allocator/ThreadLocalCachingAllocator.java snf4j-core/src/main/java/org/snf4j/core/allocator/LastCache.java snf4j-core/src/main/java/org/snf4j/core/allocator/NopAllocatorMetric.java snf4j-core/src/main/java/org/snf4j/core/allocator/SyncLastCache.java snf4j-core/src/main/java/org/snf4j/core/allocator/SyncCache.java snf4j-core/src/main/java/org/snf4j/core/EngineStreamHandler.java snf4j-core/src/main/java/org/snf4j/core/EngineDatagramHandler.java snf4j-core/src/main/java/org/snf4j/core/SelectorLoop.java snf4j-core/src/main/java/org/snf4j/core/AbstractEngineHandler.java snf4j-core/src/main/java/org/snf4j/core/SessionPipeline.java snf4j-core/src/main/java/org/snf4j/core/logger/TestingLogger.java snf4j-core/src/main/java/org/snf4j/core/logger/LoggerFactory.java snf4j-core/src/main/java/org/snf4j/core/logger/NopLogger.java snf4j-core/src/main/java/org/snf4j/core/logger/DefaultExceptionLogger.java snf4j-core/src/main/java/org/snf4j/core/logger/ExceptionLogger.java snf4j-core/src/main/java/org/snf4j/core/logger/TestingLoggerFactory.java snf4j-core/src/main/java/org/snf4j/core/logger/NopLoggerFactory.java snf4j-core/src/main/java/org/snf4j/core/DatagramServerHandler.java snf4j-core/src/main/java/org/snf4j/core/EngineDatagramWrapper.java snf4j-core/src/main/java/org/snf4j/core/EngineStreamSession.java snf4j-core/src/main/java/org/snf4j/core/EncodeTask.java snf4j-core/src/main/java/org/snf4j/core/DatagramServerSession.java snf4j-core/src/main/java/org/snf4j/core/handler/AbstractHandler.java snf4j-core/src/main/java/org/snf4j/core/handler/AbstractDatagramHandler.java snf4j-core/src/main/java/org/snf4j/core/handler/AbstractStreamHandler.java snf4j-core/src/main/java/org/snf4j/core/handler/SessionIncidentException.java snf4j-core/src/main/java/org/snf4j/core/handler/SessionEvent.java snf4j-core/src/main/java/org/snf4j/core/handler/SessionIncident.java snf4j-core/src/main/java/org/snf4j/core/handler/SessionException.java snf4j-core/src/main/java/org/snf4j/core/handler/DataEvent.java snf4j-core/src/main/java/org/snf4j/core/handler/HandshakeLoopsThresholdException.java snf4j-core/src/main/java/org/snf4j/core/handler/HandshakeTimeoutException.java snf4j-core/src/main/java/org/snf4j/core/EngineDatagramSession.java snf4j-core/src/main/java/org/snf4j/core/CodecExecutorAdapter.java snf4j-core/src/main/java/org/snf4j/core/EngineDatagramServerSession.java snf4j-core/src/main/java/org/snf4j/core/thread/FastThreadLocal.java snf4j-core/src/main/java/org/snf4j/core/thread/FastThreadLocalThread.java snf4j-core/src/main/java/org/snf4j/core/InternalSSLEngine.java snf4j-core/src/main/java/org/snf4j/core/pool/DefaultSelectorLoopPool.java snf4j-core/src/main/java/org/snf4j/core/timer/DefaultTimer.java snf4j-core/src/main/java/org/snf4j/core/timer/DefaultTimeoutModel.java snf4j-core/src/main/java/org/snf4j/core/DatagramChannelContext.java snf4j-core/src/main/java/org/snf4j/core/SocketChannelContext.java snf4j-core/src/main/java/org/snf4j/core/factory/AbstractSessionFactory.java snf4j-core/src/main/java/org/snf4j/core/factory/DefaultSessionStructureFactory.java snf4j-core/src/main/java/org/snf4j/core/factory/DefaultSelectorLoopStructureFactory.java snf4j-core/src/main/java/org/snf4j/core/factory/DefaultThreadFactory.java snf4j-core/src/main/java/org/snf4j/core/engine/EngineResult.java snf4j-core/src/main/java/org/snf4j/core/engine/HandshakeStatus.java snf4j-core/src/main/java/org/snf4j/core/engine/Status.java snf4j-core/src/main/java/org/snf4j/core/AbstractSessionTimer.java snf4j-core/src/main/java/org/snf4j/core/EventType.java snf4j-core/src/main/java/org/snf4j/core/StreamSessionPipeline.java snf4j-core/src/main/java/org/snf4j/core/ChannelContext.java snf4j-core/src/main/java/org/snf4j/core/ServerSocketChannelContext.java snf4j-core/src/main/java/org/snf4j/core/DTLSSession.java snf4j-core/src/main/java/org/snf4j/core/SSLSession.java snf4j-core/src/main/java/org/snf4j/core/DTLSServerHandler.java snf4j-core/src/main/java/org/snf4j/core/InternalSessionTimer.java snf4j-core/src/main/java/org/snf4j/core/IdentifiableObject.java snf4j-core/src/main/java/org/snf4j/core/StoppingType.java snf4j-core/src/main/java/org/snf4j/core/ServerChannelContext.java snf4j-core/src/main/java/org/snf4j/core/SessionChannelContext.java snf4j-core/src/main/java/org/snf4j/core/EndingAction.java snf4j-core/src/main/java/org/snf4j/core/ClosingState.java snf4j-core/src/main/java/org/snf4j/core/DefaultExecutor.java snf4j-core/src/main/java/org/snf4j/core/DefaultSelectorLoopController.java snf4j-core/src/main/java/org/snf4j/core/ICloseControllingException.java snf4j-core/src/main/java/org/snf4j/core/PipelineDecodeException.java snf4j-core/src/main/java/org/snf4j/core/Constants.java snf4j-core/src/main/java/org/snf4j/core/SelectorLoopStoppingException.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/Handshaker.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HttpUtils.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeUtils.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeDecoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeFactory.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeEncoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/InvalidHandshakeException.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HttpStatus.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeResponse.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeAcceptException.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/HandshakeRequest.java snf4j-websocket/src/main/java/org/snf4j/websocket/handshake/InvalidHandshakeRequestException.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/FrameDecoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/FrameEncoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/CloseFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/FrameAggregator.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/FrameUtf8Validator.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/Utf8.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/PayloadAggregator.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/Opcode.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/Frame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/InvalidFrameException.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/TextFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/ContinuationFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/AggregatedTextFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/AggregatedBinaryFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/BinaryFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/PingFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/ControlFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/PongFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/frame/DataFrame.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/compress/PerMessageDeflateExtension.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/compress/PerMessageDeflateParams.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/compress/DeflateDecoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/compress/DeflateCodec.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/compress/DeflateEncoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/compress/PerMessageDeflateEncoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/compress/PerMessageDeflateDecoder.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/InvalidExtensionException.java snf4j-websocket/src/main/java/org/snf4j/websocket/extensions/GroupIdentifier.java snf4j-websocket/src/main/java/org/snf4j/websocket/WebSocketSessionHandler.java snf4j-websocket/src/main/java/org/snf4j/websocket/DefaultWebSocketSessionConfig.java snf4j-websocket/src/main/java/org/snf4j/websocket/WebSocketSession.java snf4j-websocket/src/main/java/org/snf4j/websocket/SSLWebSocketSession.java snf4j-websocket/src/main/java/org/snf4j/websocket/AbstractWebSocketSessionFactory.java snf4j-websocket/src/main/java/org/snf4j/websocket/AbstractWebSocketHandler.java snf4j-sctp/src/main/java/org/snf4j/core/InternalSctpSession.java snf4j-sctp/src/main/java/org/snf4j/core/SctpMultiSession.java snf4j-sctp/src/main/java/org/snf4j/core/ImmutableSctpMessageInfo.java snf4j-sctp/src/main/java/org/snf4j/core/AbstractSctpChannelContext.java snf4j-sctp/src/main/java/org/snf4j/core/SctpFragments.java snf4j-sctp/src/main/java/org/snf4j/core/SctpCodecExecutorAdapter.java snf4j-sctp/src/main/java/org/snf4j/core/SctpChannelContext.java snf4j-sctp/src/main/java/org/snf4j/core/SctpMultiChannelContext.java snf4j-sctp/src/main/java/org/snf4j/core/SctpEncodeTask.java snf4j-sctp/src/main/java/org/snf4j/core/SctpServerChannelContext.java snf4j-sctp/src/main/java/org/snf4j/core/handler/AbstractSctpHandler.java snf4j-sctp/src/main/java/org/snf4j/core/handler/SctpNotificationType.java snf4j-sctp/src/main/java/org/snf4j/core/handler/SctpSendingFailureException.java snf4j-sctp/src/main/java/org/snf4j/core/SctpSession.java snf4j-sctp/src/main/java/org/snf4j/core/SctpNopCodecExecutor.java snf4j-sctp/src/main/java/org/snf4j/core/session/DefaultSctpSessionConfig.java snf4j-sctp/src/main/java/org/snf4j/core/SctpRegistrator.java snf4j-sctp/src/main/java/org/snf4j/core/factory/AbstractSctpSessionFactory.java snf4j-core-slf4j/src/main/java/org/snf4j/core/logger/impl/Slf4jLogger.java snf4j-core-slf4j/src/main/java/org/snf4j/core/logger/impl/Slf4jLoggerFactory.java snf4j-core-slf4j/src/main/java/org/snf4j/core/logger/impl/LoggerFactoryBinder.java snf4j-core-log4j2/src/main/java/org/snf4j/core/logger/impl/Log4j2Logger.java snf4j-core-log4j2/src/main/java/org/snf4j/core/logger/impl/Log4j2LoggerFactory.java snf4j-core-log4j2/src/main/java/org/snf4j/core/logger/impl/LoggerFactoryBinder.java

No flags found

Use flags to group coverage reports by test type, project and/or folders.
Then setup custom commit statuses and notifications for each flag.

e.g., #unittest #integration

#production #enterprise

#frontend #backend

Learn more about Codecov Flags here.

Showing 11 of 46 files from the diff.
Other files ignored by Codecov

@@ -107,15 +107,10 @@
Loading
107 107
	}
108 108
	
109 109
	static byte[] parseKey(String key) {
110 -
		try {
111 -
			byte[] bytes = Base64Util.decode(HttpUtils.bytes(key));
110 +
		byte[] bytes = Base64Util.decode(HttpUtils.bytes(key));
112 111
113 -
			if (bytes.length == 16) {
114 -
				return bytes;
115 -
			}
116 -
		}
117 -
		catch (Exception e) {
118 -
			//Ignore
112 +
		if (bytes != null && bytes.length == 16) {
113 +
			return bytes;
119 114
		}
120 115
		return null;
121 116
	}

@@ -0,0 +1,930 @@
Loading
1 +
/*
2 +
 * -------------------------------- MIT License --------------------------------
3 +
 * 
4 +
 * Copyright (c) 2021 SNF4J contributors
5 +
 * 
6 +
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 +
 * of this software and associated documentation files (the "Software"), to deal
8 +
 * in the Software without restriction, including without limitation the rights
9 +
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 +
 * copies of the Software, and to permit persons to whom the Software is
11 +
 * furnished to do so, subject to the following conditions:
12 +
 * 
13 +
 * The above copyright notice and this permission notice shall be included in all
14 +
 * copies or substantial portions of the Software.
15 +
 * 
16 +
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 +
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 +
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 +
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 +
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 +
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 +
 * SOFTWARE.
23 +
 *
24 +
 * -----------------------------------------------------------------------------
25 +
 */
26 +
package org.snf4j.core.session.ssl;
27 +
28 +
import java.io.ByteArrayInputStream;
29 +
import java.io.Closeable;
30 +
import java.io.File;
31 +
import java.io.IOException;
32 +
import java.io.InputStream;
33 +
import java.security.KeyException;
34 +
import java.security.KeyFactory;
35 +
import java.security.KeyStore;
36 +
import java.security.PrivateKey;
37 +
import java.security.Provider;
38 +
import java.security.SecureRandom;
39 +
import java.security.cert.CertificateException;
40 +
import java.security.cert.CertificateFactory;
41 +
import java.security.cert.X509Certificate;
42 +
import java.security.spec.PKCS8EncodedKeySpec;
43 +
import java.util.Arrays;
44 +
import java.util.List;
45 +
46 +
import javax.crypto.Cipher;
47 +
import javax.crypto.EncryptedPrivateKeyInfo;
48 +
import javax.crypto.SecretKey;
49 +
import javax.crypto.SecretKeyFactory;
50 +
import javax.crypto.spec.PBEKeySpec;
51 +
import javax.net.ssl.KeyManagerFactory;
52 +
import javax.net.ssl.SSLContext;
53 +
import javax.net.ssl.SSLEngine;
54 +
import javax.net.ssl.SSLSessionContext;
55 +
import javax.net.ssl.TrustManagerFactory;
56 +
import javax.security.auth.DestroyFailedException;
57 +
import javax.security.auth.Destroyable;
58 +
59 +
import org.snf4j.core.util.PemUtil;
60 +
import org.snf4j.core.util.PemUtil.Label;
61 +
62 +
/**
63 +
 * A builder for the {@link SSLContext}.
64 +
 *  
65 +
 * @author <a href="http://snf4j.org">SNF4J.ORG</a>
66 +
 */
67 +
public class SSLContextBuilder implements Destroyable {
68 +
	
69 +
	private final static String[] KEY_ALGOS = new String[] {"RSA","DSA","EC"};
70 +
	
71 +
	private final boolean forServer;
72 +
	
73 +
	private String protocol = ProtocolDefaults.TLS;
74 +
	
75 +
	private Provider provider;
76 +
	
77 +
	private String providerName;
78 +
	
79 +
	private int sessionCacheSize = -1;
80 +
	
81 +
	private int sessionTimeout = -1;
82 +
	
83 +
	private TrustManagerFactory trustManager;
84 +
	
85 +
	private X509Certificate[] trustCerts;
86 +
87 +
	private PrivateKey key;
88 +
	
89 +
	private char[] password;
90 +
	
91 +
	private KeyManagerFactory keyManager;
92 +
	
93 +
	private X509Certificate[] keyCerts;
94 +
	
95 +
	private SecureRandom secureRandom;
96 +
	
97 +
	//SSL engine default
98 +
	
99 +
	private String[] protocols;
100 +
101 +
	private ProtocolFilter protocolFilter;
102 +
	
103 +
	private String[] ciphers;
104 +
	
105 +
	private CipherFilter cipherFilter;
106 +
	
107 +
	private Boolean enableRetransmissions;	//JDK9
108 +
	
109 +
	private int maximumPacketSize = -1; //JDK9
110 +
	
111 +
	private Boolean useCiphersOrder;
112 +
	
113 +
	private ClientAuth clientAuth = ClientAuth.NONE;
114 +
	
115 +
	private SSLContextBuilder(boolean forServer) {
116 +
		this.forServer = forServer;
117 +
	}
118 +
119 +
	private static SSLContextBuilder forServer() {
120 +
		return new SSLContextBuilder(true);
121 +
	}
122 +
123 +
	/**
124 +
	 * Creates a builder for a client-side {@link SSLContext}.
125 +
	 * 
126 +
	 * @return a builder for a client-side {@link SSLContext}
127 +
	 */
128 +
	public static SSLContextBuilder forClient() {
129 +
		return new SSLContextBuilder(false);
130 +
	}
131 +
132 +
	/**
133 +
	 * Creates a builder for a server-side {@link SSLContext}.
134 +
	 * 
135 +
	 * @param keyFile      a file for a PKCS#8 private key in the PEM encoding
136 +
	 * @param keyCertsFile a file for an X.509 certificate chain in the PEM encoding
137 +
	 * @return a builder for a server-side {@link SSLContext}
138 +
	 * @throws IOException          if a failure occurred while reading the files
139 +
	 * @throws KeyException         if a failure occurred while creating the key
140 +
	 * @throws CertificateException if a failure occurred while creating the
141 +
	 *                              certificates
142 +
	 */
143 +
	public static SSLContextBuilder forServer(File keyFile, File keyCertsFile) throws IOException, KeyException, CertificateException {
144 +
		return forServer().keyManager(keyFile, keyCertsFile);
145 +
	}
146 +
147 +
	/**
148 +
	 * Creates a builder for a server-side {@link SSLContext}.
149 +
	 * 
150 +
	 * @param keyFile      a file for a PKCS#8 private key in the PEM encoding
151 +
	 * @param password     the password protecting the private key, or {@code null}
152 +
	 *                     if the key is not password-protected
153 +
	 * @param keyCertsFile a file for an X.509 certificate chain in the PEM encoding
154 +
	 * @return a builder for a server-side {@link SSLContext}
155 +
	 * @throws IOException          if a failure occurred while reading the files
156 +
	 * @throws KeyException         if a failure occurred while creating the key
157 +
	 * @throws CertificateException if a failure occurred while creating the
158 +
	 *                              certificates
159 +
	 */
160 +
	public static SSLContextBuilder forServer(File keyFile, char[] password, File keyCertsFile) throws IOException, KeyException, CertificateException {
161 +
		return forServer().keyManager(keyFile, password, keyCertsFile);
162 +
	}
163 +
164 +
	/**
165 +
	 * Creates a builder for a server-side {@link SSLContext}.
166 +
	 * 
167 +
	 * @param keyIn      an input stream for a PKCS#8 private key in the PEM
168 +
	 *                   encoding
169 +
	 * @param keyCertsIn an input stream for an X.509 certificate chain in the PEM
170 +
	 *                   encoding
171 +
	 * @return a builder for a server-side {@link SSLContext}
172 +
	 * @throws IOException          if a failure occurred while reading from the
173 +
	 *                              input streams
174 +
	 * @throws KeyException         if a failure occurred while creating the key
175 +
	 * @throws CertificateException if a failure occurred while creating the
176 +
	 *                              certificates
177 +
	 */
178 +
	public static SSLContextBuilder forServer(InputStream keyIn, InputStream keyCertsIn) throws IOException, KeyException, CertificateException {
179 +
		return forServer().keyManager(keyIn, keyCertsIn);
180 +
	}
181 +
182 +
	/**
183 +
	 * Creates a builder for a server-side {@link SSLContext}.
184 +
	 * 
185 +
	 * @param keyIn    an input stream for a PKCS#8 private key in the PEM encoding
186 +
	 * @param password the password protecting the private key, or {@code null} if
187 +
	 *                 the key is not password-protected
188 +
	 * @param keyCertsIn  an input stream for an X.509 certificate chain in the PEM
189 +
	 *                 encoding
190 +
	 * @return a builder for a server-side {@link SSLContext}
191 +
	 * @throws IOException          if a failure occurred while reading from the
192 +
	 *                              input streams
193 +
	 * @throws KeyException         if a failure occurred while creating the key
194 +
	 * @throws CertificateException if a failure occurred while creating the
195 +
	 *                              certificates
196 +
	 */
197 +
	public static SSLContextBuilder forServer(InputStream keyIn, char[] password, InputStream keyCertsIn) throws IOException, KeyException, CertificateException {
198 +
		return forServer().keyManager(keyIn, password, keyCertsIn);
199 +
	}
200 +
		
201 +
	/**
202 +
	 * Creates a builder for a server-side {@link SSLContext}.
203 +
	 * 
204 +
	 * @param key   a PKCS#8 private key
205 +
	 * @param keyCerts an X.509 certificate chain
206 +
	 * @return a builder for a server-side {@link SSLContext}
207 +
	 */
208 +
	public static SSLContextBuilder forServer(PrivateKey key, X509Certificate... keyCerts) {
209 +
		return forServer().keyManager(key, keyCerts);
210 +
	}
211 +
	
212 +
	/**
213 +
	 * Creates a builder for a server-side {@link SSLContext}.
214 +
	 * 
215 +
	 * @param key      a PKCS#8 private key
216 +
	 * @param password the password protecting the private key, or {@code null} if
217 +
	 *                 the key is not password-protected
218 +
	 * @param keyCerts    an X.509 certificate chain
219 +
	 * @return a builder for a server-side {@link SSLContext}
220 +
	 */
221 +
	public static SSLContextBuilder forServer(PrivateKey key, char[] password, X509Certificate... keyCerts) {
222 +
		return forServer().keyManager(key, password, keyCerts);
223 +
	}
224 +
	
225 +
	/**
226 +
	 * Creates a builder for a server-side {@link SSLContext}.
227 +
	 * 
228 +
	 * @param keyFactory a factory for a private key 
229 +
	 * @return a builder for a server-side {@link SSLContext}
230 +
	 */
231 +
	public static SSLContextBuilder forServer(KeyManagerFactory keyFactory) {
232 +
		return forServer().keyManager(keyFactory);
233 +
	}
234 +
235 +
	/**
236 +
	 * Tells if the builder if for a server-side {@link SSLContext}.
237 +
	 * 
238 +
	 * @return {@code true} if the builder if for a server-side {@link SSLContext}
239 +
	 */
240 +
	public boolean isForServer() {
241 +
		return forServer;
242 +
	}
243 +
	
244 +
	/**
245 +
	 * Tells if the builder if for a client-side {@link SSLContext}.
246 +
	 * 
247 +
	 * @return {@code true} if the builder if for a client-side {@link SSLContext}
248 +
	 */
249 +
	public boolean isForClient() {
250 +
		return !forServer;
251 +
	}
252 +
	
253 +
	/**
254 +
	 * Configures the protocol name of the {@link SSLContext} to be created by this
255 +
	 * builder.
256 +
	 * 
257 +
	 * @param protocol the protocol name
258 +
	 * @return this builder
259 +
	 */
260 +
	public SSLContextBuilder protocol(String protocol) {
261 +
		this.protocol = protocol;
262 +
		return this;
263 +
	}
264 +
265 +
	/**
266 +
	 * Configures protocol versions to enable, or {@code null} to enable the
267 +
	 * recommended protocol versions.
268 +
	 * <p>
269 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
270 +
	 * returned by the {@link #engineBuilder()} method.
271 +
	 * 
272 +
	 * @param protocols the protocol versions
273 +
	 * @return this builder
274 +
	 */
275 +
	public SSLContextBuilder protocols(String... protocols) {
276 +
		this.protocols = protocols == null ? null : protocols.clone();
277 +
		return this;
278 +
	}
279 +
280 +
	/**
281 +
	 * Configures a filter for protocol versions to enable, or {@code null} to use
282 +
	 * the default filter.
283 +
	 * <p>
284 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
285 +
	 * returned by the {@link #engineBuilder()} method.
286 +
	 * 
287 +
	 * @param filter the protocol filter
288 +
	 * @return this builder
289 +
	 */
290 +
	public SSLContextBuilder protocolFilter(ProtocolFilter filter) {
291 +
		protocolFilter = filter;
292 +
		return this;
293 +
	}
294 +
295 +
	/**
296 +
	 * Configures cipher suites to enable, or {@code null} to enable the
297 +
	 * recommended cipher suites.
298 +
	 * <p>
299 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
300 +
	 * returned by the {@link #engineBuilder()} method.
301 +
	 * 
302 +
	 * @param ciphers the cipher suites
303 +
	 * @return this builder
304 +
	 */
305 +
	public SSLContextBuilder ciphers(String... ciphers) {
306 +
		this.ciphers = ciphers == null ? null : ciphers.clone();
307 +
		return this;
308 +
	}
309 +
310 +
	/**
311 +
	 * Configures a filter for cipher suites to enable, or {@code null} to use
312 +
	 * the default filter.
313 +
	 * <p>
314 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
315 +
	 * returned by the {@link #engineBuilder()} method.
316 +
	 * 
317 +
	 * @param filter the cipher filter
318 +
	 * @return this builder
319 +
	 */
320 +
	public SSLContextBuilder cipherFilter(CipherFilter filter) {
321 +
		cipherFilter = filter;
322 +
		return this;
323 +
	}
324 +
	
325 +
	/**
326 +
	 * Configures if DTLS handshake retransmissions should be enabled.
327 +
	 * <p>
328 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
329 +
	 * returned by the {@link #engineBuilder()} method.
330 +
	 * <p>
331 +
	 * NOTE: It requires Java 9 or newer.
332 +
	 * 
333 +
	 * @param enable {@code true} to enable DTLS handshake retransmissions.
334 +
	 * @return this builder
335 +
	 */
336 +
	public SSLContextBuilder enableRetransmissions(boolean enable) {
337 +
		enableRetransmissions = enable ? Boolean.TRUE : Boolean.FALSE;
338 +
		return this;
339 +
	}
340 +
	
341 +
	/**
342 +
	 * Configures the maximum expected network packet size.
343 +
	 * <p>
344 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
345 +
	 * returned by the {@link #engineBuilder()} method.
346 +
	 * <p>
347 +
	 * NOTE: It requires Java 9 or newer.
348 +
	 * 
349 +
	 * @param maxSize the maximum expected network packet size in bytes, or 0 to use
350 +
	 *                the default value that is specified by the underlying
351 +
	 *                implementation.
352 +
	 * @return this builder
353 +
	 */
354 +
	public SSLContextBuilder maximumPacketSize(int maxSize) {
355 +
		maximumPacketSize = maxSize;
356 +
		return this;
357 +
	}
358 +
	
359 +
	/**
360 +
	 * Configures if the local cipher suites preferences should be honored during
361 +
	 * SSL/TLS/DTLS handshaking
362 +
	 * <p>
363 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
364 +
	 * returned by the {@link #engineBuilder()} method.
365 +
	 * 
366 +
	 * @param useOrder {@code true} to honor the local cipher suites preferences
367 +
	 * @return this builder
368 +
	 */
369 +
	public SSLContextBuilder useCiphersOrder(boolean useOrder) {
370 +
		useCiphersOrder = useOrder ? Boolean.TRUE : Boolean.FALSE;
371 +
		return this;
372 +
	}
373 +
	
374 +
	/**
375 +
	 * Configures the client authentication mode for a server-side
376 +
	 * {@link SSLEngine}.
377 +
	 * <p>
378 +
	 * This configuration is used to pre-configure the {@link SSLEngineBuilder}
379 +
	 * returned by the {@link #engineBuilder()} method.
380 +
	 * 
381 +
	 * @param clientAuth the client authentication mode.
382 +
	 * @return this builder
383 +
	 */
384 +
	public SSLContextBuilder clientAuth(ClientAuth clientAuth) {
385 +
		this.clientAuth = clientAuth;
386 +
		return this;
387 +
	}
388 +
	
389 +
	/**
390 +
	 * Configures the provide of the {@link SSLContext} to be created by this
391 +
	 * builder.
392 +
	 * 
393 +
	 * @param provider the provider
394 +
	 * @return this builder
395 +
	 */
396 +
	public SSLContextBuilder provider(Provider provider) {
397 +
		this.provider = provider;
398 +
		providerName = null;
399 +
		return this;
400 +
	}
401 +
402 +
	/**
403 +
	 * Configures the provider name of the {@link SSLContext} to be created by this
404 +
	 * builder.
405 +
	 * 
406 +
	 * @param provider the provider name
407 +
	 * @return this builder
408 +
	 */
409 +
	public SSLContextBuilder providerName(String provider) {
410 +
		this.providerName = provider;
411 +
		provider = null;
412 +
		return this;
413 +
	}
414 +
	
415 +
	/**
416 +
	 * Configures the timeout limit for the cached SSL session objects.
417 +
	 * 
418 +
	 * @param timeout the timeout limit in seconds, or 0 to set no limit.
419 +
	 * @return this builder
420 +
	 */
421 +
	public SSLContextBuilder sessionTimeout(int timeout) {
422 +
		sessionTimeout = timeout;
423 +
		return this;
424 +
	}
425 +
426 +
	/**
427 +
	 * Configures the size of the cache used for storing the SSL session objects.
428 +
	 * 
429 +
	 * @param size the cache size limit, or 0 to set no limit.
430 +
	 * @return this builder
431 +
	 */
432 +
	public SSLContextBuilder sessionCacheSize(int size) {
433 +
		sessionCacheSize = size;
434 +
		return this;
435 +
	}
436 +
	
437 +
	private X509Certificate[] createCerts(List<byte[]> certs) throws CertificateException {
438 +
		if (certs == null) {
439 +
			throw new CertificateException("Invalid certificate PEM format");
440 +
		}
441 +
		if (certs.isEmpty()) {
442 +
			throw new CertificateException("No certificate found");
443 +
		}
444 +
		
445 +
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
446 +
        int len = certs.size();
447 +
        X509Certificate[] out = new X509Certificate[len];
448 +
449 +
        for (int i=0; i<len; ++i) {
450 +
        	InputStream in = new ByteArrayInputStream(certs.get(i));
451 +
        	
452 +
        	try {
453 +
        		out[i] = (X509Certificate) cf.generateCertificate(in);
454 +
        	}
455 +
        	finally {
456 +
        		silentClose(in);
457 +
        	}
458 +
        }
459 +
        return out;
460 +
	}
461 +
	
462 +
	static void silentClose(Closeable stream) {
463 +
		try {
464 +
			stream.close();
465 +
		}
466 +
		catch (IOException e) {
467 +
			//Ignore
468 +
		}
469 +
	}
470 +
	
471 +
	private X509Certificate[] readCerts(File file) throws IOException, CertificateException {
472 +
		return createCerts(PemUtil.read(Label.CERTIFICATE, file));
473 +
	}
474 +
475 +
	private X509Certificate[] readCerts(InputStream in) throws IOException, CertificateException {
476 +
		return createCerts(PemUtil.read(Label.CERTIFICATE, in));
477 +
	}
478 +
	
479 +
	/**
480 +
	 * Configures trusted certificates for remote hosts verification.
481 +
	 * 
482 +
	 * @param trustCertsFile a file for X.509 certificates in the PEM encoding
483 +
	 * @return this builder
484 +
	 * @throws IOException          if a failure occurred while reading the files
485 +
	 * @throws CertificateException if a failure occurred while creating the
486 +
	 */
487 +
	public SSLContextBuilder trustManager(File trustCertsFile) throws IOException, CertificateException {
488 +
		return trustManager(readCerts(trustCertsFile));
489 +
	}
490 +
491 +
	/**
492 +
	 * Configures trusted certificates for remote hosts verification.
493 +
	 * 
494 +
	 * @param trustCertsIn an input stream for X.509 certificates in the PEM encoding
495 +
	 * @return this builder
496 +
	 * @throws IOException          if a failure occurred while reading the files
497 +
	 * @throws CertificateException if a failure occurred while creating the
498 +
	 */
499 +
	public SSLContextBuilder trustManager(InputStream trustCertsIn) throws IOException, CertificateException {
500 +
		return trustManager(readCerts(trustCertsIn));
501 +
	}
502 +
503 +
	/**
504 +
	 * Configures trusted certificates for remote hosts verification.
505 +
	 * 
506 +
	 * @param trustCerts X.509 certificates
507 +
	 * @return this builder
508 +
	 */
509 +
	public SSLContextBuilder trustManager(X509Certificate... trustCerts) {
510 +
		this.trustCerts = certs(trustCerts, true, "trustCerts");
511 +
		trustManager = null;
512 +
		return this;
513 +
	}
514 +
515 +
	/**
516 +
	 * Configures trusted certificates for remote hosts verification.
517 +
	 * 
518 +
	 * @param trustFactory a factory for trusted certificates 
519 +
	 * @return this builder
520 +
	 */
521 +
	public SSLContextBuilder trustManager(TrustManagerFactory trustFactory) {
522 +
		this.trustManager = trustFactory;
523 +
		trustCerts = null;
524 +
		return this;
525 +
	}
526 +
527 +
	private PrivateKey createKey(List<byte[]> keys, char[] password) throws KeyException {
528 +
		if (keys == null) {
529 +
			throw new KeyException("Invalid private key PEM format");
530 +
		}
531 +
		if (keys.isEmpty()) {
532 +
			throw new KeyException("No private key found");
533 +
		}
534 +
		
535 +
		PKCS8EncodedKeySpec keySpec = null;
536 +
		byte[] key = keys.get(0);
537 +
		
538 +
		if (password == null) {
539 +
			keySpec = new PKCS8EncodedKeySpec(keys.get(0));
540 +
		}
541 +
		else {
542 +
			try {
543 +
				EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(key);
544 +
				SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName());
545 +
				PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
546 +
				SecretKey pbeKey;
547 +
				Cipher cipher;
548 +
				
549 +
				try {
550 +
					pbeKey = keyFactory.generateSecret(pbeKeySpec);
551 +
					try {
552 +
						cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName());
553 +
						cipher.init(Cipher.DECRYPT_MODE, pbeKey, encryptedPrivateKeyInfo.getAlgParameters());
554 +
						keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher);
555 +
					}
556 +
					finally {
557 +
						pbeKey.destroy();
558 +
					}
559 +
				}
560 +
				finally {
561 +
					pbeKeySpec.clearPassword();
562 +
				}
563 +
			}
564 +
			catch (DestroyFailedException e) {
565 +
				//Ignore
566 +
			}
567 +
			catch (Exception e) {
568 +
				throw new KeyException("Invalid PKCS8 encoding of password-protected private key", e);
569 +
			}
570 +
		}
571 +
		
572 +
		Exception exception = null;
573 +
		
574 +
		for (int i=0; i < KEY_ALGOS.length; ++i) {
575 +
			try {
576 +
				return KeyFactory.getInstance(KEY_ALGOS[i]).generatePrivate(keySpec);
577 +
			}
578 +
			catch (Exception e) {
579 +
				exception = e;
580 +
			}
581 +
		}
582 +
		throw new KeyException("Generation of private key failed: none of " + Arrays.toString(KEY_ALGOS) + " worked", exception);
583 +
	}
584 +
	
585 +
	private PrivateKey readKey(File file, char[] password) throws IOException, KeyException {
586 +
		return createKey(PemUtil.read(password == null ? Label.PRIVATE_KEY : Label.ENCRYPTED_PRIVATE_KEY, file), 
587 +
				password);
588 +
	}
589 +
590 +
	private PrivateKey readKey(InputStream in, char[] password) throws IOException, KeyException {
591 +
		return createKey(PemUtil.read(password == null ? Label.PRIVATE_KEY : Label.ENCRYPTED_PRIVATE_KEY, in), 
592 +
				password);
593 +
	}
594 +
595 +
	/**
596 +
	 * Configures a private key with certificate chain for host identification.
597 +
	 * 
598 +
	 * @param keyFile      a file for a PKCS#8 private key in the PEM encoding
599 +
	 * @param keyCertsFile a file for an X.509 certificate chain in the PEM encoding
600 +
	 * @throws IOException          if a failure occurred while reading the files
601 +
	 * @throws KeyException         if a failure occurred while creating the key
602 +
	 * @throws CertificateException if a failure occurred while creating the
603 +
	 *                              certificates
604 +
	 * @return this builder
605 +
	 */
606 +
	public SSLContextBuilder keyManager(File keyFile, File keyCertsFile) throws IOException, KeyException, CertificateException {
607 +
		return keyManager(keyFile, null, keyCertsFile);
608 +
	}
609 +
610 +
	/**
611 +
	 * Configures a private key with certificate chain for host identification.
612 +
	 * 
613 +
	 * @param keyFile      a file for a PKCS#8 private key in the PEM encoding
614 +
	 * @param password     the password protecting the private key, or {@code null}
615 +
	 *                     if the key is not password-protected
616 +
	 * @param keyCertsFile a file for an X.509 certificate chain in the PEM encoding
617 +
	 * @throws IOException          if a failure occurred while reading the files
618 +
	 * @throws KeyException         if a failure occurred while creating the key
619 +
	 * @throws CertificateException if a failure occurred while creating the
620 +
	 *                              certificates
621 +
	 * @return this builder
622 +
	 */
623 +
	public SSLContextBuilder keyManager(File keyFile, char[] password, File keyCertsFile) throws IOException, KeyException, CertificateException {
624 +
		return keyManager(readKey(keyFile, password), password, readCerts(keyCertsFile));
625 +
	}
626 +
627 +
	/**
628 +
	 * Configures a private key with certificate chain for host identification.
629 +
	 * 
630 +
	 * @param keyIn      an input stream for a PKCS#8 private key in the PEM
631 +
	 *                   encoding
632 +
	 * @param keyCertsIn an input stream for an X.509 certificate chain in the PEM
633 +
	 *                   encoding
634 +
	 * @throws IOException          if a failure occurred while reading from the
635 +
	 *                              input streams
636 +
	 * @throws KeyException         if a failure occurred while creating the key
637 +
	 * @throws CertificateException if a failure occurred while creating the
638 +
	 *                              certificates
639 +
	 * @return this builder
640 +
	 */
641 +
	public SSLContextBuilder keyManager(InputStream keyIn, InputStream keyCertsIn) throws IOException, KeyException, CertificateException {
642 +
		return keyManager(keyIn, null, keyCertsIn);
643 +
	}
644 +
645 +
	/**
646 +
	 * Configures a private key with certificate chain for host identification.
647 +
	 * 
648 +
	 * @param keyIn      an input stream for a PKCS#8 private key in the PEM
649 +
	 *                   encoding
650 +
	 * @param password   the password protecting the private key, or {@code null} if
651 +
	 *                   the key is not password-protected
652 +
	 * @param keyCertsIn an input stream for an X.509 certificate chain in the PEM
653 +
	 *                   encoding
654 +
	 * @throws IOException          if a failure occurred while reading from the
655 +
	 *                              input streams
656 +
	 * @throws KeyException         if a failure occurred while creating the key
657 +
	 * @throws CertificateException if a failure occurred while creating the
658 +
	 *                              certificates
659 +
	 * @return this builder
660 +
	 */
661 +
	public SSLContextBuilder keyManager(InputStream keyIn, char[] password, InputStream keyCertsIn) throws IOException, KeyException, CertificateException {
662 +
		return keyManager(readKey(keyIn, password), password, readCerts(keyCertsIn));
663 +
	}
664 +
	
665 +
	private static X509Certificate[] certs(X509Certificate[] certs, boolean allowEmpty, String name) {
666 +
		if (!allowEmpty) {
667 +
			if (certs == null) {
668 +
				throw new IllegalArgumentException(name + " is null");
669 +
			}
670 +
			if (certs.length == 0) {
671 +
				throw new IllegalArgumentException(name + " is empty");
672 +
			}
673 +
		}
674 +
		if (certs != null) {
675 +
			for (X509Certificate cert: certs) {
676 +
				if (cert == null) {
677 +
					throw new IllegalArgumentException(name + " contains null entry");
678 +
				}
679 +
			}
680 +
			certs = certs.clone();
681 +
		}
682 +
		return certs;
683 +
	}
684 +
	
685 +
	/**
686 +
	 * Configures a private key with certificate chain for host identification.
687 +
	 * 
688 +
	 * @param key      a PKCS#8 private key
689 +
	 * @param keyCerts an X.509 certificate chain
690 +
	 * @return this builder
691 +
	 */
692 +
	public SSLContextBuilder keyManager(PrivateKey key, X509Certificate... keyCerts) {
693 +
		return keyManager(key, null, keyCerts);
694 +
	}
695 +
	
696 +
	/**
697 +
	 * Configures a private key with certificate chain for host identification.
698 +
	 * 
699 +
	 * @param key      a PKCS#8 private key
700 +
	 * @param password the password protecting the private key, or {@code null} if
701 +
	 *                 the key is not password-protected
702 +
	 * @param keyCerts an X.509 certificate chain
703 +
	 * @return this builder
704 +
	 */
705 +
	public SSLContextBuilder keyManager(PrivateKey key, char[] password, X509Certificate... keyCerts) {
706 +
		keyCerts = certs(keyCerts, false, "keyCerts");
707 +
		if (key == null) {
708 +
			throw new IllegalArgumentException("key is null");
709 +
		}
710 +
		this.key = key;
711 +
		this.password = password == null ? null : password.clone();
712 +
		this.keyCerts = keyCerts;
713 +
		this.keyManager = null;
714 +
		return this;
715 +
	}
716 +
	
717 +
	/**
718 +
	 * Configures a private key with certificate chain for host identification.
719 +
	 * 
720 +
	 * @param keyFactory a factory for a private key 
721 +
	 * @return this builder
722 +
	 */
723 +
	public SSLContextBuilder keyManager(KeyManagerFactory keyFactory) {
724 +
		this.keyManager = keyFactory;
725 +
		silentDestroy();
726 +
		return this;
727 +
	}
728 +
729 +
	/**
730 +
	 * Configures a secure source of randomness.
731 +
	 * 
732 +
	 * @param random the source of randomness, or {@code null} to use the default
733 +
	 *               source.
734 +
	 * @return this builder
735 +
	 */
736 +
	public SSLContextBuilder secureRandom(SecureRandom random) {
737 +
		this.secureRandom = random;
738 +
		return this;
739 +
	}
740 +
	
741 +
	private TrustManagerFactory buildTrustManager() throws Exception {
742 +
		if (trustCerts == null) {
743 +
			return null;
744 +
		}
745 +
        
746 +
		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
747 +
        
748 +
        ks.load(null, null);
749 +
        for (int i=0; i<trustCerts.length; ++i) {
750 +
        	ks.setCertificateEntry(Integer.toString(i+1), trustCerts[i]);
751 +
        }
752 +
        
753 +
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
754 +
 
755 +
        tmf.init(ks);
756 +
		return tmf;
757 +
	}
758 +
759 +
	private KeyManagerFactory buildKeyManager() throws Exception {
760 +
		if (key == null) {
761 +
			return null;
762 +
		}
763 +
		
764 +
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
765 +
        char[] password = this.password == null ? new char[0] : this.password;
766 +
        
767 +
        ks.load(null, null);
768 +
        ks.setKeyEntry("key", key, password, keyCerts);
769 +
		
770 +
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
771 +
		
772 +
        kmf.init(ks, password);
773 +
		return kmf;
774 +
	}
775 +
	
776 +
	/**
777 +
	 * Creates a new {@link SSLEngine} builder pre-configured with the current
778 +
	 * configuration settings. The returned builder is constructed with a new
779 +
	 * {@link SSLContext} created by calling the {@link #build()} method.
780 +
	 * 
781 +
	 * @return the new {@link SSLEngine} builder
782 +
	 * @throws SSLContextCreateException if a failure occurred while building the
783 +
	 *                                   {@link SSLContext} instance used to
784 +
	 *                                   construct the new {@link SSLEngine} builder
785 +
	 */
786 +
	public SSLEngineBuilder engineBuilder() throws SSLContextCreateException {
787 +
		SSLEngineBuilder builder = new SSLEngineBuilder(build(), forServer);
788 +
		Boolean value;
789 +
		
790 +
		builder.protocols(protocols);
791 +
		builder.ciphers(ciphers);
792 +
		builder.clientAuth(clientAuth);
793 +
		builder.cipherFilter(cipherFilter);
794 +
		builder.protocolFilter(protocolFilter);
795 +
		builder.maximumPacketSize(maximumPacketSize);
796 +
		value = enableRetransmissions;
797 +
		if (value != null) {
798 +
			builder.enableRetransmissions(value.booleanValue());
799 +
		}
800 +
		value = useCiphersOrder;
801 +
		if (value != null) {
802 +
			builder.useCiphersOrder(value.booleanValue());
803 +
		}
804 +
		return builder;
805 +
	}
806 +
	
807 +
	private enum Phase {
808 +
		
809 +
		NONE(null),
810 +
		GET_DEFAULT_CTX("Getting of the default SSL context failed"),
811 +
		BUILD_TRUST_MGR_FACTORY("Building of the trust manager factory failed"),
812 +
		BUILD_KEY_MGR_FACTORY("Building of the key manager factory failed"),
813 +
		BUILD_CTX("Building of the SSL context failed");
814 +
		
815 +
		String msg;
816 +
		
817 +
		private Phase(String msg) {
818 +
			this.msg = msg;
819 +
		}
820 +
		
821 +
		private String exceptionMessage() {
822 +
			return msg;
823 +
		}
824 +
	}
825 +
	
826 +
	/**
827 +
	 * Builds a new {@link SSLContext} instance based on the current configuration
828 +
	 * settings.
829 +
	 * 
830 +
	 * @return the new {@link SSLContext} instance.
831 +
	 * @throws SSLContextCreateException if a failure occurred while building the
832 +
	 *                                   {@link SSLContext} instance
833 +
	 */
834 +
	public SSLContext build() throws SSLContextCreateException {
835 +
		String protocol = this.protocol;
836 +
		Phase phase = Phase.NONE;
837 +
		
838 +
		try {
839 +
			if (protocol == null) {
840 +
				phase = Phase.GET_DEFAULT_CTX;
841 +
				return SSLContext.getDefault();
842 +
			}
843 +
		
844 +
			TrustManagerFactory tmf;
845 +
			KeyManagerFactory kmf;
846 +
		
847 +
			phase = Phase.BUILD_TRUST_MGR_FACTORY;
848 +
			tmf = trustManager != null ? trustManager : buildTrustManager();
849 +
			
850 +
			phase = Phase.BUILD_KEY_MGR_FACTORY;
851 +
			kmf = keyManager != null ? keyManager : buildKeyManager();
852 +
		
853 +
			String providerName = this.providerName;
854 +
			Provider provider = this.provider;
855 +
			SSLContext context;
856 +
			SSLSessionContext sessionContext;
857 +
858 +
			phase = Phase.BUILD_CTX;
859 +
			if (provider != null) {
860 +
				context = SSLContext.getInstance(protocol, provider);
861 +
			}
862 +
			else if (providerName != null) {
863 +
				context = SSLContext.getInstance(protocol, providerName);
864 +
			}
865 +
			else {
866 +
				context = SSLContext.getInstance(protocol);
867 +
			}
868 +
			
869 +
			context.init(
870 +
					kmf == null ? null : kmf.getKeyManagers(), 
871 +
					tmf == null ? null : tmf.getTrustManagers(), 
872 +
					secureRandom);	
873 +
			sessionContext = forServer ? context.getServerSessionContext() : context.getClientSessionContext();
874 +
			
875 +
			if (sessionCacheSize >= 0) {
876 +
				sessionContext.setSessionCacheSize(sessionCacheSize);
877 +
			}
878 +
			if (sessionTimeout >= 0) {
879 +
				sessionContext.setSessionTimeout(sessionTimeout);
880 +
			}
881 +
			
882 +
			return context;
883 +
		}
884 +
		catch (Exception e) {
885 +
			throw new SSLContextCreateException(phase.exceptionMessage(), e);
886 +
		}
887 +
	}
888 +
	
889 +
	private void silentDestroy() {
890 +
		try {
891 +
			destroy();
892 +
		}
893 +
		catch (DestroyFailedException e) {
894 +
			//Ignore
895 +
		}
896 +
	}
897 +
	
898 +
	/**
899 +
	 * Destroys sensitive information associated with this builder (i.e. password
900 +
	 * and private key).
901 +
	 *
902 +
	 * @throws DestroyFailedException if the destroy operation failed
903 +
	 */
904 +
	@Override
905 +
	public void destroy() throws DestroyFailedException {
906 +
		if (password != null) {
907 +
			Arrays.fill(password, (char)0);
908 +
			password = null;
909 +
		}
910 +
		try {
911 +
			if (key != null) {
912 +
				key.destroy();
913 +
			}
914 +
		}
915 +
		finally {
916 +
			key = null;
917 +
			keyCerts = null;
918 +
		}
919 +
	}
920 +
	
921 +
	/**
922 +
	 * Tells if sensitive information associated with this builder is destroyed
923 +
	 * 
924 +
	 * @return {@code true} if the sensitive information is destroyed
925 +
	 */
926 +
	@Override
927 +
	public boolean isDestroyed() {
928 +
		return key == null;
929 +
	}
930 +
}

@@ -30,36 +30,31 @@
Loading
30 30
31 31
/**
32 32
 * A class with Base64 utility functions.
33 -
 * <p>
34 -
 * For JDK8 and above it uses the {@link java.util.Base64 java.util.Base64}
35 -
 * implementation.
36 33
 * 
37 34
 * @author <a href="http://snf4j.org">SNF4J.ORG</a>
38 35
 */
39 36
public final class Base64Util {
40 37
38 +
	private final static byte[] EMPTY = new byte[0];
39 +
	
41 40
	private final static byte PAD = (byte) '=';
42 41
43 42
	private final static int PAD_INDEX = 64;
44 43
45 -
	private final static byte[] ALPHABET = new byte[('Z' - 'A' + 1) * 2 + 10 + 2 + 1];
44 +
	private final static char[] ALPHABET = new char[('Z' - 'A' + 1) * 2 + 10 + 2 + 1];
46 45
47 -
	private final static byte[] DECODING = new byte[256];
48 -
49 -
	private final static String JAVA_UTIL_BASE64 = "java.util.Base64";
50 -
51 -
	private final static boolean USE_JDK;
46 +
	private final static int[] DECODING = new int[256];
52 47
53 48
	static {
54 49
		int i = 0;
55 50
56 -
		for (byte c = 'A'; c <= 'Z'; c++) {
51 +
		for (char c = 'A'; c <= 'Z'; c++) {
57 52
			ALPHABET[i++] = c;
58 53
		}
59 -
		for (byte c = 'a'; c <= 'z'; c++) {
54 +
		for (char c = 'a'; c <= 'z'; c++) {
60 55
			ALPHABET[i++] = c;
61 56
		}
62 -
		for (byte c = '0'; c <= '9'; c++) {
57 +
		for (char c = '0'; c <= '9'; c++) {
63 58
			ALPHABET[i++] = c;
64 59
		}
65 60
		ALPHABET[i++] = '+';
@@ -70,28 +65,11 @@
Loading
70 65
		for (i = 0; i < ALPHABET.length - 1; ++i) {
71 66
			DECODING[ALPHABET[i]] = (byte) i;
72 67
		}
73 -
		USE_JDK = isClass(JAVA_UTIL_BASE64);
74 68
	}
75 69
76 70
	private Base64Util() {
77 71
	}
78 72
79 -
	static boolean isClass(String className) {
80 -
		try {
81 -
			Class.forName(className);
82 -
			return true;
83 -
		} catch (Exception e) {
84 -
			return false;
85 -
		}
86 -
	}
87 -
88 -
	static byte[] encode(byte[] data, boolean useJdk) {
89 -
		if (useJdk) {
90 -
			return java.util.Base64.getEncoder().encode(data);
91 -
		}
92 -
		return encode0(data);
93 -
	}
94 -
95 73
	/**
96 74
	 * Encodes bytes from the specified byte array into a newly-allocated byte array
97 75
	 * using the Base64 encoding scheme.
@@ -102,7 +80,7 @@
Loading
102 80
	 * @return A newly-allocated byte array containing the resulting encoded bytes
103 81
	 */
104 82
	public static byte[] encode(byte[] data) {
105 -
		return encode(data, USE_JDK);
83 +
		return encode(data, 0, data.length);
106 84
	}
107 85
108 86
	/**
@@ -118,37 +96,40 @@
Loading
118 96
	 * @return A String containing the resulting Base64 encoded characters
119 97
	 */
120 98
	public static String encode(byte[] data, Charset charset) {
121 -
		return new String(encode(data, USE_JDK), charset);
99 +
		return new String(encode(data, 0, data.length), charset);
122 100
	}
123 101
124 -
	static byte[] encode0(byte[] data) {
125 -
		int len = data.length;
126 -
127 -
		if (len == 0) {
128 -
			return data;
102 +
	/**
103 +
	 * Encodes bytes from the specified byte array into a newly-allocated byte array
104 +
	 * using the Base64 encoding scheme.
105 +
	 * <p>
106 +
	 * It uses "The Base 64 Alphabet" as specified in Table 1 of RFC 4648.
107 +
	 * 
108 +
	 * @param data   the byte array to encode
109 +
	 * @param offset offset within the array of the first byte to be encoded
110 +
	 * @param length number of bytes to be encoded
111 +
	 * @return A newly-allocated byte array containing the resulting encoded bytes
112 +
	 */
113 +
	public static byte[] encode(byte[] data, int offset, int length) {
114 +
		if (length == 0) {
115 +
			return EMPTY;
129 116
		}
130 117
131 -
		byte[] encoded = new byte[(len / 3 + (len % 3 == 0 ? 0 : 1)) * 4];
132 -
		int c = 0, i = 0;
133 -
		int i1, i2, i3, i4;
134 -
135 -
		for (; i < len; i += 3) {
136 -
			if (i + 3 > len) {
137 -
				break;
138 -
			}
139 -
140 -
			i1 = data[i] >>> 2 & 0x3f;
141 -
			i2 = (data[i] << 4 | data[i + 1] >>> 4 & 0x0f) & 0x3f;
142 -
			i3 = (data[i + 1] << 2 | data[i + 2] >>> 6 & 0x03) & 0x3f;
143 -
			i4 = data[i + 2] & 0x3f;
118 +
		int end = (length / 3) * 3 + offset;
119 +
		byte[] encoded = new byte[(length / 3 + (length % 3 == 0 ? 0 : 1)) * 4];
120 +
		int c = 0, i = offset, v;
144 121
145 -
			encoded[c++] = ALPHABET[i1];
146 -
			encoded[c++] = ALPHABET[i2];
147 -
			encoded[c++] = ALPHABET[i3];
148 -
			encoded[c++] = ALPHABET[i4];
122 +
		while (i < end) {
123 +
            v = (data[i++] & 0xff) << 16 | (data[i++] & 0xff) <<  8 | (data[i++] & 0xff);
124 +
			encoded[c++] = (byte)ALPHABET[(v >>> 18) & 0x3f];
125 +
			encoded[c++] = (byte)ALPHABET[(v >>> 12) & 0x3f];
126 +
			encoded[c++] = (byte)ALPHABET[(v >>> 6)  & 0x3f];
127 +
			encoded[c++] = (byte)ALPHABET[v & 0x3f];
149 128
		}
150 129
151 -
		switch (len - i) {
130 +
		int i1, i2, i3, i4;
131 +
		
132 +
		switch (length-i+offset) {
152 133
		case 1:
153 134
			i1 = data[i] >>> 2 & 0x3f;
154 135
			i2 = data[i] << 4 & 0x3f;
@@ -167,30 +148,31 @@
Loading
167 148
			return encoded;
168 149
		}
169 150
170 -
		encoded[c++] = ALPHABET[i1];
171 -
		encoded[c++] = ALPHABET[i2];
172 -
		encoded[c++] = ALPHABET[i3];
173 -
		encoded[c++] = ALPHABET[i4];
151 +
		encoded[c++] = (byte)ALPHABET[i1];
152 +
		encoded[c++] = (byte)ALPHABET[i2];
153 +
		encoded[c++] = (byte)ALPHABET[i3];
154 +
		encoded[c++] = (byte)ALPHABET[i4];
174 155
		return encoded;
175 156
	}
176 157
177 -
	static String encode0(byte[] data, Charset charset) {
178 -
		return new String(encode0(data), charset);
179 -
	}
180 -
181 -
	static byte[] decode(byte[] data, boolean useJdk) {
182 -
		if (useJdk) {
183 -
			return java.util.Base64.getDecoder().decode(data);
184 -
		}
185 -
186 -
		byte[] encoded = decode0(data);
187 -
188 -
		if (encoded == null) {
189 -
			throw new IllegalArgumentException("data is not in valid Base64 scheme");
190 -
		}
191 -
		return encoded;
158 +
	/**
159 +
	 * Encodes the specified byte array into a String using the Base64 encoding
160 +
	 * scheme.
161 +
	 * <p>
162 +
	 * It first encodes input bytes into a base64 encoded byte array by calling the
163 +
	 * {@link #encode(byte[])} method and then constructs a new String by using the
164 +
	 * encoded byte array and the specified charset.
165 +
	 * 
166 +
	 * @param data    the byte array to encode
167 +
	 * @param offset  offset within the array of the first byte to be encoded
168 +
	 * @param length  number of bytes to be encoded
169 +
	 * @param charset the charset used to encode the resulting String
170 +
	 * @return A String containing the resulting Base64 encoded characters
171 +
	 */
172 +
	public static String encode(byte[] data, int offset, int length, Charset charset) {
173 +
		return new String(encode(data, offset, length), charset);
192 174
	}
193 -
175 +
	
194 176
	/**
195 177
	 * Decodes bytes from the specified byte array into a newly-allocated byte array
196 178
	 * using the Base64 encoding scheme.
@@ -202,9 +184,24 @@
Loading
202 184
	 * @throws IllegalArgumentException - if the data is not in valid Base64 scheme
203 185
	 */
204 186
	public static byte[] decode(byte[] data) {
205 -
		return decode(data, USE_JDK);
187 +
		return decode(data, 0, data.length, false);
206 188
	}
207 189
190 +
	/**
191 +
	 * Decodes bytes from the specified byte array into a newly-allocated byte array
192 +
	 * using the Base64 encoding scheme with an option for the MIME format.
193 +
	 * <p>
194 +
	 * It uses "The Base 64 Alphabet" as specified in Table 1 of RFC 4648.
195 +
	 * 
196 +
	 * @param data   the byte array to decode
197 +
	 * @param isMime {@code true} if the data is encoded in the MIME format
198 +
	 * @return A newly-allocated byte array containing the resulting decoded bytes,
199 +
	 *         or {@code null} if the data is not in valid Base64 scheme
200 +
	 */
201 +
	public static byte[] decode(byte[] data, boolean isMime) {
202 +
		return decode(data, 0, data.length, isMime);
203 +
	}
204 +
	
208 205
	/**
209 206
	 * Decodes a Base64 encoded String into a newly-allocated byte array using the
210 207
	 * Base64 encoding scheme.
@@ -215,36 +212,85 @@
Loading
215 212
	 * 
216 213
	 * @param data    the string to decode
217 214
	 * @param charset The charset to be used to encode the String
218 -
	 * @return A newly-allocated byte array containing the resulting decoded bytes
219 -
	 * @throws IllegalArgumentException - if the data is not in valid Base64 scheme
215 +
	 * @return A newly-allocated byte array containing the resulting decoded bytes,
216 +
	 *         or {@code null} if the data is not in valid Base64 scheme
220 217
	 */
221 218
	public static byte[] decode(String data, Charset charset) {
222 -
		return decode(data.getBytes(charset), USE_JDK);
219 +
		return decode(data.getBytes(charset), false);
223 220
	}
224 221
225 -
	static byte[] decode0(byte[] data) {
226 -
		int len = data.length;
222 +
	/**
223 +
	 * Decodes a Base64 encoded String into a newly-allocated byte array using the
224 +
	 * Base64 encoding scheme with an option for the MIME format.
225 +
	 * <p>
226 +
	 * It first decodes the Base64 encoded String into a sequence of bytes using the
227 +
	 * given charset and then decode the bytes by calling the
228 +
	 * {@link #decode(byte[])} method.
229 +
	 * 
230 +
	 * @param data    the string to decode
231 +
	 * @param isMime  {@code true} if the data is encoded in the MIME format
232 +
	 * @param charset The charset to be used to encode the String
233 +
	 * @return A newly-allocated byte array containing the resulting decoded bytes,
234 +
	 *         or {@code null} if the data is not in valid Base64 scheme
235 +
	 */
236 +
	public static byte[] decode(String data, Charset charset, boolean isMime) {
237 +
		return decode(data.getBytes(charset), isMime);
238 +
	}
227 239
228 -
		if (len == 0) {
229 -
			return data;
230 -
		} else if (len < 2) {
240 +
	/**
241 +
	 * Decodes bytes from the specified byte array into a newly-allocated byte array
242 +
	 * using the Base64 encoding scheme with an option for the MIME format.
243 +
	 * <p>
244 +
	 * It uses "The Base 64 Alphabet" as specified in Table 1 of RFC 4648.
245 +
	 * 
246 +
	 * @param data   the byte array to decode
247 +
	 * @param offset offset within the array of the first byte to be decoded
248 +
	 * @param length number of bytes to be encoded
249 +
	 * @param isMime {@code true} if the data is encoded in the MIME format
250 +
	 * @return A newly-allocated byte array containing the resulting decoded bytes,
251 +
	 *         or {@code null} if the data is not in valid Base64 scheme
252 +
	 */
253 +
	public static byte[] decode(byte[] data, int offset, int length, boolean isMime) {
254 +
		int end = offset+length;
255 +
		int origEnd = end;
256 +
		int ignored = 0;
257 +
		
258 +
		if (length == 0) {
259 +
			return EMPTY;
260 +
		} else if (length < 2) {
231 261
			return null;
232 262
		}
233 263
234 -
		if (data[len - 1] == PAD) {
235 -
			--len;
236 -
			if (data[len - 1] == PAD) {
237 -
				--len;
264 +
		if (isMime) {
265 +
			for (int i=offset; i<end; ++i) {
266 +
				byte b = data[i];
267 +
				
268 +
				if (b == PAD) {
269 +
					end = i;
270 +
					length = end - offset;
271 +
					break;
272 +
				}
273 +
				if (DECODING[b & 0xff] == -1) {
274 +
					++ignored;
275 +
				}
276 +
			}
277 +
		}
278 +
		else if (data[end - 1] == PAD) {
279 +
			--end;
280 +
			--length;
281 +
			if (data[end - 1] == PAD) {
282 +
				--end;
283 +
				--length;
238 284
			}
239 285
		}
240 286
241 -
		if (len == 0) {
242 -
			return new byte[0];
287 +
		if (length == 0) {
288 +
			return EMPTY;
243 289
		}
244 290
245 -
		int calcLen = (len / 4) * 3;
291 +
		int calcLen = ((length-ignored) / 4) * 3;
246 292
247 -
		switch (len & 0x03) {
293 +
		switch ((length-ignored) & 0x03) {
248 294
		case 1:
249 295
			return null;
250 296
@@ -259,45 +305,54 @@
Loading
259 305
		}
260 306
261 307
		byte[] decoded = new byte[calcLen];
262 -
		int d = 0, i = 0;
263 -
		int v, jlen, shift;
264 -
265 -
		for (; i < len; i += 4) {
266 -
			jlen = len - i;
267 -
			if (jlen > 4) {
268 -
				jlen = 4;
269 -
				shift = 0;
270 -
			} else {
271 -
				shift = (4 - jlen) * 6;
272 -
			}
273 -
274 -
			v = 0;
275 -
			for (int j = 0; j < jlen; ++j) {
276 -
				byte c = DECODING[data[i + j] & 0xff];
277 -
278 -
				if (c == -1) {
279 -
					return null;
308 +
		int d = 0;
309 +
		int v = 0, vcount = 0;
310 +
		
311 +
		for (int i=offset; i<end; ++i) {
312 +
			int c = DECODING[data[i] & 0xff];
313 +
			
314 +
			if (c == -1) {
315 +
				if (isMime) {
316 +
					continue;
280 317
				}
281 -
				v <<= 6;
282 -
				v |= c;
318 +
				return null;
283 319
			}
284 -
			if (shift > 0) {
285 -
				decoded[d++] = (byte) (v >> (16 - shift));
286 -
				if (shift == 6) {
287 -
					decoded[d++] = (byte) (v >> (8 - shift));
288 -
				}
289 -
			} else {
320 +
			v <<= 6;
321 +
			v |= c;
322 +
			if (vcount == 3) {
290 323
				decoded[d++] = (byte) (v >> 16);
291 324
				decoded[d++] = (byte) (v >> 8);
292 325
				decoded[d++] = (byte) v;
326 +
				vcount = 0;
327 +
				v = 0;
328 +
			}
329 +
			else {
330 +
				++vcount;
293 331
			}
294 332
		}
333 +
		
334 +
		if (vcount > 0) {
335 +
			int shift = (4 - vcount) * 6;
295 336
337 +
			decoded[d++] = (byte) (v >> (16 - shift));
338 +
			if (shift == 6) {
339 +
				decoded[d++] = (byte) (v >> (8 - shift));
340 +
			}
341 +
		}
342 +
		
343 +
		if (isMime && end < origEnd) {
344 +
			for (; end < origEnd; ++end) {
345 +
				byte b = data[end];
346 +
				
347 +
				if (b == PAD) {
348 +
					continue;
349 +
				}
350 +
				if (DECODING[b & 0xff] != -1) {
351 +
					return null;
352 +
				}
353 +
			}
354 +
		}
296 355
		return decoded;
297 356
	}
298 357
299 -
	static byte[] decode0(String data, Charset charset) {
300 -
		return decode0(data.getBytes(charset));
301 -
	}
302 -
303 358
}

@@ -0,0 +1,203 @@
Loading
1 +
/*
2 +
 * -------------------------------- MIT License --------------------------------
3 +
 * 
4 +
 * Copyright (c) 2021 SNF4J contributors
5 +
 * 
6 +
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 +
 * of this software and associated documentation files (the "Software"), to deal
8 +
 * in the Software without restriction, including without limitation the rights
9 +
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 +
 * copies of the Software, and to permit persons to whom the Software is
11 +
 * furnished to do so, subject to the following conditions:
12 +
 * 
13 +
 * The above copyright notice and this permission notice shall be included in all
14 +
 * copies or substantial portions of the Software.
15 +
 * 
16 +
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 +
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 +
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 +
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 +
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 +
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 +
 * SOFTWARE.
23 +
 *
24 +
 * -----------------------------------------------------------------------------
25 +
 */
26 +
package org.snf4j.core.session.ssl;
27 +
28 +
import java.util.ArrayList;
29 +
import java.util.HashSet;
30 +
import java.util.List;
31 +
import java.util.Set;
32 +
33 +
import javax.net.ssl.SSLContext;
34 +
import javax.net.ssl.SSLEngine;
35 +
36 +
class ProtocolDefaults {
37 +
	
38 +
	final static String TLS = "TLS";
39 +
40 +
	final static String DTLS = "DTLS";
41 +
	
42 +
	static volatile ProtocolDefaults tlsDefaults;
43 +
	
44 +
	static volatile ProtocolDefaults dtlsDefaults;
45 +
	
46 +
	final static String[] PROTOCOLS = new String[] {
47 +
			"TLSv1.3", 
48 +
			"TLSv1.2", 
49 +
			"TLSv1.1", 
50 +
			"TLSv1",
51 +
			"DTLSv1.2", 
52 +
			"DTLSv1.0"
53 +
			};
54 +
	
55 +
	final static String[] CIPHERS = new String[] {
56 +
			"TLS_AES_128_GCM_SHA256",
57 +
			"TLS_AES_256_GCM_SHA384",
58 +
			"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
59 +
			"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
60 +
			"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
61 +
			"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
62 +
			"TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
63 +
			"TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",
64 +
			"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
65 +
			"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"
66 +
			};
67 +
68 +
	private final Set<String> supportedCiphers;
69 +
	
70 +
	private final String[] defaultCiphers;
71 +
72 +
	private final Set<String> supportedProtocols;
73 +
	
74 +
	private final String[] defaultProtocols;
75 +
	
76 +
	ProtocolDefaults(String protocol) {
77 +
		SSLEngine engine = ProtocolDefaults.defaultEngine(protocol);
78 +
		
79 +
		supportedCiphers = ProtocolDefaults.supportedCiphers(engine);
80 +
		defaultCiphers = ProtocolDefaults.defaultCiphers(engine, supportedCiphers);
81 +
		supportedProtocols = ProtocolDefaults.supportedProtocols(engine);
82 +
		defaultProtocols = ProtocolDefaults.defaultProtocols(engine, supportedProtocols);
83 +
	}
84 +
	
85 +
	Set<String> supportedCiphers() {
86 +
		return supportedCiphers;
87 +
	}
88 +
89 +
	String[] defaultCiphers() {
90 +
		return defaultCiphers;
91 +
	}
92 +
93 +
	Set<String> supportedProtocols() {
94 +
		return supportedProtocols;
95 +
	}
96 +
97 +
	String[] defaultProtocols() {
98 +
		return defaultProtocols;
99 +
	}
100 +
	
101 +
	static ProtocolDefaults instance(SSLEngine engine) {
102 +
		return instance(isDtls(engine));
103 +
	}
104 +
105 +
	static ProtocolDefaults instance(boolean dtls) {
106 +
		if (dtls) {
107 +
			if (dtlsDefaults == null) {
108 +
				synchronized (ProtocolDefaults.class) {
109 +
					if (dtlsDefaults == null) {
110 +
						dtlsDefaults = new ProtocolDefaults(DTLS);
111 +
					}
112 +
				}
113 +
			}
114 +
			return dtlsDefaults;
115 +
		}
116 +
		if (tlsDefaults == null) {
117 +
			synchronized (ProtocolDefaults.class) {
118 +
				if (tlsDefaults == null) {
119 +
					tlsDefaults = new ProtocolDefaults(TLS);
120 +
				}
121 +
			}
122 +
		}
123 +
		return tlsDefaults;
124 +
	}
125 +
	
126 +
	static boolean isDtls(SSLEngine engine) {
127 +
		String[] protocols = engine.getSupportedProtocols();
128 +
		
129 +
		for (String protocol: protocols) {
130 +
			if (protocol.startsWith(DTLS)) {
131 +
				return true;
132 +
			}
133 +
		}
134 +
		return false;
135 +
	}
136 +
	
137 +
	static SSLEngine defaultEngine(String protocol) throws Error {
138 +
		SSLContext context;
139 +
		
140 +
		try {
141 +
			context = SSLContext.getInstance(protocol);
142 +
			context.init(null, null, null);
143 +
		}
144 +
		catch (Exception e) {
145 +
			throw new Error("Initialization of SSL context for protocol " + protocol + " failed", e);
146 +
		}
147 +
		return context.createSSLEngine();
148 +
	}
149 +
	
150 +
	static Set<String> supportedCiphers(SSLEngine engine) {
151 +
		String[] supported = engine.getSupportedCipherSuites();
152 +
		Set<String> ciphers = new HashSet<String>(supported.length);
153 +
		
154 +
		for (String cipher: supported) {
155 +
			ciphers.add(cipher);
156 +
		}
157 +
		return ciphers;
158 +
	}
159 +
	
160 +
	static String[] defaultCiphers(SSLEngine engine, Set<String> supportedCiphers) {
161 +
		List<String> defaultList = new ArrayList<String>(CIPHERS.length);
162 +
		
163 +
		for (String cipher: CIPHERS) {
164 +
			if (supportedCiphers.contains(cipher)) {
165 +
				defaultList.add(cipher);
166 +
			}
167 +
		}
168 +
		if (defaultList.isEmpty()) {
169 +
			String[] ciphers = engine.getEnabledCipherSuites();
170 +
			for (String cipher: ciphers) {
171 +
				defaultList.add(cipher);
172 +
			}
173 +
		}
174 +
		return defaultList.toArray(new String[defaultList.size()]);
175 +
	}
176 +
177 +
	static Set<String> supportedProtocols(SSLEngine engine) {
178 +
		String[] supported = engine.getSupportedProtocols();
179 +
		Set<String> protocols = new HashSet<String>(supported.length);
180 +
		
181 +
		for (String protocol: supported) {
182 +
			protocols.add(protocol);
183 +
		}
184 +
		return protocols;
185 +
	}
186 +
	
187 +
	static String[] defaultProtocols(SSLEngine engine, Set<String> supportedPtotocols) {
188 +
		List<String> defaultList = new ArrayList<String>(PROTOCOLS.length);
189 +
		
190 +
		for (String protocol: PROTOCOLS) {
191 +
			if (supportedPtotocols.contains(protocol)) {
192 +
				defaultList.add(protocol);
193 +
			}
194 +
		}
195 +
		if (defaultList.isEmpty()) {
196 +
			String[] protocols = engine.getEnabledProtocols();
197 +
			for (String protocol: protocols) {
198 +
				defaultList.add(protocol);
199 +
			}
200 +
		}
201 +
		return defaultList.toArray(new String[defaultList.size()]);
202 +
	}
203 +
}

@@ -0,0 +1,342 @@
Loading
1 +
/*
2 +
 * -------------------------------- MIT License --------------------------------
3 +
 * 
4 +
 * Copyright (c) 2021 SNF4J contributors
5 +
 * 
6 +
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 +
 * of this software and associated documentation files (the "Software"), to deal
8 +
 * in the Software without restriction, including without limitation the rights
9 +
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 +
 * copies of the Software, and to permit persons to whom the Software is
11 +
 * furnished to do so, subject to the following conditions:
12 +
 * 
13 +
 * The above copyright notice and this permission notice shall be included in all
14 +
 * copies or substantial portions of the Software.
15 +
 * 
16 +
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 +
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 +
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 +
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 +
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 +
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 +
 * SOFTWARE.
23 +
 *
24 +
 * -----------------------------------------------------------------------------
25 +
 */
26 +
package org.snf4j.core.session.ssl;
27 +
28 +
import java.lang.reflect.Method;
29 +
30 +
import javax.net.ssl.SSLContext;
31 +
import javax.net.ssl.SSLEngine;
32 +
import javax.net.ssl.SSLParameters;
33 +
34 +
import org.snf4j.core.session.SSLEngineCreateException;
35 +
36 +
/**
37 +
 * A builder for the {@link SSLEngine}.
38 +
 *  
39 +
 * @author <a href="http://snf4j.org">SNF4J.ORG</a>
40 +
 */
41 +
public class SSLEngineBuilder implements Cloneable {
42 +
	
43 +
	private final static Method ENABLE_RETRANSMISSIONS = get("setEnableRetransmissions", boolean.class);
44 +
45 +
	private final static Method MAXIMUM_PACKET_SIZE = get("setMaximumPacketSize", int.class);
46 +
	
47 +
	private final boolean forServer;
48 +
49 +
	private final SSLContext context;
50 +
	
51 +
	private String[] protocols;
52 +
53 +
	private ProtocolFilter protocolFilter = DefaultCipherProtocolFilters.INSATNCE;
54 +
55 +
	private String[] ciphers;
56 +
	
57 +
	private CipherFilter cipherFilter = DefaultCipherProtocolFilters.INSATNCE;
58 +
59 +
	private Boolean enableRetransmissions;	//JDK9
60 +
	
61 +
	private int maximumPacketSize = -1; //JDK9
62 +
	
63 +
	private Boolean useCiphersOrder;
64 +
	
65 +
	private ClientAuth clientAuth = ClientAuth.NONE;
66 +
	
67 +
	SSLEngineBuilder(SSLContext context, boolean forServer) {
68 +
		this.context = context;
69 +
		this.forServer = forServer;
70 +
	}
71 +
	
72 +
	static Method get(String name, Class<?>... parameterTypes) {
73 +
		try {
74 +
			Method method = SSLParameters.class.getDeclaredMethod(name, parameterTypes);
75 +
			
76 +
			method.setAccessible(true);
77 +
			return method;
78 +
		} catch (Exception e) {}
79 +
		return null;
80 +
	}
81 +
	
82 +
	static void set(Method method, SSLParameters params, Object... args) throws Exception {
83 +
		if (method != null) {
84 +
			method.invoke(params, args);
85 +
		}
86 +
	}
87 +
	
88 +
	/**
89 +
	 * Creates a builder for a client-side {@link SSLEngine}.
90 +
	 * 
91 +
	 * @param context the SSL context used by the builder to create
92 +
	 *                {@link SSLEngine}
93 +
	 * @return a builder for a client-side {@link SSLEngine}
94 +
	 */
95 +
	public static SSLEngineBuilder forClient(SSLContext context) {
96 +
		return new SSLEngineBuilder(context, false);
97 +
	}
98 +
	
99 +
	/**
100 +
	 * Creates a builder for a server-side {@link SSLEngine}.
101 +
	 * 
102 +
	 * @param context the SSL context used by the builder to create
103 +
	 *                {@link SSLEngine}
104 +
	 * @return a builder for a server-side {@link SSLEngine}
105 +
	 */
106 +
	public static SSLEngineBuilder forServer(SSLContext context) {
107 +
		return new SSLEngineBuilder(context, true);
108 +
	}
109 +
	
110 +
	/**
111 +
	 * Returns the SSL context used by the builder to create {@link SSLEngine}
112 +
	 * 
113 +
	 * @return the SSL context
114 +
	 */
115 +
	public SSLContext context() {
116 +
		return context;
117 +
	}
118 +
	
119 +
	/**
120 +
	 * Tells if the builder if for a server-side {@link SSLEngine}.
121 +
	 * 
122 +
	 * @return {@code true} if the builder if for a server-side {@link SSLEngine}
123 +
	 */
124 +
	public boolean isForServer() {
125 +
		return forServer;
126 +
	}
127 +
	
128 +
	/**
129 +
	 * Tells if the builder if for a client-side {@link SSLEngine}.
130 +
	 * 
131 +
	 * @return {@code true} if the builder if for a client-side {@link SSLEngine}
132 +
	 */
133 +
	public boolean isForClient() {
134 +
		return !forServer;
135 +
	}
136 +
	
137 +
	/**
138 +
	 * Configures protocol versions to enable, or {@code null} to enable the
139 +
	 * recommended protocol versions.
140 +
	 * 
141 +
	 * @param protocols the protocol versions
142 +
	 * @return this builder
143 +
	 */
144 +
	public SSLEngineBuilder protocols(String... protocols) {
145 +
		this.protocols = protocols == null ? null : protocols.clone();
146 +
		return this;
147 +
	}
148 +
149 +
	/**
150 +
	 * Configures a filter for protocol versions to enable, or {@code null} to use
151 +
	 * the default filter.
152 +
	 * 
153 +
	 * @param filter the protocol filter
154 +
	 * @return this builder
155 +
	 */
156 +
	public SSLEngineBuilder protocolFilter(ProtocolFilter filter) {
157 +
		protocolFilter = filter == null ? DefaultCipherProtocolFilters.INSATNCE : filter;
158 +
		return this;
159 +
	}
160 +
161 +
	/**
162 +
	 * Configures cipher suites to enable, or {@code null} to enable the
163 +
	 * recommended cipher suites.
164 +
	 * 
165 +
	 * @param ciphers the cipher suites
166 +
	 * @return this builder
167 +
	 */
168 +
	public SSLEngineBuilder ciphers(String... ciphers) {
169 +
		this.ciphers = ciphers == null ? null : ciphers.clone();
170 +
		return this;
171 +
	}
172 +
173 +
	/**
174 +
	 * Configures a filter for cipher suites to enable, or {@code null} to use
175 +
	 * the default filter.
176 +
	 * 
177 +
	 * @param filter the cipher filter
178 +
	 * @return this builder
179 +
	 */
180 +
	public SSLEngineBuilder cipherFilter(CipherFilter filter) {
181 +
		cipherFilter = filter == null ? DefaultCipherProtocolFilters.INSATNCE : filter;
182 +
		return this;
183 +
	}
184 +
	
185 +
	/**
186 +
	 * Configures if DTLS handshake retransmissions should be enabled.
187 +
	 * <p>
188 +
	 * NOTE: It requires Java 9 or newer.
189 +
	 * 
190 +
	 * @param enable {@code true} to enable DTLS handshake retransmissions.
191 +
	 * @return this builder
192 +
	 */
193 +
	public SSLEngineBuilder enableRetransmissions(boolean enable) {
194 +
		enableRetransmissions = enable ? Boolean.TRUE : Boolean.FALSE;
195 +
		return this;
196 +
	}
197 +
	
198 +
	/**
199 +
	 * Configures the maximum expected network packet size.
200 +
	 * <p>
201 +
	 * NOTE: It requires Java 9 or newer.
202 +
	 * 
203 +
	 * @param maxSize the maximum expected network packet size in bytes, or 0 to use
204 +
	 *                the default value that is specified by the underlying
205 +
	 *                implementation.
206 +
	 * @return this builder
207 +
	 */
208 +
	public SSLEngineBuilder maximumPacketSize(int maxSize) {
209 +
		maximumPacketSize = maxSize;
210 +
		return this;
211 +
	}
212 +
	
213 +
	/**
214 +
	 * Configures if the local cipher suites preferences should be honored during
215 +
	 * SSL/TLS/DTLS handshaking
216 +
	 * 
217 +
	 * @param useOrder {@code true} to honor the local cipher suites preferences
218 +
	 * @return this builder
219 +
	 */
220 +
	public SSLEngineBuilder useCiphersOrder(boolean useOrder) {
221 +
		useCiphersOrder = useOrder ? Boolean.TRUE : Boolean.FALSE;
222 +
		return this;
223 +
	}
224 +
	
225 +
	/**
226 +
	 * Configures the client authentication mode for a server-side
227 +
	 * {@link SSLEngine}.
228 +
	 * 
229 +
	 * @param clientAuth the client authentication mode.
230 +
	 * @return this builder
231 +
	 */
232 +
	public SSLEngineBuilder clientAuth(ClientAuth clientAuth) {
233 +
		this.clientAuth = clientAuth;
234 +
		return this;
235 +
	}
236 +
	
237 +
	private SSLEngine configure(SSLEngine engine) throws Exception {
238 +
		ProtocolDefaults defaults = ProtocolDefaults.instance(engine);
239 +
		
240 +
		engine.setUseClientMode(!forServer);
241 +
		engine.setEnabledProtocols(protocolFilter.filterProtocols(
242 +
				protocols,
243 +
				defaults.defaultProtocols(),
244 +
				defaults.supportedProtocols()
245 +
				));
246 +
		engine.setEnabledCipherSuites(cipherFilter.filterCiphers(
247 +
				ciphers, 
248 +
				defaults.defaultCiphers(),
249 +
				defaults.supportedCiphers()
250 +
				));
251 +
		
252 +
		if (forServer) {
253 +
			switch (clientAuth) {
254 +
			case REQUESTED:
255 +
				engine.setWantClientAuth(true);
256 +
				break;
257 +
				
258 +
			case REQUIRED:
259 +
				engine.setNeedClientAuth(true);
260 +
				break;
261 +
				
262 +
			default:		
263 +
			}
264 +
		}
265 +
		
266 +
		if (maximumPacketSize >= 0 || enableRetransmissions != null || useCiphersOrder != null) {
267 +
			SSLParameters params = engine.getSSLParameters();
268 +
			
269 +
			if (useCiphersOrder != null) {
270 +
				params.setUseCipherSuitesOrder(useCiphersOrder);
271 +
			}
272 +
			if (maximumPacketSize >= 0) {
273 +
				set(MAXIMUM_PACKET_SIZE, params, maximumPacketSize);
274 +
			}
275 +
			if (enableRetransmissions != null) {
276 +
				set(ENABLE_RETRANSMISSIONS, params, enableRetransmissions);
277 +
			}
278 +
			engine.setSSLParameters(params);
279 +
		}
280 +
		
281 +
		return engine;
282 +
	}
283 +
	
284 +
	/**
285 +
	 * Builds a new {@link SSLEngine} instance based on the current configuration
286 +
	 * settings.
287 +
	 * 
288 +
	 * @return the new {@link SSLEngine} instance.
289 +
	 * @throws SSLEngineCreateException if a failure occurred while building the
290 +
	 *                                   {@link SSLEngine} instance
291 +
	 */
292 +
	public SSLEngine build() throws SSLEngineCreateException {
293 +
		try {
294 +
			return configure(context.createSSLEngine());
295 +
		}
296 +
		catch (Exception e) {
297 +
			throw new SSLEngineCreateException("Building of SSL engine failed", e);
298 +
		}
299 +
	}
300 +
301 +
	/**
302 +
	 * Builds a new {@link SSLEngine} instance based on the current configuration
303 +
	 * settings and advisory peer information.
304 +
	 * 
305 +
	 * @param peerHost the non-authoritative name of the host
306 +
	 * @param peerPort the non-authoritative port
307 +
	 * @return the new {@link SSLEngine} instance.
308 +
	 * @throws SSLEngineCreateException if a failure occurred while building the
309 +
	 *                                  {@link SSLEngine} instance
310 +
	 * @see SSLContext#createSSLEngine(String, int)
311 +
	 */
312 +
	public SSLEngine build(String peerHost, int peerPort) throws SSLEngineCreateException {
313 +
		try {
314 +
			return configure(context.createSSLEngine(peerHost, peerPort));
315 +
		}
316 +
		catch (Exception e) {
317 +
			throw new SSLEngineCreateException("Building of SSL engine with peer information failed", e);
318 +
		}
319 +
	}
320 +
	
321 +
	SSLEngineBuilder superClone() throws CloneNotSupportedException {
322 +
		return (SSLEngineBuilder) super.clone();
323 +
	}
324 +
	
325 +
	/**
326 +
	 * Generates a new copy of this builder. Subsequent changes to this builder will
327 +
	 * not affect the new copy, and vice versa.
328 +
	 */
329 +
	@Override
330 +
	public SSLEngineBuilder clone() {
331 +
		SSLEngineBuilder b;
332 +
		
333 +
		try {
334 +
			b = superClone();
335 +
		} catch (CloneNotSupportedException e) {
336 +
			throw new RuntimeException(e);
337 +
		}
338 +
		b.protocols(protocols);
339 +
		b.ciphers(ciphers);
340 +
		return b;
341 +
	}
342 +
}

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Click to load this diff.
Loading diff...

Learn more Showing 10 files with coverage changes found.

New file snf4j-core/src/main/java/org/snf4j/core/session/ssl/ClientAuth.java
New
Loading file...
New file snf4j-core/src/main/java/org/snf4j/core/session/ssl/SSLContextBuilder.java
New
Loading file...
New file snf4j-core/src/main/java/org/snf4j/core/session/ssl/SSLContextCreateException.java
New
Loading file...
New file snf4j-core/src/main/java/org/snf4j/core/session/ssl/DefaultCipherProtocolFilters.java
New
Loading file...
New file snf4j-core/src/main/java/org/snf4j/core/session/ssl/SupportedCipherProtocolFilters.java
New
Loading file...
New file snf4j-core/src/main/java/org/snf4j/core/util/PemUtil.java
New
Loading file...
New file snf4j-core/src/main/java/org/snf4j/core/session/ssl/ProtocolDefaults.java
New
Loading file...
New file snf4j-core/src/main/java/org/snf4j/core/session/ssl/SSLEngineBuilder.java
New
Loading file...
Changes in snf4j-core/src/main/java/org/snf4j/core/AbstractEngineHandler.java
-1
+1
Loading file...
Changes in snf4j-core/src/main/java/org/snf4j/core/session/DefaultSessionConfig.java
-1
+1
Loading file...
Files Coverage
snf4j-core-log4j2/src/main/java/org/snf4j/core/logger/impl 96.66%
snf4j-core-slf4j/src/main/java/org/snf4j/core/logger/impl 96.66%
snf4j-core/src/main/java/org/snf4j/core 0.03% 97.88%
snf4j-sctp/src/main/java/org/snf4j/core 98.56%
snf4j-websocket/src/main/java/org/snf4j/websocket -0.01% 99.37%
Project Totals (227 files) 98.17%
Loading