Skip to content

PLCnext Technology - Java RSC - SSL Trust manager

I am currently trying to use the PLCnext Java RSC API to get data from the GDS of a PLCnext target. The application which uses the Java API starts its own HTTP web server with ssl verification. As long as the PLCnext Java API is not used or used after the web server has started, everything works fine. But unfortunately we need to use the PLCnext Java API before the web server has started, to connect to the PLCnext target first. But this does not work.

Somehow the PLCnext Java API seems to manipulate the systems SSL trust store. Because when the web server starts after the connection to the PLCnext target has been established, I get the following errors:

ERROR [main] jetty.JettyServer (JettyServer.java:85) - Unable to launch Jetty
java.security.KeyStoreException: Failed to create trust manager
at org.bouncycastle.jsse.provider.ProvTrustManagerFactorySpi.engineInit(Unknown Source) ~[bctls-jdk15on-1.62.jar:1.62.0.0]
at javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:250) ~[?:1.8.0_222]
at org.eclipse.jetty.util.ssl.SslContextFactory.getTrustManagers(SslContextFactory.java:1291) ~[jetty-util-9.4.18.v20190429.jar:9.4.18.v20190429]

...

Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
at java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:200) ~[?:1.8.0_222]
at java.security.cert.PKIXParameters.(PKIXParameters.java:120) ~[?:1.8.0_222]
at java.security.cert.PKIXBuilderParameters.(PKIXBuilderParameters.java:104) ~[?:1.8.0_222]
at org.bouncycastle.jsse.provider.ProvX509TrustManager.(Unknown Source) ~[bctls-jdk15on-1.62.jar:1.62.0.0]
... 24 more

 

Could you provide more information about how the Java API is handling SSL verification and why it needs to manipulate the trust manager? Is there any way to use default trust managers or something like that?

 

Comments

  • Hello jbals,


    here are some snippets of a soon to be released Doumentation.

    I hope this helps you.

    SSL Handling

    The connect method with the TcpCommunicationSettings parameter uses the default system JSSE provider e.g. SunJSSE. The provider must be able to handle the device certificate.

    The default device certificate should be validated according to the CHAIN model and not to the PKIX model.

     

    Therefore the RSC API provides a TrustManager which uses the BouncyCastle library functions. This TrustManger will be initialized during the first connection attempt. The TrustManager adds the JCE BouncyCastle provider and JSSE BouncyCastle provider. If there is no instance of the provider, a new instance will be created and added to the provider list.

    Note:  BouncyCastle is used instead of specified default provider 

    This TrustManager will be used by the SSL session if the connect methods with the ConnectionInfo parameter of the ServiceManger class are called.

    The Java RSC API contains a trust store in PKCS#12 format that contains the certificate which is required to validate the device certificate.

    This trust store is used by the TrustManager unless a custom specified trust store shall be used which must be enabled by useSystemTrustStore=true.

    ServiceManager serviceManager;
    ...
    // connect(ConnectionInfo connectionInfo, SecurityInfo securityInfo, ValidityModel validityModel, boolean useSystemTrustStore)
    boolean useSystemTrustStore=true;
    serviceManager.connect(connectionInfo, securityInfo, null, useSystemTrustStore);

     

     

     BouncyCastle is used instead of specified default provider

    In Version 1.0.281.0 of the Java RSC API the BouncyCastle providers are placed at position 1.

    Solution: Load BouncyCastle providers before connection is established with Java RSC API.

    import java.security.Security;
    import java.security.Provider;

    ...

    Class<?> cls = Thread.currentThread().getContextClassLoader()
        .loadClass("org.bouncycastle.jce.provider.BouncyCastleProvider");
    String jceProviderName = (String) cls.getField("PROVIDER_NAME").get(null);
    if (Security.getProvider(jceProviderName) == null) {
      Security.addProvider((Provider) cls.getConstructor().newInstance());
    }
    cls = Thread.currentThread().getContextClassLoader()
        .loadClass("org.bouncycastle.jsse.provider.BouncyCastleJsseProvider");
    String jsseProviderName = (String) cls.getField("PROVIDER_NAME").get(null);
    if (Security.getProvider(jsseProviderName) == null) {
      Security.addProvider((Provider) cls.getConstructor().newInstance());
    }

    Note: This type of registration is not persistent and can be done only by "trusted" programs.



    If not you could provide a snipped of your application that does the establishes the connection.
    We can then check this to work out a workaround together.

    kind regards,

    Oliver

     

  • Hello,

    we tried your code example but get the following error message now:

    Dec 19, 2019 9:30:27 AM org.bouncycastle.jsse.provider.ProvTlsClient notifyAlertRaised
    INFO: Client raised fatal(2) certificate_unknown(46) alert: Failed to read record
    org.bouncycastle.tls.TlsFatalAlert: certificate_unknown(46)
    at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.checkServerTrusted(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvTlsClient$1.notifyServerCertificate(Unknown Source)
    at org.bouncycastle.tls.TlsUtils.processServerCertificate(Unknown Source)
    at org.bouncycastle.tls.TlsClientProtocol.handleServerCertificate(Unknown Source)
    at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)
    at org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)
    at org.bouncycastle.tls.TlsProtocol.blockForHandshake(Unknown Source)
    at org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
    at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
    at com.phoenixcontact.arp.system.rsc.b.d.connect(SourceFile:122)
    at com.phoenixcontact.ade.internal.commonremoting.a.i.connect(SourceFile:94)
    at com.phoenixcontact.ade.internal.commonremoting.a.m.a(SourceFile:92)
    at com.phoenixcontact.ade.commonremoting.impl.a.d.connect(SourceFile:200)
    at com.phoenixcontact.arp.system.rsc.ServiceManager.connect(SourceFile:1350)
    at de.verlinked.msb.plugin.bundle.protocol.plcnext.gds.GdsDataAccessService.start(GdsDataAccessService.java:64)
    at de.verlinked.msb.plugin.bundle.protocol.plcnext.gds.GdsProtocolInstance.start(GdsProtocolInstance.java:46)
    at de.verlinked.msb.core.v2core.ProtocolRunner.lambda$start$5(ProtocolRunner.java:272)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at de.verlinked.msb.common.executor.DiscardingSingleThreadExecutor.run(DiscardingSingleThreadExecutor.java:40)
    at java.lang.Thread.run(Thread.java:748)
    Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
    at com.phoenixcontact.arp.system.rsc.b.c.checkServerTrusted(SourceFile:209)
    at org.bouncycastle.jsse.provider.ImportX509TrustManager_5.checkServerTrusted(Unknown Source)
    ... 24 more
    Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
    at org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi.engineValidate(Unknown Source)
    at java.security.cert.CertPathValidator.validate(CertPathValidator.java:292)
    at com.phoenixcontact.arp.system.rsc.b.c.checkServerTrusted(SourceFile:198)

    This makes sense since it seems that your implementation of a X509TrustManager is now not used anymore and therefore the cert can not be resolved. By the way, why did you obfuscate your code (this does not help nor does it prevent from reading your code. It just make things more complicated or what are you trying to hide)?

    So what is the correct way to make the connection to the target trustworthy?

     

     

  • Hi,

    we got the solution. We have initialized the security providers as you did in your code but did not use the system trust store. So "boolean useSystemTrustStore=false" in our case. By this, the internal trust store of the Java RSC API is used and the communication is working. Additionally, our Jetty web server is also started correctly and is able to handle the TLS connections.

    Thanks for your help!

Sign In or Register to comment.