I'm doing research on GenericObjectPool by putting Cipher in pool so it can be reused.
GenericObjectPool<Cipher> pool;
CipherFactory factory = new CipherFactory();
this.pool = new GenericObjectPool<Cipher>(factory);
pool.setMaxTotal(10);
pool.setBlockWhenExhausted(true);
pool.setMaxWaitMillis(30 * 1000);
CipherFactory
public class CipherFactory extends BasePooledObjectFactory<Cipher> {
private boolean running = false;
#Override
public Cipher create() throws Exception {
return Cipher.getInstance("DESede/CBC/NoPadding");
}
#Override
public PooledObject<Cipher> wrap(Cipher arg0) {
return new DefaultPooledObject<Cipher>(arg0);
}
#Override
public boolean validateObject(PooledObject<Cipher> p) {
//Ensures that the instance is safe to be returned by the pool
return true;
}
#Override
public void destroyObject(PooledObject<Cipher> p) {
//Destroys an instance no longer needed by the pool.
System.out.println("destroying");
}
#Override
public void activateObject(PooledObject<Cipher> p) throws Exception { //Reinitialize an instance to be returned by the pool
setRunning(true);
}
#Override
public void passivateObject(PooledObject<Cipher> p) throws Exception { // reset the object after the object returns to the pool
setRunning(false);
}
public void setRunning(boolean running) {
this.running = running;
}
//
}
This is how I implement ObjectPool in my Example class
public Key a(byte[] afyte) throws Exception {
Cipher cipher = null;
cipher = pool.borrowObject(); //get the object from the pool
try {
System.out.println("****************** After borrow ****************");
printPool();
cipher.init(Cipher.DECRYPT_MODE, mkkey, algParamSpec);
byte[] de = cipher.doFinal(afyte);
SecretKey mk = new SecretKeySpec(de, "DESede");
return mk;
} catch (Exception e) {
pool.invalidateObject(cipher);
cipher = null;
} finally {
if (null != cipher) {
pool.returnObject(cipher);
System.out.println("****************** After return ****************");
printPool();
}
}
return (Key) cipher;
}
printPool
public void printPool() {
System.out.println("Pool for cipher with instances DESede/CBC/NoPadding");
System.out.println("Active [" + pool.getNumActive() + "]"); //Return the number of instances currently borrowed from this pool
System.out.println("Idle [" + pool.getNumIdle() + "]"); //The number of instances currently idle in this pool
System.out.println("Total Created [" + pool.getCreatedCount() + "]");
}
Am I on the right path ? Is it possible to increase pool size ?
Edit
The answer from #http works fine to me. But if I have another method encryptECB(Key key, byte[] b), how should I write ?
Any help would be appreciated !
You are on the right track. When constructing the GenericObjectPool, you can use the constructor that accepts a GenericObjectPoolConfig object which contains all the configuration values for your object pool. The example below would let your pool grow to 20 connections before it was exhausted...
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMinIdle(2);
config.setMaxIdle(5);
config.setMaxTotal(20);
GenericObjectPool<Cipher> pool;
CipherFactory factory = new CipherFactory();
this.pool = new GenericObjectPool<Cipher>(factory, config);
GenericeObjectPoolConfig also has a setBlockWhenExhausted method to specify the behaviour when the pool has reached the maxTotal connections. See https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/BaseObjectPoolConfig.html#setBlockWhenExhausted-boolean- for details.
A pattern I implement when using commons pool is to create 2 interfaces, one for your pooled object and one for your factory...
public interface PooledCipher extends java.io.Closeable {
byte[] doFinal(byte[] bytes) throws Exception;
SecretKeySpec getSecretKeySpec(byte[] bytes) throws Exception;
}
public interface CipherFactory {
PooledCipher getCipher() throws Exception;
void close();
}
CipherFactory implementation...
public class CipherFactoryImpl extends BasePooledObjectFactory<PooledCipher>
implements CipherFactory {
private final GenericObjectPoolConfig config;
private final GenericObjectPool<PooledCipher> pool;
private final String transformation;
private final int opmode;
private final Key key;
private final AlgorithmParameters params;
private final String secretKeySpecAlgorithm;
public CipherFactoryImpl(GenericObjectPoolConfig config, String transformation, int opmode, Key key, AlgorithmParameters params, String secretKeySpecAlgorithm) {
this.config = config;
this.pool = new GenericObjectPool<PooledCipher>(this, config);
this.transformation = transformation;
this.opmode = opmode;
this.key = key;
this.params = params;
this.secretKeySpecAlgorithm = secretKeySpecAlgorithm
}
#Override
public PooledCipher create() throws Exception {
return new PooledCipherImpl(pool, transformation, opmode, key, params, secretKeySpecAlgorithm);
}
#Override
public PooledCipher getCipher() throws Exception {
return pool.borrowObject();
}
#Override
public void destroyObject(PooledObject<PooledCipher> p) throws Exception {
try {
PooledCipherImpl cipherImpl = (PooledCipherImpl)p.getObject();
// do whatever you need with cipherImpl to destroy it
} finally {
super.destroyObject(p);
}
}
#Override
public void close() {
pool.close();
}
#Override
public PooledObject<PooledCipher> wrap(PooledCipher cipher) {
return new DefaultPooledObject<PooledCipher>(cipher);
}
}
PooledCipher implementation...
public class PooledCipherImpl implements PooledCipher {
private final ObjectPool<PooledCipher> pool;
private final Cipher cipher;
private final String secretKeySpecAlgorithm;
private boolean destroyOnClose = false;
public PooledCipherImpl(ObjectPool<PooledCipher> pool, String transformation, int opmode, Key key, AlgorithmParameters params, String secretKeySpecAlgorithm) {
this.pool = pool;
this.cipher = Cipher.getInstance(transformation);
this.cipher.init(opmode, key, params);
this.secretKeySpecAlgorithm = secretKeySpecAlgorithm;
}
#Override
public byte[] doFinal(byte[] bytes) throws Exception {
try {
return cipher.doFinal(bytes);
} catch (Exception e) {
destroyOnClose = true;
throw e;
}
}
#Override
public SecretKeySpec getSecretKeySpec(byte[] bytes) {
return new SecretKeySpec(doFinal(bytes), secretKeySpecAlgorithm);
}
#Override
public void close() throws IOException {
try {
if (destroyOnClose) {
pool.destroyObject(this);
} else {
pool.returnObject(this);
}
} catch (Exception e) {
throw new IOException(e);
}
}
}
Then you construct your CipherFactory like this...
String transformation = "DESede/CBC/NoPadding";
String secretKeySpecAlgorithm = "DESede";
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// set up the poolConfig here
poolConfig.setMaxTotal(20);
CipherFactory cipherFactory = new CipherFactoryImpl(poolConfig, transformation, Cipher.DECRYPT_MODE, mkkey, algParamSpec, secretKeySpecAlgorithm);
And use it like this...
public Key unwrapKey(byte[] tmkByte) throws Exception {
try (PooledCipher cipher = cipherFactory.getCipher()) {
return cipher.getSecretKeySpec(tmkByte);
}
}
Also you can reuse the PooledCipher and CipherFactory interfaces to create other implementations, such as JCA.
Related
I want to send multiple outbound message with my own custom JCA outbound adapter on Liberty application server .
this is my resource adapter :
#Connector(description = "Example Resource Adapter", displayName = "Example Resource Adapter", eisType = "Example Resource Adapter", version = "1.0")
public class ExampleResourceAdapter implements ResourceAdapter {
private EndpointTarget endpointTarget;
private MessageEndpointFactory messageEndpointFactory;
public void start(BootstrapContext bootstrapContext) {
}
public void stop() {
}
public void endpointActivation(final MessageEndpointFactory messageEndpointFactory, final ActivationSpec activationSpec) {
this.messageEndpointFactory = messageEndpointFactory;
}
public void endpointDeactivation(MessageEndpointFactory messageEndpointFactory, ActivationSpec activationSpec) {
if (endpointTarget != null) {
endpointTarget.getMessageEndpoint().release();
}
}
public XAResource[] getXAResources(ActivationSpec[] activationSpecs) {
return new XAResource[0];
}
public void executeRequest(String iid) {
endpointTarget = new EndpointTarget(messageEndpointFactory, iid);
endpointTarget.start();
}
and this is jca managed connection :
public class ExampleManagedConnection implements ManagedConnection {
private static Logger log = Logger.getLogger(ExampleManagedConnection.class.getName());
private PrintWriter logwriter;
private ExampleManagedConnectionFactory mcf;
private List<ConnectionEventListener> listeners;
private ExampleConnectionImpl connection;
public ExampleManagedConnection(ExampleManagedConnectionFactory mcf) {
this.mcf = mcf;
this.logwriter = null;
this.listeners = Collections.synchronizedList(new ArrayList<>(1));
this.connection = null;
}
public Object getConnection(Subject subject,
ConnectionRequestInfo cxRequestInfo) throws ResourceException {
log.finest("getConnection()");
connection = new ExampleConnectionImpl(this, mcf);
return connection;
}
public void associateConnection(Object connection) throws ResourceException {
log.finest("associateConnection()");
if (connection == null)
throw new ResourceException("Null connection handle");
if (!(connection instanceof ExampleConnectionImpl))
throw new ResourceException("Wrong connection handle");
this.connection = (ExampleConnectionImpl) connection;
}
public void cleanup() throws ResourceException {
log.finest("cleanup()");
}
public void destroy() throws ResourceException {
log.finest("destroy()");
}
public void addConnectionEventListener(ConnectionEventListener listener) {
log.finest("addConnectionEventListener()");
if (listener == null) {
throw new IllegalArgumentException("Listener is null");
}
listeners.add(listener);
}
public void removeConnectionEventListener(ConnectionEventListener listener) {
log.finest("removeConnectionEventListener()");
if (listener == null)
throw new IllegalArgumentException("Listener is null");
listeners.remove(listener);
}
public PrintWriter getLogWriter() throws ResourceException {
log.finest("getLogWriter()");
return logwriter;
}
public void setLogWriter(PrintWriter out) throws ResourceException {
log.finest("setLogWriter()");
logwriter = out;
}
public LocalTransaction getLocalTransaction() throws ResourceException {
throw new NotSupportedException("getLocalTransaction() not supported");
}
public XAResource getXAResource() throws ResourceException {
throw new NotSupportedException("getXAResource() not supported");
}
public ManagedConnectionMetaData getMetaData() throws ResourceException {
log.finest("getMetaData()");
return new ExampleManagedConnectionMetaData();
}
public void getPreTimeMarketOrders(String iid) {
ExampleResourceAdapter ExampleResourceAdapter = (ExampleResourceAdapter) mcf.getResourceAdapter();
ExampleResourceAdapter.executeRequest(iid);
}
}
i receive this exception when send more than 500 requests :
javax.resource.spi.RetryableUnavailableException: limit for number of MessageEndpoint proxies reached. Limit = 500
at com.ibm.ws.ejbcontainer.mdb.BaseMessageEndpointFactory.createEndpoint(BaseMessageEndpointFactory.java:349)
at com.ibm.ws.ejbcontainer.mdb.internal.MessageEndpointFactoryImpl.createEndpoint(MessageEndpointFactoryImpl.java:385)
How can change JCA adapter Thread Pool in Liberty/OpenLiberty Application Server ?
Looking at the OpenLiberty source, 500 is a default value that is used absent other configuration.
It looks like you can configure a different max pool size for a bean type. Refer to the section covering com.ibm.websphere.ejbcontainer.poolSize in this document.
That said, your approach seems a bit unconventional in that MessageEndpointFactory is intended for inbound, not outbound communication. Inbound communication involves a Message Driven Bean to receive the inbound messages (which could have a com.ibm.websphere.ejbcontainer.poolSize configured for it).
Your approach of overwriting endpointTarget within executeRequest is also suspicious. #Connector/ResourceAdapter class ExampleResourceAdapter is a singleton, so if you have overlapping executeRequest it will overwrite endpointTarget and only one of them will be released in endpointDeactivation.
I am using Redis in one of our project.While I realized that redis needs objects to be serialized to be persisted, I want to understand how to deal with some classes which refers to external library classes(StandardServletEnvironment) in my case ,which doesn't implement serializable and we can't modify it as well ? I am getting notSerializableException in these cases.
If you want to store user defined Java objects in redis, serialization is the suitable option. However when you go with Java native serialization it comes with some drawbacks like you faced and also it is too slow. I also faced the same kind of problem, after a long search I came up with solution to use kryo serialization.Kryo doesn't needs serialization implementation and it is faster enough than java native serialization.
P.S:If you don't want to use kryo and use the java inbuilt serialization, then create serializable class and pass your object to this class and do your stuff.
I hope this will help you.
As Praga stated, kyro is a good solution for (de)serialization of objects that do not implement Serializable interface. Here is a sample code for serialization with kyro, hope it helps:
Kryo kryo = new Kryo();
private byte[] encode(Object obj) {
ByteArrayOutputStream objStream = new ByteArrayOutputStream();
Output objOutput = new Output(objStream);
kryo.writeClassAndObject(objOutput, obj);
objOutput.close();
return objStream.toByteArray();
}
private <T> T decode(byte[] bytes) {
return (T) kryo.readClassAndObject(new Input(bytes));
}
Maven dependency:
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.1</version>
</dependency>
Full Implementation for redis integration:
RedisInterface :
public class RedisInterface {
static final Logger logger = LoggerFactory.getLogger(RedisInterface.class);
private static RedisInterface instance =null;
public static RedisInterface getInstance ()
{
if(instance ==null)
createInstance();
return instance ;
}
private static synchronized void createInstance()
{
if(instance ==null)//in case of multi thread instances
instance =new RedisInterface();
}
JedisConfig jedis = new JedisConfig();
public boolean setAttribute(String key, Object value)
{
return this.setAttribute(key, key, value);
}
public boolean setAttribute(String key, Object value, int expireSeconds)
{
return this.setAttribute(key, key, value, expireSeconds);
}
public boolean setAttribute(String key, String field, Object value)
{
int expireSeconds = 20 *60; //20 minutes
return this.setAttribute(key, field, value, expireSeconds);
}
public boolean setAttribute(String key, String field, Object value, int expireSeconds)
{
try
{
if(key==null || "".equals(key) || field==null || "".equals(field))
return false;
byte[]keyBytes = key.getBytes();
byte[]fieldBytes = field.getBytes();
byte []valueBytes = encode(value);
long start = new Date().getTime();
jedis.set(keyBytes, fieldBytes, valueBytes, expireSeconds);
long end = new Date().getTime();
long waitTime =end-start;
logger.info("{} key saved to redis in {} milliseconds with timeout: {} seconds", new Object[] {key, waitTime, expireSeconds} );
return true;
}
catch(Exception e)
{
logger.error( "error on saving object to redis. key: " + key, e);
return false;
}
}
public <T> T getAttribute(String key)
{
return this.getAttribute(key, key);
}
public <T> T getAttribute(String key, String field)
{
try
{
if(key==null || "".equals(key) || field==null || "".equals(field)) return null;
byte[]keyBytes = key.getBytes();
byte[]fieldBytes = field.getBytes();
long start = new Date().getTime();
byte[] valueBytes = jedis.get(keyBytes, fieldBytes);
T o =null;
if(valueBytes!=null && valueBytes.length>0)
o = decode(valueBytes);
long end = new Date().getTime();
long waitTime =end-start;
logger.info("{} key read operation from redis in {} milliseconds. key found?: {}", new Object[] {key, waitTime, (o!=null)});
return o;
}
catch (Exception e)
{
logger.error( "error on getting object from redis. key: "+ key, e);
return null;
}
}
Kryo kryo = new Kryo();
private byte[] encode(Object obj) {
ByteArrayOutputStream objStream = new ByteArrayOutputStream();
Output objOutput = new Output(objStream);
kryo.writeClassAndObject(objOutput, obj);
objOutput.close();
return objStream.toByteArray();
}
private <T> T decode(byte[] bytes) {
return (T) kryo.readClassAndObject(new Input(bytes));
}
}
JedisConfig :
public class JedisConfig implements Closeable
{
private Pool<Jedis> jedisPool = null;
private synchronized void initializePool()
{
if(jedisPool!=null) return;
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(Integer.parseInt(Config.REDIS_MAX_ACTIVE_CONN)); // maximum active connections
poolConfig.setMaxIdle(Integer.parseInt(Config.REDIS_MAX_IDLE_CONN)); // maximum idle connections
poolConfig.setMaxWaitMillis(Long.parseLong(Config.REDIS_MAX_WAIT_MILLIS)); // max wait time for new connection (before throwing an exception)
if("true".equals(Config.REDIS_SENTINEL_ACTIVE))
{
String [] sentinelsArray = Config.REDIS_SENTINEL_HOST_LIST.split(",");
Set<String> sentinels = new HashSet();
for(String sentinel : sentinelsArray)
{
sentinels.add(sentinel);
}
String masterName = Config.REDIS_SENTINEL_MASTER_NAME;
jedisPool = new JedisSentinelPool(masterName, sentinels, poolConfig, Integer.parseInt(Config.REDIS_CONN_TIMEOUT));
}
else
{
jedisPool = new JedisPool(poolConfig,
Config.REDIS_IP,
Integer.parseInt(Config.REDIS_PORT),
Integer.parseInt(Config.REDIS_CONN_TIMEOUT));
}
}
protected Jedis getJedis()
{
if(jedisPool==null)
initializePool();
Jedis jedis = jedisPool.getResource();
return jedis;
}
public Long set(final byte[] key, final byte[] field, final byte[] value, int expireSeconds)
{
Jedis redis = null;
Long ret =0L;
try
{
redis = getJedis();
ret = redis.hset(key, field, value);
redis.expire(key, expireSeconds);
}
finally
{
if(redis!=null)
redis.close();
}
return ret;
}
public byte[] get(final byte[] key, final byte[] field) {
Jedis redis = null ;
byte[] valueBytes = null;
try
{
redis = getJedis();
valueBytes = redis.hget(key, field);
}
finally
{
if(redis!=null)
redis.close();
}
return valueBytes;
}
#Override
public void close() throws IOException {
if(jedisPool!=null)
jedisPool.close();
}
}
I define a schedule task in the main thread, I want to clean the value of the map which caches my project's client information in ChannelHandler. But it doesn't work. What did I do wrong?
This is main app code where I schedule a task.
public class Server {
public static void main(String[] args) throws Exception {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
final ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(1);
ServerBootstrap b = new ServerBootstrap();
//init() code Omitted
ScheduledFuture<?> sf = ctx.executor().scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
HashSet<String> clients = new HashSet<>();
Map<String,String> map = LoginAuthRespHandler.getNodeCheck();
System.out.println(map.size());
for (String key:map.keySet()) {
clients.add(map.get(key));
}
try{
//doSomething();
}catch (Exception e){
e.printStackTrace();
}
map.clear();
clients.clear();
}
},10,10,TimeUnit.SECONDS);
ChannelFuture cf = b.bind(NettyConstant.REMOTEIP,NettyConstant.PORT).sync();
System.out.println("Netty server start ok on: "
+ (NettyConstant.REMOTEIP + " : " + NettyConstant.PORT));
cf.channel().closeFuture().sync();
work.shutdownGracefully();
boss.shutdownGracefully();
}
}
And this is the ChannelHandler code.
public class LoginAuthRespHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginAuthRespHandler.class);
private static Map<String, String> nodeCheck = new ConcurrentHashMap<String, String>();
private String[] whiteList = { "127.0.0.1", "192.168.56.1" };
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
AlarmMessage message = (AlarmMessage) msg;
if (message.getHeader() != null && message.getHeader().getType() == MessageType.LOGIN_REQ.value()) {
String nodeIndex = ctx.channel().remoteAddress().toString();
AlarmMessage loginResp = null;
if (nodeCheck.containsKey(nodeIndex)) {
loginResp = buildResponse(ResultType.FAIL);
} else {
InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
String ip = address.getAddress().getHostAddress();
boolean isOK = false;
for (String WIP : whiteList) {
if (WIP.equals(ip)) {
isOK = true;
break;
}
}
loginResp = isOK ? buildResponse(ResultType.SUCCESS) : buildResponse(ResultType.FAIL);
if (isOK)
//add a client value to the map
nodeCheck.put(nodeIndex, message.getBody().toString());
}
ctx.writeAndFlush(loginResp);
} else {
ctx.fireChannelRead(msg);
}
}
private AlarmMessage buildResponse(ResultType result) {
AlarmMessage message = new AlarmMessage();
Header header = new Header();
header.setType(MessageType.LOGIN_RESP.value());
message.setHeader(header);
message.setBody(result.value());
return message;
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
String nodeIndex = ctx.channel().remoteAddress().toString();
ctx.close();
if(nodeCheck.containsKey(nodeIndex)){
nodeCheck.remove(nodeIndex);
}
}
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
//nodeCheck.remove(ctx.channel().remoteAddress().toString());
ctx.close();
ctx.fireExceptionCaught(cause);
}
public synchronized static Map<String, String> getNodeCheck() {
return nodeCheck;
}
}
For supporting HTTPS connections through a Java 1.6 API to remote hosts using TLS 1.2, we have developed a customized TLS SocketConnection factory based on Bouncy Castle Libraries (v. 1.53)
It's very easy to use, just:
String httpsURL = xxxxxxxxxx
URL myurl = new URL(httpsURL);
HttpsURLConnection con = (HttpsURLConnection )myurl.openConnection();
con.setSSLSocketFactory(new TSLSocketConnectionFactory());
InputStream ins = con.getInputStream();
During testing, I connect different web and remote hosts exposed into SSLabs Tests
90% of the time this works fine! But there are some cases in which we get an annoying error: "Internal TLS error, this could be an attack" . It has been checked that there is no attack. That's a common error based on the treatment of internal BouncyCastle exceptions. I'm trying to find a common pattern to those remote host that fails with little luck.
Updated:
Updating some code for extra information, we get this:
org.bouncycastle.crypto.tls.TlsFatalAlert: illegal_parameter(47)
at org.bouncycastle.crypto.tls.AbstractTlsClient.checkForUnexpectedServerExtension(AbstractTlsClient.java:56)
at org.bouncycastle.crypto.tls.AbstractTlsClient.processServerExtensions(AbstractTlsClient.java:207)
at org.bouncycastle.crypto.tls.TlsClientProtocol.receiveServerHelloMessage(TlsClientProtocol.java:773)
The extension type i get is this:
ExtensionType:11
ExtensionData:
Acording to ExtensionType class, "ec_point_formats". This causes "UnexpectedServerExtension" --> The "UnexpectedServerExtension" causes a --> TlsFatalAlert: illegal_parameter , and at last this a "Internal TLS error, this could be an attack"
Any advise to log or trace this strange TLS Errors....???? As i say, this code works 90%...but with some remote host i get this errof
The trick consists in overriding startHandShake to use Bouncy's TLSClientProtocol:
Override ClientExtensions to include "host" ExtensionType. Just ExtensionType.server_name ( maybe any more Extension to include?)
Create a TlsAuthentication to include remoteCerts on Socket's
peerCertificate .Also optionally check if remote certs are in
default keystore (cacerts,etc..)
I share the code of TLSSocketConnectionFactory:
public class TLSSocketConnectionFactory extends SSLSocketFactory {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SECURE RANDOM
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
private SecureRandom _secureRandom = new SecureRandom();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Adding Custom BouncyCastleProvider
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Override
public Socket createSocket(Socket socket, final String host, int port, boolean arg3)
throws IOException {
if (socket == null) {
socket = new Socket();
}
if (!socket.isConnected()) {
socket.connect(new InetSocketAddress(host, port));
}
final TlsClientProtocol tlsClientProtocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(), _secureRandom);
return _createSSLSocket(host, tlsClientProtocol);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SOCKET FACTORY METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#Override
public String[] getDefaultCipherSuites() {
return null;
}
#Override
public String[] getSupportedCipherSuites(){
return null;
}
#Override
public Socket createSocket( String host,
int port) throws IOException,UnknownHostException{
return null;
}
#Override
public Socket createSocket( InetAddress host,
int port) throws IOException {
return null;
}
#Override
public Socket createSocket( String host,
int port,
InetAddress localHost,
int localPort) throws IOException, UnknownHostException {
return null;
}
#Override
public Socket createSocket( InetAddress address,
int port,
InetAddress localAddress,
int localPort) throws IOException{
return null;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//SOCKET CREATION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private SSLSocket _createSSLSocket(final String host , final TlsClientProtocol tlsClientProtocol) {
return new SSLSocket() {
private java.security.cert.Certificate[] peertCerts;
#Override
public InputStream getInputStream() throws IOException {
return tlsClientProtocol.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
return tlsClientProtocol.getOutputStream();
}
#Override
public synchronized void close() throws IOException {
Log.to("util").info("\\\n::::::Close Socket");
tlsClientProtocol.close();
}
#Override
public void addHandshakeCompletedListener(HandshakeCompletedListener arg0) {
}
#Override
public boolean getEnableSessionCreation() {
return false;
}
#Override
public String[] getEnabledCipherSuites() {
return null;
}
#Override
public String[] getEnabledProtocols() {
return null;
}
#Override
public boolean getNeedClientAuth(){
return false;
}
#Override
public SSLSession getSession() {
return new SSLSession() {
#Override
public int getApplicationBufferSize() {
return 0;
}
#Override
public String getCipherSuite() {
throw new UnsupportedOperationException();
}
#Override
public long getCreationTime() {
throw new UnsupportedOperationException();
}
#Override
public byte[] getId() {
throw new UnsupportedOperationException();
}
#Override
public long getLastAccessedTime() {
throw new UnsupportedOperationException();
}
#Override
public java.security.cert.Certificate[] getLocalCertificates() {
throw new UnsupportedOperationException();
}
#Override
public Principal getLocalPrincipal() {
throw new UnsupportedOperationException();
}
#Override
public int getPacketBufferSize() {
throw new UnsupportedOperationException();
}
#Override
public X509Certificate[] getPeerCertificateChain()
throws SSLPeerUnverifiedException {
return null;
}
#Override
public java.security.cert.Certificate[] getPeerCertificates()throws SSLPeerUnverifiedException {
return peertCerts;
}
#Override
public String getPeerHost() {
throw new UnsupportedOperationException();
}
#Override
public int getPeerPort() {
return 0;
}
#Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
return null;
//throw new UnsupportedOperationException();
}
#Override
public String getProtocol() {
throw new UnsupportedOperationException();
}
#Override
public SSLSessionContext getSessionContext() {
throw new UnsupportedOperationException();
}
#Override
public Object getValue(String arg0) {
throw new UnsupportedOperationException();
}
#Override
public String[] getValueNames() {
throw new UnsupportedOperationException();
}
#Override
public void invalidate() {
throw new UnsupportedOperationException();
}
#Override
public boolean isValid() {
throw new UnsupportedOperationException();
}
#Override
public void putValue(String arg0, Object arg1) {
throw new UnsupportedOperationException();
}
#Override
public void removeValue(String arg0) {
throw new UnsupportedOperationException();
}
};
}
#Override
public String[] getSupportedProtocols() {
return null;
}
#Override
public boolean getUseClientMode() {
return false;
}
#Override
public boolean getWantClientAuth() {
return false;
}
#Override
public void removeHandshakeCompletedListener(HandshakeCompletedListener arg0) {
}
#Override
public void setEnableSessionCreation(boolean arg0) {
}
#Override
public void setEnabledCipherSuites(String[] arg0) {
}
#Override
public void setEnabledProtocols(String[] arg0) {
}
#Override
public void setNeedClientAuth(boolean arg0) {
}
#Override
public void setUseClientMode(boolean arg0) {
}
#Override
public void setWantClientAuth(boolean arg0) {
}
#Override
public String[] getSupportedCipherSuites() {
return null;
}
#Override
public void startHandshake() throws IOException {
Log.to("util").info("TSLSocketConnectionFactory:startHandshake()");
tlsClientProtocol.connect(new DefaultTlsClient() {
#SuppressWarnings("unchecked")
#Override
public Hashtable<Integer, byte[]> getClientExtensions() throws IOException {
Hashtable<Integer, byte[]> clientExtensions = super.getClientExtensions();
if (clientExtensions == null) {
clientExtensions = new Hashtable<Integer, byte[]>();
}
//Add host_name
byte[] host_name = host.getBytes();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final DataOutputStream dos = new DataOutputStream(baos);
dos.writeShort(host_name.length + 3);
dos.writeByte(0); //
dos.writeShort(host_name.length);
dos.write(host_name);
dos.close();
clientExtensions.put(ExtensionType.server_name, baos.toByteArray());
return clientExtensions;
}
#Override
public TlsAuthentication getAuthentication()
throws IOException {
return new TlsAuthentication() {
#Override
public void notifyServerCertificate(Certificate serverCertificate) throws IOException {
try {
KeyStore ks = _loadKeyStore();
Log.to("util").info(">>>>>>>> KeyStore : "+ks.size());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List<java.security.cert.Certificate> certs = new LinkedList<java.security.cert.Certificate>();
boolean trustedCertificate = false;
for ( org.bouncycastle.asn1.x509.Certificate c : serverCertificate.getCertificateList()) {
java.security.cert.Certificate cert = cf.generateCertificate(new ByteArrayInputStream(c.getEncoded()));
certs.add(cert);
String alias = ks.getCertificateAlias(cert);
if(alias != null) {
Log.to("util").info(">>> Trusted cert\n" + c.getSubject().toString());
if (cert instanceof java.security.cert.X509Certificate) {
try {
( (java.security.cert.X509Certificate) cert).checkValidity();
trustedCertificate = true;
Log.to("util").info("Certificate is active for current date\n"+cert);
} catch(CertificateExpiredException cee) {
R01FLog.to("r01f.util").info("Certificate is expired...");
}
}
} else {
Log.to("util").info(">>> Unknown cert " + c.getSubject().toString());
Log.to("util").fine(""+cert);
}
}
if (!trustedCertificate) {
throw new CertificateException("Unknown cert " + serverCertificate);
}
peertCerts = certs.toArray(new java.security.cert.Certificate[0]);
} catch (Exception ex) {
ex.printStackTrace();
throw new IOException(ex);
}
}
#Override
public TlsCredentials getClientCredentials(CertificateRequest arg0)
throws IOException {
return null;
}
/**
* Private method to load keyStore with system or default properties.
* #return
* #throws Exception
*/
private KeyStore _loadKeyStore() throws Exception {
FileInputStream trustStoreFis = null;
try {
String sysTrustStore = null;
File trustStoreFile = null;
KeyStore localKeyStore = null;
sysTrustStore = System.getProperty("javax.net.ssl.trustStore");
String javaHome;
if (!"NONE".equals(sysTrustStore)) {
if (sysTrustStore != null) {
trustStoreFile = new File(sysTrustStore);
trustStoreFis = _getFileInputStream(trustStoreFile);
} else {
javaHome = System.getProperty("java.home");
trustStoreFile = new File(javaHome + File.separator + "lib" + File.separator + "security" + File.separator + "jssecacerts");
if ((trustStoreFis = _getFileInputStream(trustStoreFile)) == null) {
trustStoreFile = new File(javaHome + File.separator + "lib" + File.separator + "security" + File.separator + "cacerts");
trustStoreFis = _getFileInputStream(trustStoreFile);
}
}
if (trustStoreFis != null) {
sysTrustStore = trustStoreFile.getPath();
} else {
sysTrustStore = "No File Available, using empty keystore.";
}
}
String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType")!=null?System.getProperty("javax.net.ssl.trustStoreType"):KeyStore.getDefaultType();
String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider")!=null?System.getProperty("javax.net.ssl.trustStoreProvider"):"";
if (trustStoreType.length() != 0) {
if (trustStoreProvider.length() == 0) {
localKeyStore = KeyStore.getInstance(trustStoreType);
} else {
localKeyStore = KeyStore.getInstance(trustStoreType, trustStoreProvider);
}
char[] keyStorePass = null;
String str5 = System.getProperty("javax.net.ssl.trustStorePassword")!=null?System.getProperty("javax.net.ssl.trustStorePassword"):"";
if (str5.length() != 0) {
keyStorePass = str5.toCharArray();
}
localKeyStore.load(trustStoreFis, (char[]) keyStorePass);
if (keyStorePass != null) {
for (int i = 0; i < keyStorePass.length; i++) {
keyStorePass[i] = 0;
}
}
}
return (KeyStore)localKeyStore;
} finally {
if (trustStoreFis != null) {
trustStoreFis.close();
}
}
}
private FileInputStream _getFileInputStream(File paramFile) throws Exception {
if (paramFile.exists()) {
return new FileInputStream(paramFile);
}
return null;
}
};
}
});
}
};//Socket
}
}
If you look at RFC 4492 5.2, you'll see that the server CAN send the "ec_point_formats" extension, but is only supposed to do so "when negotiating an ECC cipher suite". If you want TLSClient to just ignore the extra extension instead of raising an exception, I suggest overriding TlsClient.allowUnexpectedServerExtension(...) to allow ec_point_formats in the same way the default implementation allows elliptic_curves:
protected boolean allowUnexpectedServerExtension(Integer extensionType, byte[] extensionData)
throws IOException
{
switch (extensionType.intValue())
{
case ExtensionType.ec_point_formats:
/*
* Exception added based on field reports that some servers send Supported
* Point Format Extension even when not negotiating an ECC cipher suite.
* If present, we still require that it is a valid ECPointFormatList.
*/
TlsECCUtils.readSupportedPointFormatsExtension(extensionData);
return true;
default:
return super.allowUnexpectedServerExtension(extensionType, extensionData);
}
}
If this is a widespread problem, we might consider adding this case to the default implementation.
For logging, there are the (TLSPeer) methods notifyAlertRaised and notifyAlertReceived that you can override on your TLSClient implementation.
i am posting this so maybe some people might find this helpful - i was also getting invalid_parameter(47) exception from my BC client code.
this error started to happen when we upgraded BC from version 1.62 (bcprov- and bctls-jdk15on-162 JARs) to 1.71 (bcprov-, bctls-, bcpkix-, bcutil-jdk15to18-171).
my stacktrace was different from the above problem, though:
org.bouncycastle.tls.TlsFatalAlert: illegal_parameter(47)
at org.bouncycastle.tls.TlsClientProtocol.processServerHello(Unknown Source)
at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
...
I had some trouble figuring out how to debug the internal BC TLS code. Тhe provided JARs are compiled without the debug info - I had to download the source files from Maven Central and build from them, creating a new project in Netbeans with existing sources - the only Java version it can be built with is 8, but the end result could be referenced by my Java 6 client code.
Having compiled bctls with debug info, and referencing it from my client, I could actually land in the code that was throwing the exception:
protected void processServerHello(ServerHello serverHello)
throws IOException
{
...
if (sessionServerExtensions != null && !sessionServerExtensions.isEmpty())
{
{
/*
* RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
* and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
* ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
* client.
*/
boolean serverSentEncryptThenMAC = TlsExtensionsUtils.hasEncryptThenMACExtension(sessionServerExtensions);
if (serverSentEncryptThenMAC && !TlsUtils.isBlockCipherSuite(securityParameters.getCipherSuite()))
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}
securityParameters.encryptThenMAC = serverSentEncryptThenMAC;
}
...
}
I don't really know what encrypt-then-MAC and MAC-then-encrypt is, and I didn't want to get deep into their meaning. After some searching, I figured out that possibly I had to limit the list of the cipher suites used for the communication - because possibly some cipher suite that is chosen by default forces the server into some condition where it behaves differently from the RFC specification - and yes, this at least worked:
TrustManagerFactory tmf = ...
SSLContext sc = SSLContext.getInstance("TLSv1.2", BouncyCastleJsseProvider.PROVIDER_NAME);
sc.init(null, tmf.getTrustManagers(), new SecureRandom());
SSLConnectionSocketFactory ret = new SSLConnectionSocketFactory(sc,
new String[] { "TLSv1.2" },
new String[] { "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256" },
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// the created SSLConnectionSocketFactory is then used to create socketFactoryRegistry
// which is then passed to constructor of Apache HttpComponents' PoolingHttpClientConnectionManager
// which is used to create a new instance of Apache HttpClient
Other useful stuff that I learned is
there are sites, e.g. https://www.ssllabs.com/ssltest that analyze the available protocols and cipher suites that a server supports.
the list of the cipher suites above is not exactly arbitrary - I took it from this stackoverflow q&a.
I try to implement a SFTP upload with a progress bar.
Unfortunately the progress bar is not updated...
Here is a part of my code:
public class MainView extends JFrame implements SftpProgressMonitor {
private static final Logger LOG = Logger.getLogger(MainView.class);
private String _source;
private JProgressBar _progressBar;
private JButton _button;
public MainView() {
initComponents();
}
void initComponents() {
_button = new JButton("Send");
_button.addActionListener(new ActionListener() {
// If clicked, send event to controller...
});
_progressBar = new JProgressBar();
// Do init stuff here
// ...
}
#Override
public boolean count(long byteTransfered) {
int transfered = _progressBar.getValue();
transfered += byteTransfered;
_progressBar.setValue(transfered);
return true;
}
#Override
public void end() {
LOG.info("Transfer of "+_source+" finished!");
}
#Override
public void init(int op, String src, String dest, long max) {
_progressBar.setValue(0);
_progressBar.setMinimum(0);
_progressBar.setMaximum((int) max);
_source = src;
}
}
public class Controller {
private final MainView _view;
private final SftpClient _ftp;
private final Server _server;
public Controller() {
_server = new Server("192.168.0.1");
_view = new MainView();
_ftp = new SftpClient(_server);
_view.setVisible(true);
}
public void send() {
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
public void run() {
File testFile = new File("/PathToFile/file.txt");
String remoteDir = "/MyRemoteDir/";
_ftp.put(testFile, remoteDir, testFile.getName(), _view);
}
});
}
public static void main(String[] args) {
Controller controller = new Controller();
}
}
public class SftpClient {
private static final Logger LOG = Logger.getLogger(SftpClient.class);
/** Connection port number */
public static final int PORT = 22;
/** SECURED protocol name */
public static final String PROTOCOL = "sftp";
/** Connection time out in milliseconds */
public static final int TIME_OUT = 3000;
private Server _server;
/** This class serves as a central configuration point, and as a factory for Session objects configured with these settings */
private JSch _client;
/** A session represents a connection to a SSH server */
private Session _session;
/** Channel connected to a SECURED server (as a subsystem of the SSH server) */
private ChannelSftp _channelSftp;
/**
* Value returned by the last executed command.
*/
private int _exitValue;
public SftpClient(Server server) {
_client = new JSch();
_server = server;
}
protected void connect() throws AuthenticationException, Exception {
try {
if (_client == null) {
_client = new JSch();
}
if (_session == null) {
_session = _client.getSession(_server.getLogin(), _server.getAddress(), PORT);
_session.setConfig("StrictHostKeyChecking", "no");
_session.setPassword(_server.getPassword());
if (LOG.isDebugEnabled()) {
LOG.debug("Connecting to "+_server.getAddress()+" with login "+_server.getLogin()+"...");
}
}
if (!_session.isConnected()) {
_session.connect(TIME_OUT);
}
if(_channelSftp == null || _channelSftp.isConnected() == false) {
Channel c = _session.openChannel(PROTOCOL);
c.connect();
// disconnect previous channel if it has not been killed properly
if (_channelSftp != null && _channelSftp.isConnected()) {
_channelSftp.disconnect();
}
_channelSftp = (ChannelSftp) c;
}
if (LOG.isInfoEnabled()) {
LOG.info("Connected to "+_server.getAddress()+" with login "+_server.getLogin());
}
} catch(JSchException e) {
if ("Auth fail".equals(e.getMessage())) {
throw new AuthenticationException(e);
} else {
throw new Exception(e);
}
}
}
protected void connect(String path) throws AuthenticationException, Exception {
connect();
if (_channelSftp != null && _channelSftp.isConnected()) {
_channelSftp.cd(path);
}
}
#Override
public void disconnect() {
if (_channelSftp != null && _channelSftp.isConnected()) {
_channelSftp.disconnect();
_channelSftp.exit();
}
if (_session != null && _session.isConnected()) {
_session.disconnect();
if (LOG.isInfoEnabled()) {
LOG.info("SECURED FTP disconnected");
}
}
}
#Override
public void put(File localFile, String destPath, SftpProgressMonitor monitor) throws Exception {
put(localFile, destPath, localFile.getName(), monitor);
}
#Override
public void put(File localFile, String destPath, String remoteFileName, SftpProgressMonitor monitor) throws Exception {
if (LOG.isInfoEnabled()) {
LOG.info("Send file "+localFile+" to "+_server+" in "+destPath);
}
if (localFile == null) {
_exitValue = -1;
LOG.error("The given local file is null. Aborting tranfer.");
return;
}
if (!localFile.exists()) {
_exitValue = -1;
LOG.error("'"+localFile+"' doesn't exist. Aborting tranfer.");
return;
}
if(!localFile.canRead()) {
_exitValue = -1;
LOG.error("Cannot read '"+localFile+"'. Aborting tranfer.");
return;
}
final InputStream input = new BufferedInputStream(new FileInputStream(localFile));
if (input == null || input.available() <= 0) {
_exitValue = -1;
LOG.error("Cannot read file "+localFile);
return;
}
try {
connect(destPath);
_channelSftp.put(input, remoteFileName, monitor);
_exitValue = _channelSftp.getExitStatus();
} catch(SftpException e){
throw new IOException(e);
} finally {
if (_channelSftp != null && _channelSftp.isConnected()) {
_channelSftp.disconnect();
_channelSftp.exit();
}
IOUtils.closeQuietly(input);
}
}
}
The count() method is never called. And the source and destination strings of the init contain both -. Am I doing it wrong?
I've changed my code and it works now. I don't use put(InputStream src, String dst, int mode) anymore but put(String src, String dst, SftpProgressMonitor monitor).
I've also implemented a DefaultBoundedRangeModel class. It modifies directly the JProgressBar and it is more interesting for me because I have several files to transfer.
public class ProgressModel extends DefaultBoundedRangeModel implements SftpProgressMonitor {
/** Logger */
private static Logger LOG = Logger.getLogger(ProgressModel.class);
private String _fileBeingTransfered;
/**
* Constructs the model.
*/
public ProgressModel() {
_fileBeingTransfered = "";
}
#Override
public boolean count(long count) {
int value = (int) (getValue() + count);
setValue(value);
fireStateChanged();
if(value < getMaximum()) {
return true;
} else {
return false;
}
}
#Override
public void end() {
LOG.info(_fileBeingTransfered+" transfert finished.");
if(getValue() == getMaximum()) {
LOG.info("All transfers are finished!");
}
}
#Override
public void init(int op, String src, String dest, long max) {
LOG.info("Transfering "+src+" to "+dest+" | size: "+max);
_fileBeingTransfered = src;
}
}
I don't know what caused my problem. Maybe it was the put method.
In order to get updates you have to send reference to monitor to FTP client. And you do this:
_ftp.put(testFile, remoteDir, testFile.getName(), _view);
However what _view is? It is a private final field of class Controller that is never initialized. Therefore it is null. You implemented your callback method count() into class MainView but do not send reference to it to the FTP client. I do not know where do you create instance of Controller but you should pass reference to instance of MainView to it.