I am using an Apache CXF client, running in a Windows Java 1.6.0_29-b11 VM to connect to an IBM mainframe (I believe it is zSeries), and invoking a SOAP Web Service running there. The connection is done through SSL/TLS, and most of the time works fine.
However, from time to time I have SSL Exceptions with a bad record MAC message. Here is the output of the program using with the javax.net.debug property.
2011-11-16 12:32:37,731 INFO LoggingOutInterceptor: Outbound Message
---------------------------
ID: 29
Address: https://1.2.3.4/access/servlet/blabla.atk123
Encoding: UTF-8
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=["Blablaaction/ATK123.Execute"]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ATK123.Execute xmlns="Blabla"><Usrid>WA</Usrid><Usrpwd>54321</Usrpwd><Ultautid>9999</Ultautid></ATK123.Execute></soap:Body></soap:Envelope>
--------------------------------------
pool-1-thread-1, setSoTimeout(30000) called
pool-1-thread-1, WRITE: TLSv1 Application Data, length = 321
pool-1-thread-1, WRITE: TLSv1 Application Data, length = 262
pool-1-thread-1, READ: TLSv1 Application Data, length = 483
pool-1-thread-1, READ: TLSv1 Application Data, length = 16148
pool-1-thread-1, READ: TLSv1 Application Data, length = 282
%% Invalidated: [Session-1, SSL_RSA_WITH_RC4_128_SHA]
pool-1-thread-1, SEND TLSv1 ALERT: fatal, description = bad_record_mac
pool-1-thread-1, WRITE: TLSv1 Alert, length = 22
pool-1-thread-1, called closeSocket()
pool-1-thread-1, handling exception: javax.net.ssl.SSLException: bad record MAC
2011-11-16 12:32:38,511 WARN PhaseInterceptorChain: Interceptor for {Blabla}ATK123#{Blabla}Execute has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault: bad record MAC
at org.apache.cxf.interceptor.LoggingInInterceptor.logging(LoggingInInterceptor.java:144)
at org.apache.cxf.interceptor.LoggingInInterceptor.handleMessage(LoggingInInterceptor.java:73)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:797)
.... (more stuff)
Unfortunately, I don't have possibilities to modify or debug the endpoint at the server.
What could be causing this?
How can I isolate and fix this behavior?
If you are getting a lot of bad packets due to some fault in your network it can happen that a bad packet will at random survive the 32-bit TCP checksum. About 1 in 4 billion bad packets will slip by TCP. Once this packet is delivered to SSL it will generate a bad record MAC for sure, because the SSL Mac is 96 bits in size.
If this is the cause, the only solution is improve the network.
Note that, in general, this is a very unlikely cause of a bad record MAC. Even a network with faulty hardware that generates bad packets is unlikely to generate them with correct IP and TCP metadata such that the packets are actually passed to the socket corresponding to the TLS connection.
This isn't related to Java, it is an SSL/TLS failure:
20 Bad record MAC fatal Possibly a bad SSL implementation, or payload
has been tampered with e.g. FTP firewall rule
on FTPS server.
It probably has something to do with the SSL implementation and the amount of data that is being sent being too big, I doubt it is random.
Related
I'm troubleshooting some SSL/TLS problems, with -Djavax.net.debug on the command line and it would help immensely to have some logging where the server certificate is logged in a format which can be parsed and read.
I tried following debug settings:
-Djavax.net.debug=ssl:record:plaintext
-Djavax.net.debug=ssl:handshake:verbose:keymanager:trustmanager
-Djavax.net.debug=ssl:handshake:verbose
The closed I came was with the last statement which logs the following:
10/11/2021 10:27:36 "version" : "v3",
10/11/2021 10:27:36 "serial number" : "I8 00 00 00 00 D2 91 BH 88 A4 10 58 00 00 02 00 04 9E 4B",
10/11/2021 10:27:36 "signature algorithm": "SHA256withRSA",
10/11/2021 10:27:36 "issuer" : "CN=test, DC=test, DC=test, DC=com",
10/11/2021 10:27:36 "not before" : "2021-07-23 17:38:30.000 UTC",
10/11/2021 10:27:36 "not after" : "2026-07-22 17:38:30.000 UTC",
10/11/2021 10:27:36 "subject" : "CN=CNTest, OU=TIS, O="ACME Inc", L=France, ST=Paris, C=EU",
10/11/2021 10:27:36 "subject public key" : "RSA",
10/11/2021 10:27:36 "extensions" : [
...
]
Which is already useful but it would help immensely to have the server certificate in a readable format to further troubleshoot the problem. It would help to compare the certificate we received with the actual that is on the server.
I already tried to use the openssl tooling to print the certificates. But the Java application is also using queues which seems to use different certificates than I was supplied and isn't easy to extract the queue certificates with the openssl tooling.
The above assumption was an incorrect assumption. I found the problem, we only supplied 1 ciphersuite on clientHello. One ciphersuite the server didn't support so that's why the handshaking failed:
11/9/2021 3:24:03 PMSession ID: {}
11/9/2021 3:24:03 PMCipher Suites: [TLS_DHE_DSS_WITH_AES_256_CBC_SHA256]
11/9/2021 3:24:03 PMCompression Methods: { 0 }
...
READ: TLSv1.2 Alert, length = 2
RECV TLSv1.2 ALERT: fatal, handshake_failure
called closeSocket()
handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
But still the original question stands and would be useful for debugging.
sources:
https://colinpaice.blog/2020/04/05/using-java-djavax-net-debug-to-examine-data-flows-including-tls/
https://access.redhat.com/solutions/973783
Using openssl to get the certificate from a server
https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html
For the old stack (below 8u261 or 11) in all cases, and the new stack when the protocol used (negotiated) is TLS1.2 or below, javax.net.debug=ssl:handshake:packet will show you a 'raw write' for each outgoing record with (all) the data in hex and ASCII, and at least two 'raw read' for each incoming record (one for the header of 5 bytes, and one or more for the data) ditto. The fields going into an outgoing record are shown before the record, while the fields decoded from an incoming record are shown after it. For example on a connection to https://example.com after (build and) write the ClientHello and read (and decode) the ServerHello, I get for the Certificate message/record this pastebin (otherwise exceeds Stack size limit).
Take the body data (only) and on Unix, or WSL, run it through cut -c9-58 <hex | xxd -r -p >bin (for old stack -c7-56). The file bin now contains the Certificate message exactly as received: the first byte is 0B, the next three bytes are the (bigendian) length of the body of the message, the next three bytes are the length of the certificate list, and the next three are the length of the first certificate. To separate out that first certificate, do tail -c+11 bin | head -c$((0x$(xxd -p -s7 -l3 bin))) >cert1. You can now convert the cert to PEM with openssl x509 -inform d <cert1 or examine it with any other x509 options, or examine it with keytool -printcert -file cert1, or any other suitable tool. If you want (all or some of) the other cert(s) in the chain, it's a little more complicated.
TLS1.3, in new stack only, is slightly different. It encrypts the Certificate message and may have a hello-retry cycle, may have an early CCS, and will have an EncryptedExtensions message before the Certificate. Add :plaintext to the sysprop and for each incoming record you get a raw read of the header (5 bytes) in clear, a raw read of the body in cipher, and a decryption of the body, like this for the Certificate message/record.
This message differs slightly from the previous protocol version by having one byte of 00 between the message length and the certificate-list length, so change to: tail -c+12 bin | head -c$((0x$(xxd -p -s8 -l3 bin))).
Note some servers may combine messages in one handshake record (or encrypted handshake record), rather than using a separate record for each as example.com did, which will require a slight variation. If you have an example (accessible) of such, I will adjust.
However, I have no idea what you mean by 'queues' in an SSL/TLS connection, and why the much easier openssl s_client isn't usable. If you mean virtual hosting with the server choosing among multiple certificates using SNI, see the man page description of the -servername x option.
It is not possible to get the server certificate as pem with javax.net.debug You can try the following snippet which I also use for my own project:
Map<String, List<String>> certificates = CertificateUtils.getCertificateAsPem(
"https://github.com/",
"https://stackoverflow.com/");
It will extract all the certificates for the given urls in pem format. It is available within my own library which is publicly available here: GitHub - SSLContext Kickstart
Or you can also do the same with a CLI, see here: GitHub - Certificate Ripper Below is an example of the snippet above:
crip print -u=https://github.com -u=https://stackoverflow.com -f=pem
Please let me know if this is something which will do the trick for you
While calling an external Api secured using Mutual SSL, getting the javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Went through the ssl debug logs and seems like client and server are able to exchange and validate the certificate and communication is failing after that step.
Putting relevant SSL debug logs :
^^^ClientHello, TLSv1.2
^^^List of CipherSuites
^^^ServerHello, TLSv1.2
^^^Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256
** Certificate chain
..............................
................................
^^^CertificateRequest
............
^^^ServerHelloDone
^^^Certificate chain
..................
....................
no IV derived for this protocol
CertificateVerify
Signature Algorithm SHA256withRSA
main, WRITE: TLSv1.2 Handshake, length = 264
main, WRITE: TLSv1.2 Change Cipher Spec, length = 1
*** Finished
verify_data: { 46, 243, 175, 158, 127, 112, 161, 153, 5, 195, 3, 35 }
main, WRITE: TLSv1.2 Handshake, length = 80
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Communication is failing after certificateverify step, not sure if it is due to "no IV derived for this protocol". Can someone throw some light on this, what could be possible reason for failure after client certificateverify step. Also for the client I am using Java 8 with Java Cryptography Extension (JCE) Unlimited Strength
I have prepared a simple TLS PSK client test case based on MockPSKTlsClient by Bouncy Castle.
In the main method I call:
public static void main(String[] args) throws IOException {
SecureRandom random = new SecureRandom();
TlsPSKIdentity identity = new BasicTlsPSKIdentity("Client_identity", Hex.decode("1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A"));
Socket socket = new Socket(InetAddress.getLocalHost(), 12345);
TlsClientProtocol proto = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(), random);
MockPSKTlsClient client = new MockPSKTlsClient(null, identity);
proto.connect(client);
OutputStream clearOs = proto.getOutputStream();
InputStream clearIs = proto.getInputStream();
clearOs.write("GET / HTTP/1.1\r\n\r\n".getBytes("UTF-8"));
Streams.pipeAll(clearIs, System.out); // why is java.io.EOFException thrown?
}
As you can see, I send a GET / HTTP/1.1 string to the openssl server, which is started as:
# openssl s_server \
-psk 1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A \
-psk_hint Client_identity\
-cipher PSK-AES256-CBC-SHA \
-debug -state -nocert -accept 12345 -tls1_2 -www
After that I call Streams.pipeAll() method, which is merely:
public static void pipeAll(InputStream inStr, OutputStream outStr)
throws IOException
{
byte[] bs = new byte[BUFFER_SIZE];
int numRead;
while ((numRead = inStr.read(bs, 0, bs.length)) >= 0) // Why is EOFException thrown?
{
outStr.write(bs, 0, numRead);
}
}
This copies openssl s_server answer to the screen and also surprisingly throws an EOFException at the end:
TLS-PSK client negotiated TLS 1.2
Established session: 68e647e3276f345e82effdb7cc04649f6872d245ae01489c08ed109c5906dd16
HTTP/1.0 200 ok
Content-type: text/html
<HTML><BODY BGCOLOR="#ffffff">
<pre>
s_server -psk 1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A1A -psk_hint Client_identity -cipher PSK-AES256-CBC-SHA -debug -state -nocert -accept 12345 -tls1_2 -www
Secure Renegotiation IS supported
Ciphers supported in s_server binary
TLSv1/SSLv3:PSK-AES256-CBC-SHA
---
Ciphers common between both SSL end points:
PSK-AES256-CBC-SHA
Signature Algorithms: RSA+SHA1:RSA+SHA224:RSA+SHA256:RSA+SHA384:RSA+SHA512:DSA+SHA1:DSA+SHA224:DSA+SHA256:DSA+SHA384:DSA+SHA512:ECDSA+SHA1:ECDSA+SHA224:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512
Shared Signature Algorithms: RSA+SHA1:RSA+SHA224:RSA+SHA256:RSA+SHA384:RSA+SHA512:DSA+SHA1:DSA+SHA224:DSA+SHA256:DSA+SHA384:DSA+SHA512:ECDSA+SHA1:ECDSA+SHA224:ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512
---
New, TLSv1/SSLv3, Cipher is PSK-AES256-CBC-SHA
SSL-Session:
Protocol : TLSv1.2
Cipher : PSK-AES256-CBC-SHA
Session-ID: 68E647E3276F345E82EFFDB7CC04649F6872D245AE01489C08ED109C5906DD16
Session-ID-ctx: 01000000
Master-Key: B023F1053230C2938E1D3FD6D73FEB41DEC3FC1068A390FE6DCFD60A6ED666CA2AD0CD1DAD504A087BE322DD2C870C0C
Key-Arg : None
PSK identity: Client_identity
PSK identity hint: Client_identity
SRP username: None
Start Time: 1479312253
Timeout : 7200 (sec)
Verify return code: 0 (ok)
---
13 items in the session cache
0 client connects (SSL_connect())
0 client renegotiates (SSL_connect())
0 client connects that finished
14 server accepts (SSL_accept())
0 server renegotiates (SSL_accept())
13 server accepts that finished
0 session cache hits
0 session cache misses
0 session cache timeouts
0 callback cache hits
0 cache full overflows (128 allowed)
---
no client certificate available
</BODY></HTML>
TLS-PSK client raised alert: fatal(2), internal_error(80)
> Failed to read record
java.io.EOFException
at org.bouncycastle.crypto.tls.TlsProtocol.safeReadRecord(Unknown Source)
at org.bouncycastle.crypto.tls.TlsProtocol.readApplicationData(Unknown Source)
at org.bouncycastle.crypto.tls.TlsInputStream.read(Unknown Source)
at de.afarber.tlspskclient2.Main.pipeAll(Main.java:52)
at de.afarber.tlspskclient2.Main.main(Main.java:44)
Exception in thread "main" java.io.IOException: Internal TLS error, this could be an attack
at org.bouncycastle.crypto.tls.TlsProtocol.failWithError(Unknown Source)
at org.bouncycastle.crypto.tls.TlsProtocol.safeReadRecord(Unknown Source)
at org.bouncycastle.crypto.tls.TlsProtocol.readApplicationData(Unknown Source)
at org.bouncycastle.crypto.tls.TlsInputStream.read(Unknown Source)
at de.afarber.tlspskclient2.Main.pipeAll(Main.java:52)
at de.afarber.tlspskclient2.Main.main(Main.java:44)
My question is: why is EOFException thrown?
Usually InputStream.read() is supposed to return -1 at the end of stream and not throw an exception.
How to detect an end of stream properly, when TLS PSK encryption is used?
In the long term I would like to extend my test case to a program acting as reverse PSK TLS proxy in front of embedded Jetty - and would prefer not to rely on exceptions to detect that the client is done reading or writing.
EOFException is thrown (as of v1.56) because the required close_notify alert was not received. This means that the TLS layer cannot exclude the possibility that the application data was truncated.
Truncation means that the data you received so far was correctly (per the active cipher suite) transmitted, but there may have been more data that you didn't receive. Truncation may be accidental or malicious. For many applications, later data may affect the meaning of earlier data, so a truncation may arbitrarily alter semantics.
For some application protocols, it may be possible to determine that there was no actual truncation (i.e. just missing close_notify) - consider the HTTP Content-Length header, or that some or all of the truncated data might still be usefully accepted - consider a stream of self-delimiting, independent messages. This cannot be done in the TLS layer itself; or rather, it is done by requiring close_notify!
So, EOFException is raised to "[signal] that an end of file or end of stream has been reached unexpectedly during input". At this point the application should conservatively assume the data was truncated, but application-specific mechanisms may yet allow the acceptance of part or all of the data as explained above.
As of (not yet released) v1.57 we have added TlsNoCloseNotifyException as a subclass of EOFException, that will only/always be thrown in this specific case, hopefully allowing for simpler application code.
Currently trying to invoke a web service, however i am getting a connection reset/connection reset by peer, below is the stack trace:
WRITE: TLSv1 Handshake, length = 81
WRITE: SSLv2 client hello message, length = 110
waiting for close_notify or alert: state 1
Exception while waiting for close java.net.SocketException: Connection reset
handling exception: java.net.SocketException: Connection reset
SEND TLSv1 ALERT: fatal, description = unexpected_message
WRITE: TLSv1 Alert, length = 2
Exception sending alert: java.net.SocketException: socket write error: Connection reset by peer.
called closeSocket()
called close()
called closeInternal(true)
called close()
called closeInternal(true)
called close()
called closeInternal(true)
It was an SSL issue, as per the trace, there was only a Client Hello, but no Server Hello Response, (http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html) after import of server public key to client keystore it is now working
I apologize for the really newbie quality of this question.
I have a EJBCA server set up and two CA's on it. One is the default one I created the server instance with.
I took the wsdl from EJBCA and generated Java code with it.
Using the generated java client code, I'm trying to open a connection to the EJBCA server.
According to EJBCA here: http://ejbca.sourceforge.net/ws/org/ejbca/core/protocol/ws/client/gen/package-summary.html
This the way to initialize a connection to the server.
I took the truststore.jks file from the EJBCA server as well as the superadmin.jks file from the server and put it on my local machine and made it available for my app as a resource.
val truststore = this.getClass.getClassLoader.getResource("pathto/wsTrustStore")
System.setProperty("javax.net.ssl.trustStore", truststore.getFile)
System.setProperty("javax.net.ssl.trustStorePassword","wsTrustStorePassword")
val keystore = this.getClass.getClassLoader.getResource("pathto/superadminKeyStore")
System.setProperty("javax.net.ssl.keyStore", keystore.getFile)
System.setProperty("javax.net.ssl.keyStorePassword", "superadminKeyStorePassword")
val qname = new QName(componentConfig.getString("http://ws.protocol.components.ejbca.org/"), "EjbcaWSService")
val service = new EjbcaWSService(new URL("https://myejbcaserver.com:8443/ejbca/ejbcaws/ejbcaws?wsdl"), qname)
service.getEjbcaWSPort
this is the java ssl debug logs
* CertificateRequest
Cert Types: RSA, DSS, ECDSA
Cert Authorities:
ServerHelloDone
** Certificate chain
* ClientKeyExchange, RSA PreMasterSecret, TLSv1
New I/O worker #2, WRITE: TLSv1 Handshake, length = 141
...
New I/O worker #2, WRITE: TLSv1 Change Cipher Spec, length = 1
* Finished
verify_data: { 95, 232, 182, 57, 141, 10, 130, 36, 152, 210, 86, 173 }
New I/O worker #2, WRITE: TLSv1 Handshake, length = 48
New I/O worker #2, waiting for close_notify or alert: state 1
New I/O worker #2, READ: TLSv1 Alert, length = 2
New I/O worker #2, RECV TLSv1 ALERT: fatal, bad_certificate
%% Invalidated: [Session-1, TLS_RSA_WITH_AES_128_CBC_SHA]
New I/O worker #2, called closeSocket()
New I/O worker #2, Exception while waiting for close javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
New I/O worker #2, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
Is there anyone who has experience with this that can help me out?