1 package club.wpia.gigi.ping;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.math.BigInteger;
7 import java.net.InetSocketAddress;
8 import java.net.Socket;
9 import java.nio.ByteBuffer;
10 import java.nio.channels.SocketChannel;
11 import java.security.GeneralSecurityException;
12 import java.security.KeyManagementException;
13 import java.security.KeyStore;
14 import java.security.KeyStoreException;
15 import java.security.SecureRandom;
16 import java.util.Arrays;
18 import javax.net.ssl.SNIHostName;
19 import javax.net.ssl.SNIServerName;
20 import javax.net.ssl.SSLContext;
21 import javax.net.ssl.SSLEngine;
22 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
23 import javax.net.ssl.SSLEngineResult.Status;
24 import javax.net.ssl.SSLException;
25 import javax.net.ssl.SSLParameters;
26 import javax.net.ssl.TrustManager;
27 import javax.net.ssl.TrustManagerFactory;
28 import javax.net.ssl.X509TrustManager;
29 import javax.security.cert.CertificateException;
30 import javax.security.cert.X509Certificate;
32 import club.wpia.gigi.dbObjects.CACertificate;
33 import club.wpia.gigi.dbObjects.Certificate;
34 import club.wpia.gigi.dbObjects.CertificateOwner;
35 import club.wpia.gigi.dbObjects.Domain;
36 import club.wpia.gigi.dbObjects.DomainPingConfiguration;
37 import club.wpia.gigi.dbObjects.DomainPingExecution;
38 import sun.security.x509.AVA;
39 import sun.security.x509.X500Name;
41 public class SSLPinger extends DomainPinger {
43 private static final String OID_EKU_serverAuth = "1.3.6.1.5.5.7.3.1";
45 public static final String[] TYPES = new String[] {
46 "xmpp", "server-xmpp", "smtp", "imap"
49 private KeyStore truststore;
51 public SSLPinger(KeyStore truststore) {
52 this.truststore = truststore;
56 public DomainPingExecution ping(Domain domain, String configuration, CertificateOwner u, DomainPingConfiguration conf) {
57 try (SocketChannel sch = SocketChannel.open()) {
58 sch.socket().setSoTimeout(5000);
59 String[] parts = configuration.split(":", 4);
60 sch.socket().connect(new InetSocketAddress(domain.getSuffix(), Integer.parseInt(parts[2])), 5000);
61 if (parts.length == 4) {
64 startXMPP(sch, false, domain.getSuffix());
67 startXMPP(sch, true, domain.getSuffix());
78 String key = parts[0];
79 String value = parts[1];
80 String res = test(sch, domain.getSuffix(), u, value);
81 return enterPingResult(conf, res, res, null);
82 } catch (IOException e) {
83 return enterPingResult(conf, "error", "connection Failed", null);
88 private void startIMAP(SocketChannel sch) throws IOException {
89 Socket s = sch.socket();
90 InputStream is = s.getInputStream();
91 OutputStream os = s.getOutputStream();
93 os.write("ENABLE STARTTLS\r\n".getBytes("UTF-8"));
98 private void startXMPP(SocketChannel sch, boolean server, String domain) throws IOException {
99 Socket s = sch.socket();
100 InputStream is = s.getInputStream();
101 OutputStream os = s.getOutputStream();
102 os.write(("<stream:stream to=\"" + domain + "\" xmlns=\"jabber:" + (server ? "server" : "client") + "\"" + " xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">").getBytes("UTF-8"));
104 os.write("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>".getBytes("UTF-8"));
106 scanFor(is, "<proceed");
111 private void scanFor(InputStream is, String scanFor) throws IOException {
113 while (pos < scanFor.length()) {
114 if (is.read() == scanFor.charAt(pos)) {
122 private void startSMTP(SocketChannel sch) throws IOException {
123 Socket s = sch.socket();
124 InputStream is = s.getInputStream();
126 s.getOutputStream().write("EHLO ssl.pinger\r\n".getBytes("UTF-8"));
127 s.getOutputStream().flush();
129 s.getOutputStream().write("HELP\r\n".getBytes("UTF-8"));
130 s.getOutputStream().flush();
132 s.getOutputStream().write("STARTTLS\r\n".getBytes("UTF-8"));
133 s.getOutputStream().flush();
137 private void readSMTP(InputStream is) throws IOException {
139 boolean finish = true;
141 char c = (char) is.read();
145 } else if (c == '-') {
148 throw new Error("Invalid smtp: " + c);
162 private String test(SocketChannel sch, String domain, CertificateOwner subject, String tok) {
163 System.out.println("SSL- connecting");
166 sch.socket().setSoTimeout(5000);
167 SSLContext sc = SSLContext.getInstance("SSL");
169 TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
170 tmf.init(truststore);
171 sc.init(null, new TrustManager[] {
172 new X509TrustManager() {
175 public java.security.cert.X509Certificate[] getAcceptedIssuers() {
180 public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
181 java.security.cert.X509Certificate c = chain[0];
182 if (c.getExtendedKeyUsage() == null || !c.getExtendedKeyUsage().contains(OID_EKU_serverAuth)) {
183 throw new java.security.cert.CertificateException("Extended Key Usage for SSL Server Authentication missing");
188 public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {}
190 }, new SecureRandom());
191 } catch (KeyManagementException e) {
193 } catch (KeyStoreException e) {
196 SSLEngine se = sc.createSSLEngine();
197 ByteBuffer enc_in = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
198 ByteBuffer enc_out = ByteBuffer.allocate(se.getSession().getPacketBufferSize());
199 ByteBuffer dec_in = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
200 ByteBuffer dec_out = ByteBuffer.allocate(se.getSession().getApplicationBufferSize());
201 se.setUseClientMode(true);
202 SSLParameters sp = se.getSSLParameters();
203 sp.setServerNames(Arrays.<SNIServerName>asList(new SNIHostName(domain)));
204 se.setSSLParameters(sp);
207 while (se.getHandshakeStatus() != HandshakeStatus.FINISHED && se.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) {
208 switch (se.getHandshakeStatus()) {
211 se.wrap(dec_out, enc_out);
213 while (enc_out.remaining() > 0) {
219 if (enc_in.remaining() == 0) {
224 while (se.unwrap(enc_in, dec_in).getStatus() == Status.BUFFER_UNDERFLOW) {
225 enc_in.position(enc_in.limit());
226 enc_in.limit(enc_in.capacity());
234 se.getDelegatedTask().run();
236 case NOT_HANDSHAKING:
242 System.out.println("SSL- connected");
243 X509Certificate[] peerCertificateChain = se.getSession().getPeerCertificateChain();
244 X509Certificate first = peerCertificateChain[0];
245 if (first.getIssuerDN().equals(first.getSubjectDN())) {
246 first.verify(first.getPublicKey());
247 X500Name p = (X500Name) first.getSubjectDN();
248 X500Name n = new X500Name(p.getEncoded());
249 for (AVA i : n.allAvas()) {
250 if (i.getObjectIdentifier().equals((Object) X500Name.orgUnitName_oid)) {
251 String toke = i.getDerValue().getAsString();
252 if (tok.equals(toke)) {
253 return PING_SUCCEDED;
255 return "Self-signed certificate is wrong";
261 BigInteger serial = first.getSerialNumber();
262 Certificate c = Certificate.getBySerial(serial);
264 return "Certificate not found: Serial " + serial.toString(16) + " missing.";
266 CACertificate p = c.getParent();
267 if ( !first.getIssuerDN().equals(p.getCertificate().getSubjectDN())) {
268 return "Broken certificate supplied";
270 first.verify(p.getCertificate().getPublicKey());
271 if (c.getOwner().getId() != subject.getId()) {
272 return "Owner mismatch";
274 return PING_SUCCEDED;
275 } catch (GeneralSecurityException e) {
276 // e.printStackTrace();
277 return "Security failed";
278 } catch (SSLException e) {
279 // e.printStackTrace(); TODO log for user debugging?
280 return "Security failed";
281 } catch (IOException e) {
282 // e.printStackTrace(); TODO log for user debugging?
283 return "Connection closed";
284 } catch (CertificateException e) {
285 // e.printStackTrace();
286 return "Security failed";