|
| 1 | +SSL (TLS) support for jTDS |
| 2 | +========================== |
| 3 | + |
| 4 | + Author: Rob Worsnop |
| 5 | + Version: $Id: README.SSL,v 1.1 2005-01-13 17:26:50 alin_sinpalean Exp $ |
| 6 | + |
| 7 | +The challenge |
| 8 | +============= |
| 9 | + |
| 10 | +At this point it should be noted that the solution has been tested against SQL |
| 11 | +Server 2000 (SQL2K), and nothing else. Everything in this section applies to SQL |
| 12 | +Server 2000. Having said that, the changes made to support SSL will not affect |
| 13 | +the behavior of jTDS with other products when using plain sockets. |
| 14 | + |
| 15 | + Requesting encryption |
| 16 | + --------------------- |
| 17 | + |
| 18 | + SQL2K allows clients to discover whether or not SSL is supported before |
| 19 | + starting an SSL session. This is done via a special TDS message. SQL2K's |
| 20 | + response depends on whether or not a digital certificate is installed. The |
| 21 | + request for encryption is also a notification of encryption. In other words, |
| 22 | + once a client has requested encryption, and it is supported, SQL2K then |
| 23 | + expects an SSL handshake. |
| 24 | + |
| 25 | + Header confusion |
| 26 | + ---------------- |
| 27 | + |
| 28 | + To my mind, the most logical approach for SSL in SQL2K would be for the server |
| 29 | + to switch from expecting TDS packets to expecting SSL or TLS records. The |
| 30 | + application data SSL records would then contain TDS packets, whereas the |
| 31 | + handshake records would just be pure SSL. This would make adding SSL trivially |
| 32 | + easy � JSSE would do all of this for us. |
| 33 | + |
| 34 | + Unfortunately this is not how it works. Instead, the handshake records are |
| 35 | + enclosed in TDS packets. Then, after the handshake, things get reversed: the |
| 36 | + TDS packets are delivered inside SSL records (as encrypted application data). |
| 37 | + |
| 38 | + Handshake batching fussiness |
| 39 | + ---------------------------- |
| 40 | + |
| 41 | + After sending the Client Hello message, and receiving a response from the |
| 42 | + server, the client then sends two or three (depending on whether or not an |
| 43 | + existing SSL session if being resumed) more records. These are Client Key |
| 44 | + Exchange (new sessions only), Change Cipher Spec, and Finished. |
| 45 | + |
| 46 | + SQL2K will not tolerate these records being sent separately: they must all be |
| 47 | + delivered in the same TDS packet. |
| 48 | + |
| 49 | + JSSE transmits these records separately; hence the challenge. |
| 50 | + |
| 51 | +The requirements |
| 52 | +================ |
| 53 | + |
| 54 | +o If SSL is configured (see further down for more details on this), then jTDS |
| 55 | + must send an encryption request, and handle the response appropriately. |
| 56 | +o All SSL handshake records written by JSSE must be intercepted and enclosed in |
| 57 | + TDS packets before transmission. Application data records must be passed on |
| 58 | + unmodified. |
| 59 | +o Client Key Exchange and Change Cipher Spec messages written by JSSE must be |
| 60 | + intercepted and deferred until a Finished message is written. Then all three |
| 61 | + (or two if resumed session) messages are transmitted in a TDS packet. |
| 62 | +o All traffic returned from SQL2K must be inspected. When a TDS header is |
| 63 | + discovered, it must be stripped off before passing on the data up to JSSE. |
| 64 | + |
| 65 | +The approach |
| 66 | +============ |
| 67 | + |
| 68 | +The JSSE API allows SSL to be tunneled through an existing socket. The existing |
| 69 | +socket specified becomes a delegate. |
| 70 | + |
| 71 | +So, if SSL is switched on, we create a plain socket, send the encryption request |
| 72 | +on it, and then specify that socket as the delegate for JSSE. Well, almost. |
| 73 | +Actually, after the encryption request, the plain socket is wrapped in a socket |
| 74 | +implementation of our own, and this wrapper is actually what JSSE sees. By |
| 75 | +overriding Socket.getOutputStream and Socket.getInputStream, and providing |
| 76 | +custom implementations of those interfaces, we are able to intercept all traffic |
| 77 | +being sent by JSSE and SQL2K. It is, therefore, in the custom I/O streams that |
| 78 | +we are able to provide the mediation between JSSE and SQL2K. |
| 79 | + |
| 80 | +The input stream is relatively simple. Traffic from SQL2K is inspected and it is |
| 81 | +determined whether or not a TDS packet or an SSL record has been sent. If a TDS |
| 82 | +packet is detected then its header is stripped off. To ensure that we know where |
| 83 | +the packet/record boundaries are, the stream will, using the length attribute, |
| 84 | +read in the entire packet/record and store it in a buffer. JSSE is then fed |
| 85 | +bytes from this buffer until it is exhausted. |
| 86 | + |
| 87 | +The output stream is more complex. Although it will always receive SSL or TLS |
| 88 | +records from JSSE, its required action depends on the contents of those records. |
| 89 | +It is therefore necessary to parse the records to a greater extent than is done |
| 90 | +by the input stream. This is true particularly of the handshake records, which |
| 91 | +have a sub-type descriptor as part of the record body (which has its own |
| 92 | +header). To reflect this requirement, there is a fairly elaborate set of |
| 93 | +classes, one for each record type. Some of these classes add little value, and |
| 94 | +are basically identical to others. But constructing them adds virtually no |
| 95 | +overhead and their existence makes the code more readable and debugging a lot |
| 96 | +simpler � seeing in the debugger, for example, that a TLSChgCipherSpecRecord has |
| 97 | +just been written is certainly helpful. They are justifiable also for reasons of |
| 98 | +symmetry: it is necessary, for example, to have a class that parses a handshake |
| 99 | +record, so why not have classes for other record types, too? |
| 100 | + |
| 101 | +How to use it |
| 102 | +============= |
| 103 | + |
| 104 | +An optional property, "ssl", has been added to the URL. Its legal values are: |
| 105 | + |
| 106 | + o off � SSL is not request or used. This is the default. |
| 107 | + o request � SSL is requested; if the server does not support it then a plain |
| 108 | + connection is used. |
| 109 | + o require � SSL is requested; if the server does not support it then an |
| 110 | + exception is thrown. |
| 111 | + o authenticate � Same as require except the server's certificate must be |
| 112 | + signed by a trusted CA. |
| 113 | + |
| 114 | +Additional libraries |
| 115 | +==================== |
| 116 | + |
| 117 | +If JDK 1.3 or below is used, the optional JSSE 1.0.3 will be required. JSSE has |
| 118 | +been integrated into JDK 1.4. |
| 119 | + |
| 120 | +Future work |
| 121 | +=========== |
| 122 | + |
| 123 | +Some of the classes in the net.sourceforge.jtds.ssl package contain code that |
| 124 | +deals with TDS packets. Much of this code duplicates functionality that already |
| 125 | +exists in jTDS. But, without refactoring other parts of jTDS, it was not |
| 126 | +possible to make use of it. If any refactoring is to be done, it probably ought |
| 127 | +to be done as a separate task, by jTDS developers. |
0 commit comments