/*
* Copyright 2015 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package one.nio.net;
import one.nio.mgt.Management;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ServiceConfigurationError;
import java.util.StringTokenizer;
class NativeSslContext extends SslContext {
// Intermediate compatibility ciphersuite according to https://wiki.mozilla.org/Security/Server_Side_TLS
private static final String DEFAULT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA";
static final NativeSslContext DEFAULT;
long ctx;
NativeSslContext() throws SSLException {
this.ctx = ctxNew();
Management.registerMXBean(new NativeSslContextMXBeanImpl(),
"one.nio.net:type=SslContext,id=" + Long.toHexString(ctx));
}
@Override
public void close() {
if (ctx != 0) {
Management.unregisterMXBean("one.nio.net:type=SslContext,id=" + Long.toHexString(ctx));
ctxFree(ctx);
ctx = 0;
}
}
@Override
public void setProtocols(String protocols) {
int enabled = 0;
StringTokenizer st = new StringTokenizer(protocols.toLowerCase(), " ,:+", false);
while (st.hasMoreTokens()) {
String protocol = st.nextToken();
if (protocol.equals("compression")) {
enabled |= 0x00020000;
} else if (protocol.equals("sslv2")) {
enabled |= 0x01000000;
} else if (protocol.equals("sslv3")) {
enabled |= 0x02000000;
} else if (protocol.equals("tlsv1")) {
enabled |= 0x04000000;
} else if (protocol.equals("tlsv1.1")) {
enabled |= 0x10000000;
} else if (protocol.equals("tlsv1.2")) {
enabled |= 0x08000000;
}
}
int all = 0x00020000 + 0x01000000 + 0x02000000 + 0x04000000 + 0x08000000 + 0x10000000;
clearOptions(enabled);
setOptions(all - enabled);
}
@Override
public native void setCiphers(String ciphers) throws SSLException;
@Override
public native void setCertificate(String certFile, String privateKeyFile) throws SSLException;
@Override
public native void setCA(String caFile) throws SSLException;
@Override
public native void setVerify(int verifyMode) throws SSLException;
@Override
public native void setTicketKey(byte[] ticketKey) throws SSLException;
@Override
public native void setTimeout(long timeout) throws SSLException;
private native void setOptions(int options);
private native void clearOptions(int options);
private native long getSessionCounter(int key);
private native long[] getSessionCounters(int keysBitmap);
private static native void init();
private static native long ctxNew() throws SSLException;
private static native void ctxFree(long ctx);
private static byte[] readFile(String fileName) throws IOException {
RandomAccessFile raf = new RandomAccessFile(fileName, "r");
try {
byte[] data = new byte[(int) raf.length()];
raf.readFully(data);
return data;
} finally {
raf.close();
}
}
static {
init();
try {
DEFAULT = new NativeSslContext();
String certFile = System.getProperty("one.nio.ssl.certFile");
String privateKeyFile = System.getProperty("one.nio.ssl.privateKeyFile");
if (certFile != null || privateKeyFile != null) {
DEFAULT.setCertificate(certFile, privateKeyFile);
}
String protocols = System.getProperty("one.nio.ssl.protocols");
if (protocols != null) {
DEFAULT.setProtocols(protocols);
}
String ciphers = System.getProperty("one.nio.ssl.ciphers", DEFAULT_CIPHERS);
DEFAULT.setCiphers(ciphers);
String ticketKeyFile = System.getProperty("one.nio.ssl.ticketKeyFile");
if (ticketKeyFile != null) {
DEFAULT.setTicketKey(readFile(ticketKeyFile));
}
String timeout = System.getProperty("one.nio.ssl.timeout");
if (timeout != null) {
DEFAULT.setTimeout(Long.parseLong(timeout));
}
} catch (IOException e) {
throw new ServiceConfigurationError("Could not create OpenSSL context", e);
}
}
private class NativeSslContextMXBeanImpl implements SslContextMXBean {
@Override
public long getNumber() {
return getSessionCounter(0);
}
@Override
public long getConnect() {
return getSessionCounter(1);
}
@Override
public long getConnectGood() {
return getSessionCounter(2);
}
@Override
public long getConnectRenegotiate() {
return getSessionCounter(3);
}
@Override
public long getAccept() {
return getSessionCounter(4);
}
@Override
public long getAcceptGood() {
return getSessionCounter(5);
}
@Override
public long getAcceptRenegotiate() {
return getSessionCounter(6);
}
@Override
public long getHits() {
return getSessionCounter(7);
}
@Override
public long getCustomHits() {
return getSessionCounter(8);
}
@Override
public long getMisses() {
return getSessionCounter(9);
}
@Override
public long getTimeouts() {
return getSessionCounter(10);
}
@Override
public long getEvicted() {
return getSessionCounter(11);
}
}
}