Java - SocketException: Connection Reset when Client Send Request - java

In Java Network Programming 4th Edition, Chapter 10 about Secure Socket, there is an example of build Secure Server. The code can be find here.
I'm trying to make more simple version of the code. Here is my code:
try {
SSLContext context = SSLContext.getInstance(algorithm);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
KeyStore ks = KeyStore.getInstance("JKS");
//char[] password = System.console().readPassword("Password: "); // open the .class with command line
char[] password = {'t', 'h', 'i', 's', 'i', 's', 't', 'i', 'a', 'n'};
ks.load(new FileInputStream("src/jnp4e.keys"), password);
kmf.init(ks, password);
context.init(kmf.getKeyManagers(), null, null); // null = accept the default
// wipe the password
Arrays.fill(password, '0');
SSLServerSocketFactory factory
= context.getServerSocketFactory();
SSLServerSocket server
= (SSLServerSocket) factory.createServerSocket(PORT);
// add anonymous (non-authenticated) cipher suites
String[] supported = server.getSupportedCipherSuites();
String[] anonCipherSuitesSupported = new String[supported.length];
int numAnonCipherSuitesSupported = 0;
for (int i = 0; i < supported.length; i++) {
if (supported[i].indexOf("_anon_") > 0) {
anonCipherSuitesSupported[numAnonCipherSuitesSupported++]
= supported[i];
}
}
String[] oldEnabled = server.getEnabledCipherSuites();
String[] newEnabled = new String[oldEnabled.length
+ numAnonCipherSuitesSupported];
System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
System.arraycopy(anonCipherSuitesSupported, 0, newEnabled,
oldEnabled.length, numAnonCipherSuitesSupported);
server.setEnabledCipherSuites(newEnabled);
System.out.println("OK..");
// Now all the set up is complete and we can focus
// on the actual communication.
while (true) {
// This socket will be secure,
// but there's no indication of that in the code!
try (Socket theConnection = server.accept()) {
InputStream in = theConnection.getInputStream();
Reader r = new InputStreamReader(in, "UTF-8");
int c;
while ((c = r.read()) != -1) {
System.out.write(c);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
} catch (IOException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException ex) {
ex.printStackTrace();
}
The only diffence is in my code I create a Reader so the server can read characters.
I tried this server with simple client that send text. Here is the Client:
int port = 7000; // default https port
String host = "localhost";
SSLSocketFactory factory
= (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = null;
try {
socket = (SSLSocket) factory.createSocket(host, port);
// enable all the suites
String[] supported = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites(supported);
Writer out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
out.write("Hello");
}catch(Exception e){
}
I run the Server first and then the Cient. But when the Server accept input from Client it throws this exception:
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:189)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
...
UPDATE CLIENT
Based on dave answer, I add 2 lines of code flush() and close()
...
out.write("Hello");
out.flush();
socket.close();
...
But another exception arrive:
javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown

OutputStreamWriter on a socket stream apparently buffers and your client didn't .flush() or .close() so your data isn't actually sent.
If your Java program (or more exactly JVM) exits without doing .close() on a socket stream (including closing a Writer which passes through to the Stream) handling depends on the platform; on Windows it sends a RST which causes the "Connection reset" exception you see at the peer. Unix closes the connection normally at the TCP level, which is actually not fully normal for SSL/TLS, but "close enough" (as it were) that Java treats it as EOF.
Edit for followon question:
Server getting SSLHandshakeException "received alert bad_certificatecertificate_unknown" which theoretically could mean a few things but almost always means that the certificate the server is using (from the keystore you loaded, along with matching privatekey) is not signed by a CA (Certificate Authority) trusted by the client.
The code you show for the client doesn't do anything to set or alter its truststore; if there isn't code elsewhere that does so, or external settings like the java commandline option -Dx=y to set system properties, the client will use the JSSE default truststore, which is the file JRE/lib/security/jssecacerts if it exists otherwise the file JRE/lib/security/cacerts (where JRE is the directory where your JRE is installed; if you use a JDK, the JRE is a subdirectory of the JDK directory). If you (and anyone else on your system) haven't modified these files since the JRE was installed, jssecacerts doesn't exist and cacerts contains a set of "well-known root" CAs determined by Oracle, like Verisign and Equifax etc.
Thus, you need to either:
use a certificate issued by a well-known CA; if you don't already have such a cert you have to obtain it from the CA by proving (at least) your control of the domain name(s) certified and depending on the CA possibly paying a fee; if you do have or get such a cert, install it in your keystore, in the privatekey entry, with any chain certs (for well-known CAs there almost always is at least one chain cert).
use a certificate issued by any other CA, including an adhoc CA you make up, and including as the limit case a selfsigned certificate which is implicitly its own CA, such as the one keytool -genkeypair generates automatically; and put the CA certificate for that CA (or that selfsigned cert) into the truststore used by the client. For that there are two ways:
put the server's CA cert (or selfsigned cert) in the default truststore file of the JRE used by the client. This does affect any other programs sharing that default truststore, which is potentially all other programs using that JRE. If you use jssecacerts it only affects JSSE, if you use cacerts it also affects the verification of signed code (if any), plus it gets wiped out if you upgrade your JRE in place, as usually is automatic on Windows.
create (or reuse) another truststore, put the server's CA cert in there, and have the client use that nondefault truststore. There are several options for that: set the system properties for the default truststore externally, set them explicitly in your program (before first use of JSSE!), explicitly load a "keystore" file (actually containing the cert(s)) and use its trustmanager in a nondefault SSLSocketFactory much like your server code does for keymanager, or even write your own trustmanager with whatever store(s?) you like and use that similarly.
Edit#2 Simple Example
Covering all these options in detail would be much too long, but one simple option is as follows.
Generate keypair and (default) selfsigned certificate:
keytool -genkeypair -keystore ksfile -keyalg RSA
For the prompt "first and last name" (which is actually the CommonName attribute in the cert) enter the name of the server, in particular the name the client(s) will use to connect to the server; in the question this is "localhost". The other name fields don't matter; fill or omit them as you like, except that Country if used must be 2 letters as the prompt says. Instead of answering the prompts, you can add on the command line -dname "CN=common_name_value". If you have more than one name for the server(s) there are some options omitted here.
For some other applications you may need to specify the entry name with -alias name; for this question it isn't needed.
Get a copy of the certificate:
keytool -exportcert -rfc -keystore ksfile [-alias name] -file certfile
In this example the client is on the same machine as the server. In another case it would be necessary to copy the contents of this file from the server to the client; this is often done most conveniently by copying the file.
Put cert in client truststore. As above there are many options, but the one you will probably see suggested the most often because it is usually the quickest to start is to just put it in the JRE default file, cacerts:
keytool -importcert -keystore JRE/lib/security/cacerts -file certfile
where JRE is the directory where your JRE (Java Runtime Environment) is installed. This depends on your OS and how you installed your JRE (or JDK, which includes a JRE) such as with a package manager or not. If you have more than one JRE and/or JDK installed, it depends which one you are using.
On Unix if you invoke java without specifying a path, which java (or in bash and perhaps other shells, type java) will tell you the full pathname that is run. Note however this is often a symbolic link to the real location, which should be in the form /somewhere/java[version]/bin/java; change the bin/java part to lib/security/cacerts.
On Windows, if you install a normal "system-wide" Java, the program you run is %windir%\system32\java.exe where windir is usually c:\windows but the actual code and files for JRE are in c:\program files\java\jreN or c:\program files (x86)\java\jreN depending on your architecture (32-bit or 64-bit) and jreN is currently jre7 or jre8 as applicable, but likely to expand in the future. Or run the Java Control Panel; in the Java tab the View button shows location(s) of the installed JRE(s).

Related

Debugging SSLSocket.startHandshake exception

I have a java TLS based client/server applications I wrote 2-3 years ago and have been using almost daily since then on a number of different linux installations. I don't do a lot of work in java -- writing this was really an exercise.
Anyway, it's never caused me grief since then, until yesterday, when all of a sudden I cannot authenticate with a server from a Fedora 31 box. The cause was an update to openjdk the day before. Other machines that have not had the update do not have the problem. However, I tried rebuilding using Oracle JDK 13, and the problem is replicated.
Just to be clear:
The server where the exception is thrown has not been updated.
The only clients which now fail are ones connecting from the system which had the jdk update.
Compiling the whole application and testing with a recent Oracle JDK has the problem -- clients cannot connect to servers. So this is something I have to fix or roll back the JRE and use that everywhere. >_<
Authentication is done using PKIX where there's a private CA backing the set of client and server certs, in pkcs12 files generated with keytool. The SSLContext is initialized with a TrustManagerFactor and KeyStore more or less like this:
KeyStore trusts;
TrustManagerFactory tmf;
String trustPassword("abc123");
try (
FileInputStream trustIn =
new FileInputStream(new File("foobar.pkcs12"))
) {
tmf = TrustManagerFactory.getInstance("PKIX");
trusts = KeyStore.getInstance("pkcs12");
trusts.load(trustIn, trustPassword.toCharArray());
tmf.init(trusts);
}
SSLContext sscon = SSLContext.getInstance("TLSv1.2");
sscon.init (
someKeyManager,
tmf.getTrustManagers(),
new SecureRandom()
);
As mentioned, I am no java wizard. However, I have done TLS network programming in other languages, so the concepts are pretty familiar.
The server socket is created:
SSLServerSocket listenSock = sscon
.getServerSocketFactory()
.createServerSocket(port, backlog);
And a client connection:
SSLSocket client = (SSLSocket)listenSock.accept();
SSLParameters sslp = new SSLParameters();
sslp.setProtocols(new String[]{"TLSv1.2"});
client.setSSLParameters(sslp);
try { client.startHandshake(); }
That try is where an exception is now thrown on the server, "Received fatal alert: certificate_unknown".
A jdb dump of the exception:
ex = {
serialVersionUID: 4511006460650708967
java.io.IOException.serialVersionUID: 7818375828146090155
java.lang.Exception.serialVersionUID: -3387516993124229948
java.lang.Throwable.serialVersionUID: -3042686055658047285
java.lang.Throwable.backtrace: instance of java.lang.Object[5] (id=2005)
java.lang.Throwable.detailMessage: "readHandshakeRecord"
java.lang.Throwable.UNASSIGNED_STACK: instance of java.lang.StackTraceElement[0] (id=2007)
java.lang.Throwable.cause: instance of java.net.SocketException(id=2008)
java.lang.Throwable.stackTrace: instance of java.lang.StackTraceElement[0] (id=2007)
java.lang.Throwable.depth: 5
java.lang.Throwable.SUPPRESSED_SENTINEL: instance of java.util.Collections$EmptyList(id=2009)
java.lang.Throwable.suppressedExceptions: instance of java.util.ArrayList(id=2010)
java.lang.Throwable.NULL_CAUSE_MESSAGE: "Cannot suppress a null exception."
java.lang.Throwable.SELF_SUPPRESSION_MESSAGE: "Self-suppression not permitted"
java.lang.Throwable.CAUSE_CAPTION: "Caused by: "
java.lang.Throwable.SUPPRESSED_CAPTION: "Suppressed: "
java.lang.Throwable.EMPTY_THROWABLE_ARRAY: instance of java.lang.Throwable[0] (id=2015)
java.lang.Throwable.$assertionsDisabled: true
}
None of which is helpful to me, meaning I don't understand it.
Where do I go from here? The certs are fine, at least according to keytool, and as mentioned this code had been running for years, accessed frequently without problem.1 I have searched around about the specific error ("certificate unknown") but the results aren't very helpful -- the cert is there, the cert is not expired, the trust was loaded initially without qualm, etc.
This includes regenerating the PKIX stuff with keytool at regular intervals, as the certs are created with a limited lifespan. The key pairs use 2048 bit RSA and -sigalg SHA256withRSA.
As it turns out, you can get some wonderful debug output during the handshake by using:
java --Djavax.net.debug=all ...
More about that: https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html
Since it was only a client system that had the problem post update, it makes sense that it was the client that was the one rejecting the connection. And the reason:
TrustAnchor with subject "CN=myAuthority, C=CA"
does not have keyCertSign bit set in KeyUsage extension
Which, on inspection, it did not, which is a bit surprising because this was the cert used successfully to sign all the (otherwise working) client and server certs. It's also a bit mysterious why this would matter when authenticating, but a little firmer security is not a bad thing.
The ultimate cause was in a script I've been using all this time to regenerate certs, and which was used to create the CA initially. This is part of the parameters passed to keytool when creating the self-signed CA cert:
-ext KU=keyCertSign -ext KU=cRLSign
The cert did have the latter, meaning the previous value was replaced. The correct way to do this is to list them together:
-ext KU=keyCertSign,cRLSign
Replacing the whole flawed at the root PKIX infrastructure wasn't too hard after all that.

Loading pk12 file within the application in Wildfly 16

I have a code which calls a rest api protected with certificate, and that code was working for some time without issues, until I migrate application from Wildfly 10 to Wildfly 16.
The code itself is straight forward, it creates http request and set custom socket factory: `
private SSLSocketFactory getSSLSocketFactory() {
char[] certPassword = {}; // password hidden from you
try {
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
final SSLContext sslContext = SSLContext.getInstance("TLS");
final KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fileInputStream = new FileInputStream(new File("cert.pk12"))) {
keyStore.load(fileInputStream, certPassword);
} catch (final Exception e) {
logger.error("....", e);
}
this.readLoadCertificateFile(keyStore);
kmf.init(keyStore, certPassword);
sslContext.init(kmf.getKeyManagers(), new TrustManager[]{new AnyTrust()}, null);
return sslContext.getSocketFactory();
} catch (Exception e) {
logger.error(".....", e);
}
throw new IllegalStateException("....");
}
HTTPRequest req = ....
req.setSSLSocketFactory(getSSLSocketFactory());
tokenHttpResp = req.send();`
`
All seems good but when I run this code from within WF16 it throws
IOException: Failed to load .p12 keystore:C:\Cert\cert.p12; error
constructing MAC: java.lang.SecurityException: JCE cannot authenticate
the provider BC;
org.bouncycastle.jcajce.provider.keystore.pkcs12.PKCS12KeyStoreSpi.engineLoad::-1
java.security.KeyStore.load in KeyStore.java::1445
I checked for the bouncy castle lib, and I don't have it in the application itself...
If any one knows what the issue could be I would appreciate any input.
Alternatively, I'm looking to move this socket factory creation in to the container itself, seems like Wildfly Elytron subsystem specifically designed for this, is it a good idea?
Answering my own question.
The error message like "JCE cannot authenticate the provider BC" indicating that the jar file, the Security Provider is loaded from, cannot be verified by JVM. Either the jar is not signed or signature cannot be verified. In my case, the newer Wildfly version has a newer version of bouncy castle library, which for some reason, cannot be verified by Java 8. Interesting enough, that it is fine with Java 10. Some people on the Internet says that this issue only occurs in Oracle's JVM and does not exist for Open JDK, I haven't tested it, just think it worth to mention.
To overcome issue you need to tell JVM to trust the security provider, for that, make sure that the Security Provider you want to use/JVM decided to use, mentioned in jre/lib/security/java.security file, it should have line like:
security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
Then copy Jars with Security Provider into /jre/lib/ext folder

Java caching SSL failures - can I flush these somehow

I'm trying to test my SSL implementation in unit tests and have one scenario I can't quite understand.
When I connect to a host once and fail, every following connection will fail also, even if it has the correct certificates. I'm assuming somewhere along the way I'd have to flush a cache.
Here is my code, server and client are running locally. I use one jks-File for both trustStore and keyStore. The error occurrs no matter what the initial error was, I'll always get the first error the next time.
If I don't perform the first request the second one works.
If you're wondering what the use case is here, we have some local servers that use https certificates from an internal PKI, when someone misconfigures a server or a certificate, we'd like to be able to change them obviously, without shutting down the whole VM.
//attempt a connection without certificates, will fail
try (final InputStream stream = new URL("https://localhost:" + port).openStream()){
System.out.println(IOUtils.toString(stream, Charset.defaultCharset()));
} catch (IOException e){
System.out.println("Failed to load: " + StackTraceUtil.getStackTrace(e));
}
//copies the jks file to a temporary location
final File jksFile = copyJKSFile();
//ignore host names, running locally, won't use this in production
HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> hostname.equalsIgnoreCase("localhost"));
//set the system properties
System.setProperty("javax.net.ssl.keyStore", jksFile.getAbsolutePath());
System.setProperty("javax.net.ssl.keyStorePassword", password);
System.setProperty("javax.net.ssl.trustStore", jksFile.getAbsolutePath());
System.setProperty("javax.net.ssl.trustStorePassword", password);
//this should work now
try (final InputStream stream = new URL("https://localhost:" + port).openStream()){
System.out.println(IOUtils.toString(stream, Charset.defaultCharset()));
} catch (IOException e){
System.out.println("Failed to load: " + StackTraceUtil.getStackTrace(e));
}
Thanks for any help!
So I found a solution and I thought I'd share it in case someone else would have this problem at some point.
The class javax.net.ssl.HttpsURLConnection uses a javax.net.ssl.SSLSocketFactory to load the Key- and TrustStore, which uses a javax.net.ssl.SSLContext internally. When you don't overwrite anything, it uses the default implementation, which loads the files and can't be reset once loaded.
So what I did was not to use the default implementation, but to set my own SSLContext, when I knew the files would change.
final SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
If you want to use older versions of TLS, the full list should be here

Enabling TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA cipher suite [duplicate]

Server:
TLS Version: v1.2
Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Client:
JRE 1.7
I am receiving the below error when I try to connect to the Server from Client through SSL directly:
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
The below code enables TLSv1.2
Set<String> enabledTLSSet = new HashSet<String>(Arrays.asList(sslsocket.getEnabledProtocols()));
enabledTLSSet.add("TLSv1.2");
sslsocket.setEnabledProtocols(enabledTLSSet.toArray(new String[enabledTLSSet.size()]));
The below code enables TLS_RSA_WITH_AES_256_CBC_SHA256 Cipher Suite:
Set<String> enabledCipherSuitesSet = new HashSet<String>(Arrays.asList(sslsocket.getEnabledCipherSuites()));
enabledCipherSuitesSet.add("TLS_RSA_WITH_AES_256_CBC_SHA256");
sslsocket.setEnabledCipherSuites(enabledCipherSuitesSet.toArray(new String[enabledCipherSuitesSet.size()]));
After doing both of the above from Java code, I'm able to connect to the server successfully through SSL.
Is it possible to enable/force TLSv1.2 and TLS_RSA_WITH_AES_256_CBC_SHA256 in Java 7 without changing any Java Code through properties, parameters or Debug props?
I tried all of the below properties in all forms and combinations (enabling and disabling) and failed.
-Dhttps.protocols=TLSv1.2
-Dhttps.cipherSuites=TLS_RSA_WITH_AES_256_CBC_SHA256
-Ddeployment.security.TLSv1.2=true
I'm executing the program similar to the below:
java -jar -Dhttps.protocols=TLSv1.2 -Dhttps.cipherSuites=TLS_RSA_WITH_AES_256_CBC_SHA256 Ddeployment.security.TLSv1.2=true -Djavax.net.debug=ssl:handshake SSLPoker.jar <SERVER> 443
SSLPoker contains the below code:
package com.ashok.ssl;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
/**
* Establish a SSL connection to a host and port, writes a byte and prints the response - Ashok Goli. See
* http://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services
*/
public class SSLPoke {
/**
* The main method.
* Usage: $java -jar SSLPoker.jar <host> <port>
*
* #param args the arguments
*/
public static void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: " + SSLPoke.class.getName() + " <host> <port>");
System.exit(1);
}
try {
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket =
(SSLSocket) sslsocketfactory.createSocket(args[0], Integer.parseInt(args[1]));
InputStream in = sslsocket.getInputStream();
OutputStream out = sslsocket.getOutputStream();
// Write a test byte to get a reaction :)
out.write(1);
while (in.available() > 0) {
System.out.print(in.read());
}
System.out.println("Successfully connected");
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
Any pointers how to achieve this with no Java code changes would be much appreciated.
It is only possible if you use a simple HTTPS connection (not SSL Sockets) using the properties
-Dhttps.protocols=TLSv1.2
-Dhttps.cipherSuites=TLS_RSA_WITH_AES_256_CBC_SHA256
See the post at http://fsanglier.blogspot.com.es/
Java 7 introduced support for TLS v1.2 (refer to
http://docs.oracle.com/javase/7/docs/technotes/guides/security/enhancements-7.html)
BUT does not enable it by default. In other words, your client app
must explicitly specify "TLS v1.2" at SSLContext creation, or
otherwise will just not be able to use it.
If you need to use directly secure socket protocol, create a "TLSv1.2" SSLContext at application startup and use the SSLContext.setDefault(ctx) call to register that new context as the default one.
SSLContext context = SSLContext.getInstance("TLSv1.2");
SSLContext.setDefault(context);
The JREs disable all 256-bit crypto by default. To enable you can download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files here: http://www.oracle.com/technetwork/java/javase/downloads/index.html
Replace the local_policy.jar and US_export_policy.jar jars files into your lib/security in jre directory.
It looks like current JRE's ship both the limited and unlimited policy files under the JRE's install folder in lib/security, each in separate sub folders. By default, in lib/security/java.security, the limited policy is used by default. But if you uncomment the crypto.policy=unlimited line, that will allow Java to use the unlimited policy files and enable the 256-bit ciphers/algorithms.

How to accept/find the certificate from an opc ua server?

I am new to opc ua and not a pro in java. While setting up a client in java I'm having trouble with the certificate dealing. I want to connect to the server via Basic 256, SignAndEncrypt. As I understand, in this stage of security a certificate, created or loaded by the client, is send to the server, where it must be accepted. The server then sends a certificate back to the client, which then needs to be accepted by the client. Please, correct me, if I'm wrong.
Creating/Loading a certificate on the client side and sending it to the server does already work fine (see code below) and I can then accept it on the server side manually. But after that I'm stuck: How can I see this certificate validation in my code and how can I find the server certificate, let alone accept it?
I used the SampleConsoleClient of opc ua for some orientation during implementation. But in contrast to there, I do not use any user input.
Here's some of my code so far.
Initialization:
try {
client = new UaClient(serverUri);
} catch (final URISyntaxException e) {
throw new InitializationException("The server uri has an invalid syntax.", e);
}
try {
client.setApplicationIdentity(createApplicationIdentity());
} catch (final SecureIdentityException e) {
throw new InitializationException(
"Application Identity could not be created due to a Security Identity Exception.", e);
} catch (final IOException e) {
throw new InitializationException("Application Identity could not be created due to an IO Exception.",
e);
}
createApplicationIdentity():
final ApplicationDescription appDescription = new ApplicationDescription();
appDescription.setApplicationName(new LocalizedText(APPLICATION_NAME, Locale.ENGLISH));
appDescription.setApplicationUri(APPLICATION_URI);
appDescription.setProductUri(PRODUCT_URI);
appDescription.setApplicationType(ApplicationType.Client);
// Setting security features
client.setSecurityMode(SecurityMode.BASIC256_SIGN_ENCRYPT);
client.setCertificateValidator(validator);
validator.setValidationListener(myValidationListener); //myValidationListener is similar to most lines in MyCertificateValidationListener in the opc ua samples
final File privatePath = new File(validator.getBaseDir(), "private");
final KeyPair issuerCertificate = null;
final int[] keySizes = null;
final ApplicationIdentity identity = ApplicationIdentity.loadOrCreateCertificate(appDescription,
"Sample Organisation", "opcua", privatePath, issuerCertificate, keySizes, true);
identity.setApplicationDescription(appDescription);
return identity;
After initializing, I try to connect like this (with annotation, how I imagine the connection could work properly):
final String securityPolicy = client.getEndpoint() == null
? client.getSecurityMode().getSecurityPolicy().getPolicyUri()
: client.getEndpoint().getSecurityPolicyUri();
client.setSessionName(String.format("%s#%s/Session%d", APPLICATION_NAME,
ApplicationIdentity.getActualHostNameWithoutDomain(), ++sessionCount));
try {
//Idea: catch the server certificate and accept it. Only if that was possible: connect
client.connect();
} catch (final ServiceException e) {
e.printStackTrace();
}
client.setKeepSubscriptions(false);
// After that resolving namespace index (works fine)
}
And the error, that is thrown:
WARN (?:?): /<IPofServer> Error org.opcfoundation.ua.common.ServiceResultException: Bad_SecurityChecksFailed
(0x80130000) "An error occurred verifying security." at
org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(Unknown Source)
com.prosysopc.ua.client.ConnectException: Failed to create secure channel to server: : opc.tcp://<IPofServer>
[http://opcfoundation.org/UA/SecurityPolicy#Basic256,SignAndEncrypt]
ServiceResult=Bad_SecurityChecksFailed (0x80130000) "An error occurred verifying security."
at com.prosysopc.ua.client.UaClient.n(Unknown Source)
at com.prosysopc.ua.client.UaClient.connect(Unknown Source)
at *lineOfCode*
Caused by: org.opcfoundation.ua.common.ServiceResultException:
Bad_SecurityChecksFailed (0x80130000) "An error occurred verifying security."
at org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(Unknown Source)
With the lineOfCode being client.connect().
Thanks in advance for the help!!
The server will send it's certificate to the client. The client then has to
verify the validity of the certificate. This amounts to verifying the signature of the certificate, checking the validity, whether the hostname in the certificate matches the hostname in the endpoint, checking CRLs and so forth. Usually the SDK (the validator) should do this for you, but you might need to feed some parameters into the validator which checks should actually be performed. The security policy Basic256 imposes some minimal requirements on the certificate which the certificate should meet, of course. You can check the requirements here: http://opcfoundation-onlineapplications.org/profilereporting/ -- go to Security Category -> Facets -> Security policy.
check whether the server certificate is trusted. This usually amounts to checking whether a copy of the (puclic key) certificate has been put into some certicate store chosen as a trust store. If you write the client it's up to you to say which store to choose, but you will need to tell the validator where to look. I don't know that much about OPc UA development in Java, but you should check which certificate stores the validator expects. Maybe there is a default keyfile.
(On server side the same happens with the client certificate).
This asssumes you are starting out with self-signed certificates. If you are using certificates signed by a CA both applications (server and client) will need to be able to verify the whole chain of the other party. It can be stored locally in some store or can be send by the other party. At least one certificate in the chain has to be trustest (has to be put into the trust store).
For a general description on how UA security works have a look at this link:
https://opcfoundation.org/wp-content/uploads/2014/08/11_OPC_UA_Security_How_It_Works.pdf
For a detailed account you should consult the specification, available at GitHub.
Edit: one addtional remark which may help here: you seem to be using some SDK for the purpose in question. While validation of certificates, i.e. doing the signature checks etc, is usually covered by such an SDK the configuration of the application is the task of the application (programmer). This includes the location where you store trusted certificates and where and how you gather together missing parts of certificate chains. You might first try to check how demo clients and servers deal with this task, in other words check out the configuration tasks for such applications by trying to create a secure connection from, say, UA Expert to the sample servers from the OPC foundation. In the .Net SDK of the OPC foundation the location for the trust store defaults to a certain directory in the file system (a subfolder of C:\ProgramData\OpcFoundation, it's Windows only). You can, however, overwrite this when you initialize the validator. Other clients use their own directory structure for the storage of trusted certificates
You are obviously referring to the Prosys OPC UA Java SDK.
What always happens first, when you try to establish a secure connection for the first time, is that the server will deny access to your client application - and returns Bad_SecurityChecksFailed.
Only, after you have told the server to trust (the certificate of) your client application, will you get to the phase where the client application will try to verify the server's certificate - and your 'validationListener' will be triggered.
Thank you all for your answers. In the meantime, I tried basically copy/paste and modifying the connect() and initalize() methods from the SampleConsoleClient in the Prosys SDK samples And that worked. Guess it had something to do with renewing some information, but I'm not quite sure of that... Fact is, my application is now working, but thanks for your efforts!

Categories

Resources