Java tutorial
/* * jPOS Project [http://jpos.org] * Copyright (C) 2000-2018 jPOS Software SRL * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.jpos.qrest; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.BindException; import java.security.*; import java.util.Arrays; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.ssl.SslHandler; import io.netty.handler.timeout.IdleStateHandler; import org.jpos.core.Configuration; import org.jpos.core.ConfigurationException; import org.jpos.iso.ISOUtil; import org.jpos.q2.QBeanSupport; import org.jpos.space.Space; import org.jpos.space.SpaceFactory; import org.jpos.transaction.Context; import org.jpos.util.LogEvent; import org.jpos.util.Logger; import org.jpos.util.NameRegistrar; import javax.net.ssl.*; public class RestServer extends QBeanSupport implements Runnable { private ServerBootstrap serverBootstrap; private ChannelFuture cf; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; private Space sp; private Thread initializerThread; private String password = null; private String keyPassword = null; private String keyStore = null; private boolean serverAuthNeeded; private boolean enableTLS = false; private boolean clientAuthNeeded = false; private String[] enabledCipherSuites; private String[] enabledProtocols; @Override protected void initService() throws GeneralSecurityException, IOException { sp = SpaceFactory.getSpace(); final SSLContext sslContext = enableTLS ? getSSLContext() : null; bossGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup(); serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { int timeout = cfg.getInt("timeout", 300); ch.pipeline().addLast(new IdleStateHandler(timeout, timeout, timeout)); if (enableTLS) { ch.pipeline().addLast(new SslHandler(getSSLEngine(sslContext), true)); } ch.pipeline().addLast(new HttpServerCodec()); ch.pipeline().addLast(new HttpObjectAggregator(512 * 1024)); ch.pipeline().addLast(new RestSession(RestServer.this)); } }).option(ChannelOption.SO_BACKLOG, 128).option(ChannelOption.SO_REUSEADDR, true) .childOption(ChannelOption.SO_KEEPALIVE, true); if (enableTLS) { logSSLEngineInfo(getSSLEngine(sslContext)); } } @Override protected void startService() { initializerThread = new Thread(this, getName()); initializerThread.start(); } @Override protected void stopService() { if (initializerThread != null) { initializerThread.interrupt(); NameRegistrar.unregister(getName()); if (cf != null && cf.channel() != null) cf.channel().close(); } } @Override protected void destroyService() { if (workerGroup != null) { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } @Override public void run() { int port = cfg.getInt("port", 8080); while (running()) { try { cf = serverBootstrap.bind(port).sync(); if (cf.isSuccess()) { getLog().info(getName() + " ready - port " + port); NameRegistrar.register(getName(), this); break; } } catch (Throwable t) { if (t instanceof BindException) { getLog().warn(t.getMessage() + " port (" + port + ") --- retrying"); } else { getLog().warn(t); break; } ISOUtil.sleep(5000L); } } } public void queue(Context ctx) { sp.out(cfg.get("queue"), ctx, 60000L); } @Override public void setConfiguration(Configuration cfg) throws ConfigurationException { super.setConfiguration(cfg); enableTLS = cfg.getBoolean("TLS", false); keyStore = cfg.get("keystore"); password = cfg.get("storepassword", getPassword()); keyPassword = cfg.get("keypassword", getKeyPassword()); clientAuthNeeded = cfg.getBoolean("client-auth", false); serverAuthNeeded = cfg.getBoolean("server-auth", false); enabledCipherSuites = cfg.getAll("enabled-cipher"); enabledProtocols = cfg.getAll("enable-protocol"); } private SSLEngine getSSLEngine(SSLContext sslContext) throws IOException { SSLEngine engine = null; engine = sslContext.createSSLEngine(); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { engine.setEnabledCipherSuites(enabledCipherSuites); } if (enabledProtocols != null && enabledProtocols.length > 0) { engine.setEnabledProtocols(enabledProtocols); } engine.setNeedClientAuth(clientAuthNeeded); engine.setUseClientMode(false); engine.beginHandshake(); return engine; } private SSLContext getSSLContext() throws GeneralSecurityException, IOException { KeyStore ks = KeyStore.getInstance("JKS"); if (keyStore == null || keyStore.length() == 0) { keyStore = System.getProperty("user.home") + File.separator + ".keystore"; } try (FileInputStream fis = new FileInputStream(new File(keyStore))) { ks.load(fis, password.toCharArray()); } KeyManagerFactory km = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); km.init(ks, keyPassword.toCharArray()); KeyManager[] kma = km.getKeyManagers(); TrustManager[] tma = getTrustManagers(ks); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kma, tma, SecureRandom.getInstance("SHA1PRNG")); return sslContext; } private TrustManager[] getTrustManagers(KeyStore ks) throws GeneralSecurityException { if (serverAuthNeeded) { TrustManagerFactory tm = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tm.init(ks); return tm.getTrustManagers(); } else { // Create a trust manager that does not validate certificate chains return new TrustManager[] { new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[] {}; } public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { } public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { } } }; } } private void logSSLEngineInfo(SSLEngine engine) { LogEvent evt = getLog().createInfo(); evt.addMessage("ciphersuites: " + Arrays.toString(engine.getEnabledCipherSuites())); evt.addMessage(" protocols: " + Arrays.toString(engine.getEnabledProtocols())); Logger.log(evt); } // Have custom hooks get passwords // You really need to modify these two implementations protected String getPassword() { return System.getProperty("jpos.ssl.storepass", null); } protected String getKeyPassword() { return System.getProperty("jpos.ssl.keypass", null); } }