You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by jf...@apache.org on 2020/03/03 20:55:53 UTC
[plc4x] 02/02: Initial Support for Certs.
This is an automated email from the ASF dual-hosted git repository.
jfeinauer pushed a commit to branch feature/PLC4X-185-cert-support-opc-ua
in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit 2423f36ac9b9f729eac76a7d0cea0c0c1e1208bb
Author: Julian Feinauer <j....@pragmaticminds.de>
AuthorDate: Tue Mar 3 21:55:35 2020 +0100
Initial Support for Certs.
---
.../java/opcua/connection/KeyStoreLoader.java | 100 +++++++++++++++++++++
.../opcua/connection/OpcuaTcpPlcConnection.java | 31 +++++++
.../apache/plc4x/java/opcua/ManualPLC4XOpcua.java | 3 +-
3 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/KeyStoreLoader.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/KeyStoreLoader.java
new file mode 100644
index 0000000..821f58d
--- /dev/null
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/KeyStoreLoader.java
@@ -0,0 +1,100 @@
+package org.apache.plc4x.java.opcua.connection;
+
+import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
+import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder;
+import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.regex.Pattern;
+
+/**
+ * Copied from Eclipse Milo Project:
+ * https://github.com/eclipse/milo/blob/master/milo-examples/server-examples/src/main/java/org/eclipse/milo/examples/server/KeyStoreLoader.java
+ */
+public class KeyStoreLoader {
+
+ private static final Pattern IP_ADDR_PATTERN = Pattern.compile(
+ "^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
+
+ private static final String CLIENT_ALIAS = "client-ai";
+ private static final char[] PASSWORD = "password".toCharArray();
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private X509Certificate clientCertificate;
+ private KeyPair clientKeyPair;
+
+ KeyStoreLoader load(Path baseDir) throws Exception {
+ KeyStore keyStore = KeyStore.getInstance("PKCS12");
+
+ Path serverKeyStore = baseDir.resolve("example-client.pfx");
+
+ logger.info("Loading KeyStore at {}", serverKeyStore);
+
+ if (!Files.exists(serverKeyStore)) {
+ keyStore.load(null, PASSWORD);
+
+ KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
+
+ SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair)
+ .setCommonName("Eclipse Milo Example Client")
+ .setOrganization("digitalpetri")
+ .setOrganizationalUnit("dev")
+ .setLocalityName("Folsom")
+ .setStateName("CA")
+ .setCountryCode("US")
+ .setApplicationUri("urn:eclipse:milo:examples:client")
+ .addDnsName("localhost")
+ .addIpAddress("127.0.0.1");
+
+ // Get as many hostnames and IP addresses as we can listed in the certificate.
+ for (String hostname : HostnameUtil.getHostnames("0.0.0.0")) {
+ if (IP_ADDR_PATTERN.matcher(hostname).matches()) {
+ builder.addIpAddress(hostname);
+ } else {
+ builder.addDnsName(hostname);
+ }
+ }
+
+ X509Certificate certificate = builder.build();
+
+ keyStore.setKeyEntry(CLIENT_ALIAS, keyPair.getPrivate(), PASSWORD, new X509Certificate[]{certificate});
+ try (OutputStream out = Files.newOutputStream(serverKeyStore)) {
+ keyStore.store(out, PASSWORD);
+ }
+ } else {
+ try (InputStream in = Files.newInputStream(serverKeyStore)) {
+ keyStore.load(in, PASSWORD);
+ }
+ }
+
+ Key serverPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD);
+ if (serverPrivateKey instanceof PrivateKey) {
+ clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS);
+ PublicKey serverPublicKey = clientCertificate.getPublicKey();
+ clientKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey);
+ }
+
+ return this;
+ }
+
+ X509Certificate getClientCertificate() {
+ return clientCertificate;
+ }
+
+ KeyPair getClientKeyPair() {
+ return clientKeyPair;
+ }
+
+}
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java
index 75d77e0..1e9c603 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java
@@ -52,8 +52,12 @@ import org.eclipse.milo.opcua.stack.core.types.structured.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -194,6 +198,32 @@ public class OpcuaTcpPlcConnection extends BaseOpcuaPlcConnection {
.findFirst()
.orElseThrow(() -> new PlcConnectionException("No desired endpoints from"));
+ // Security
+ Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
+ try {
+ Files.createDirectories(securityTempDir);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if (!Files.exists(securityTempDir)) {
+ throw new PlcConnectionException("unable to create security dir: " + securityTempDir);
+ }
+ LoggerFactory.getLogger(getClass())
+ .info("security temp dir: {}", securityTempDir.toAbsolutePath());
+
+ KeyStoreLoader loader;
+ try {
+ loader = new KeyStoreLoader().load(securityTempDir);
+ } catch (Exception e) {
+ throw new PlcConnectionException("Unable to load Security!", e);
+ }
+
+ SecurityPolicy securityPolicy = SecurityPolicy.None;
+
+ logger.info("Using endpoint: {} [{}/{}]",
+ endpoint.getEndpointUrl(), securityPolicy, endpoint.getSecurityMode());
+ // End Security
+
if (this.skipDiscovery) {
//ApplicationDescription applicationDescription = new ApplicationDescription();
//endpoint = new EndpointDescription(address.getHostAddress(),applicationDescription , null, MessageSecurityMode.None, SecurityPolicy.None.getUri(), null , TransportProfile.TCP_UASC_UABINARY.getUri(), UByte.valueOf(0));// TODO hier machen wenn fertig
@@ -242,6 +272,7 @@ public class OpcuaTcpPlcConnection extends BaseOpcuaPlcConnection {
OpcUaClientConfig config = OpcUaClientConfig.builder()
.setApplicationName(LocalizedText.english("eclipse milo opc-ua client of the apache PLC4X:PLC4J project"))
.setApplicationUri("urn:eclipse:milo:plc4x:client")
+ .setCertificate(loader.getClientCertificate())
.setEndpoint(endpoint)
.setIdentityProvider(getIdentityProvider())
.setRequestTimeout(UInteger.valueOf(requestTimeout))
diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualPLC4XOpcua.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualPLC4XOpcua.java
index 89146db..57410cd 100644
--- a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualPLC4XOpcua.java
+++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualPLC4XOpcua.java
@@ -73,7 +73,8 @@ public class ManualPLC4XOpcua {
PlcField field = fieldH.createField("ns=2;i=10855");
try {
opcuaConnection = (OpcuaTcpPlcConnection)
- new PlcDriverManager().getConnection("opcua:tcp://localhost:4843?discovery=true");
+ // new PlcDriverManager().getConnection("opcua:tcp://localhost:4843?discovery=true");
+ new PlcDriverManager().getConnection("opcua:tcp://opcuaserver.com:48010");//?discovery=true");
} catch (PlcConnectionException e) {
e.printStackTrace();