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();