package com.mes.milo.utils; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.stack.client.security.DefaultClientCertificateValidator; import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.*; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.regex.Pattern; @Slf4j 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 X509Certificate clientCertificate; private X509Certificate[] clientCertificateChain; private KeyPair clientKeyPair; private DefaultClientCertificateValidator certificateValidator; public KeyStoreLoader load() throws Exception { Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security"); Files.createDirectories(securityTempDir); if (!Files.exists(securityTempDir)) { throw new Exception("unable to create security dir: " + securityTempDir); } File pkiDir = securityTempDir.resolve("pki").toFile(); log.info("security temp dir: {}", securityTempDir.toAbsolutePath()); KeyStore keyStore = KeyStore.getInstance("PKCS12"); Path serverKeyStore = securityTempDir.resolve("milo-client.pfx"); DefaultTrustListManager trustListManager = new DefaultTrustListManager(pkiDir); certificateValidator = new DefaultClientCertificateValidator(trustListManager); log.info("Loading KeyStore at {}", serverKeyStore); if (!Files.exists(serverKeyStore)) { keyStore.load(null, PASSWORD); KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048); SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair) .setCommonName("Milo Client") .setOrganization("kangaroohy") .setOrganizationalUnit("dev") .setLocalityName("Folsom") .setStateName("CA") .setCountryCode("US") .setApplicationUri("urn:kangaroohy:milo: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 : CustomUtil.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 clientPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD); if (clientPrivateKey instanceof PrivateKey) { clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS); clientCertificateChain = Arrays.stream(keyStore.getCertificateChain(CLIENT_ALIAS)) .map(X509Certificate.class::cast) .toArray(X509Certificate[]::new); PublicKey clientPublicKey = clientCertificate.getPublicKey(); clientKeyPair = new KeyPair(clientPublicKey, (PrivateKey) clientPrivateKey); } return this; } public X509Certificate getClientCertificate() { return clientCertificate; } public X509Certificate[] getClientCertificateChain() { return clientCertificateChain; } public DefaultClientCertificateValidator getCertificateValidator() { return certificateValidator; } public KeyPair getClientKeyPair() { return clientKeyPair; } }