View Javadoc
1   package de.dlr.bt.stc.opcuaserver;
2   
3   import java.io.File;
4   import java.nio.file.Files;
5   import java.nio.file.Path;
6   import java.nio.file.Paths;
7   import java.security.KeyPair;
8   import java.security.Security;
9   import java.security.cert.X509Certificate;
10  import java.util.ArrayList;
11  import java.util.LinkedHashSet;
12  import java.util.List;
13  import java.util.Set;
14  import java.util.concurrent.CompletableFuture;
15  import java.util.concurrent.ExecutionException;
16  import java.util.concurrent.TimeUnit;
17  import java.util.concurrent.TimeoutException;
18  
19  import org.bouncycastle.jce.provider.BouncyCastleProvider;
20  import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
21  import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig;
22  import org.eclipse.milo.opcua.sdk.server.identity.CompositeValidator;
23  import org.eclipse.milo.opcua.sdk.server.identity.UsernameIdentityValidator;
24  import org.eclipse.milo.opcua.sdk.server.identity.X509IdentityValidator;
25  import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
26  import org.eclipse.milo.opcua.stack.core.StatusCodes;
27  import org.eclipse.milo.opcua.stack.core.UaRuntimeException;
28  import org.eclipse.milo.opcua.stack.core.security.DefaultCertificateManager;
29  import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager;
30  import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
31  import org.eclipse.milo.opcua.stack.core.transport.TransportProfile;
32  import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
33  import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
34  import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
35  import org.eclipse.milo.opcua.stack.core.types.structured.BuildInfo;
36  import org.eclipse.milo.opcua.stack.core.util.CertificateUtil;
37  import org.eclipse.milo.opcua.stack.core.util.NonceUtil;
38  import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
39  import org.eclipse.milo.opcua.stack.core.util.SelfSignedHttpsCertificateBuilder;
40  import org.eclipse.milo.opcua.stack.server.EndpointConfiguration;
41  import org.greenrobot.eventbus.EventBus;
42  
43  import lombok.extern.slf4j.Slf4j;
44  
45  @Slf4j
46  public class UAServer {
47  	private static final String IP_ANY = "0.0.0.0";
48  
49  	static {
50  		// Required for SecurityPolicy.Aes256_Sha256_RsaPss
51  		Security.addProvider(new BouncyCastleProvider());
52  		try {
53  			NonceUtil.blockUntilSecureRandomSeeded(10, TimeUnit.SECONDS);
54  		} catch (ExecutionException | InterruptedException | TimeoutException e) {
55  			e.printStackTrace();
56  			System.exit(-1);
57  		}
58  	}
59  
60  	private final OpcUaServer server;
61  
62  	public UAServer(int tcpBindPort, EventBus eventBus) throws Exception {
63  
64  		Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "server", "security");
65  		Files.createDirectories(securityTempDir);
66  		if (!Files.exists(securityTempDir)) {
67  			throw new Exception("unable to create security temp dir: " + securityTempDir);
68  		}
69  
70  		File pkiDir = securityTempDir.resolve("pki").toFile();
71  
72  		log.info("security dir: {}", securityTempDir.toAbsolutePath());
73  		log.info("security pki dir: {}", pkiDir.getAbsolutePath());
74  
75  		KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir);
76  
77  		DefaultCertificateManager certificateManager = new DefaultCertificateManager(loader.getServerKeyPair(),
78  				loader.getServerCertificateChain());
79  
80  		DefaultTrustListManager trustListManager = new DefaultTrustListManager(pkiDir);
81  
82  //		DefaultServerCertificateValidator certificateValidator = new DefaultServerCertificateValidator(
83  //				trustListManager);
84  		// TODO: Think of better way to handle certificates
85  		AcceptAllCertificateValidator acceptAllValidator = new AcceptAllCertificateValidator();
86  
87  		KeyPair httpsKeyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
88  
89  		SelfSignedHttpsCertificateBuilder httpsCertificateBuilder = new SelfSignedHttpsCertificateBuilder(httpsKeyPair);
90  		httpsCertificateBuilder.setCommonName(HostnameUtil.getHostname());
91  		HostnameUtil.getHostnames(IP_ANY).forEach(httpsCertificateBuilder::addDnsName);
92  		X509Certificate httpsCertificate = httpsCertificateBuilder.build();
93  
94  		// TODO: Implement User access if necessary
95  		UsernameIdentityValidator identityValidator = new UsernameIdentityValidator(true, authChallenge -> true);
96  
97  		X509IdentityValidator x509IdentityValidator = new X509IdentityValidator(c -> true);
98  
99  		// If you need to use multiple certificates you'll have to be smarter than this.
100 		X509Certificate certificate = certificateManager.getCertificates().stream().findFirst()
101 				.orElseThrow(() -> new UaRuntimeException(StatusCodes.Bad_ConfigurationError, "no certificate found"));
102 
103 		// The configured application URI must match the one in the certificate(s)
104 		String applicationUri = CertificateUtil.getSanUri(certificate)
105 				.orElseThrow(() -> new UaRuntimeException(StatusCodes.Bad_ConfigurationError,
106 						"certificate is missing the application URI"));
107 
108 		Set<EndpointConfiguration> endpointConfigurations = createEndpointConfigurations(certificate, tcpBindPort);
109 
110 		@SuppressWarnings("unchecked")
111 		OpcUaServerConfig serverConfig = OpcUaServerConfig.builder().setApplicationUri(applicationUri)
112 				.setApplicationName(LocalizedText.english("Shepard Timeseries Collector"))
113 				.setEndpoints(endpointConfigurations)
114 				.setBuildInfo(new BuildInfo("urn:dlr:stc:server:opcua", "DLR e.V.", "Shepard Timeseries Connector",
115 						OpcUaServer.SDK_VERSION, "", DateTime.now()))
116 				.setCertificateManager(certificateManager).setTrustListManager(trustListManager)
117 				.setCertificateValidator(acceptAllValidator).setHttpsKeyPair(httpsKeyPair)
118 				.setHttpsCertificateChain(new X509Certificate[] { httpsCertificate })
119 				.setIdentityValidator(new CompositeValidator<>(identityValidator, x509IdentityValidator))
120 				.setProductUri("urn:dlr:stc:server:opcua").build();
121 
122 		server = new OpcUaServer(serverConfig);
123 
124 		STCNamespace stcns = new STCNamespace(server, eventBus);
125 		stcns.startup();
126 	}
127 
128 	private Set<EndpointConfiguration> createEndpointConfigurations(X509Certificate certificate, int bindPort) {
129 		Set<EndpointConfiguration> endpointConfigurations = new LinkedHashSet<>();
130 
131 		List<String> bindAddresses = new ArrayList<>();
132 		bindAddresses.add(IP_ANY);
133 
134 		Set<String> hostnames = new LinkedHashSet<>();
135 		hostnames.add(HostnameUtil.getHostname());
136 		hostnames.addAll(HostnameUtil.getHostnames(IP_ANY));
137 
138 		for (String bindAddress : bindAddresses) {
139 			for (String hostname : hostnames) {
140 				EndpointConfiguration.Builder builder = EndpointConfiguration.newBuilder().setBindAddress(bindAddress)
141 						.setHostname(hostname).setPath("/").setCertificate(certificate)
142 						.addTokenPolicies(OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS);
143 
144 				EndpointConfiguration.Builder noSecurityBuilder = builder.copy().setSecurityPolicy(SecurityPolicy.None)
145 						.setSecurityMode(MessageSecurityMode.None);
146 
147 				endpointConfigurations.add(buildTcpEndpoint(noSecurityBuilder, bindPort));
148 
149 				// TCP Basic256Sha256 / SignAndEncrypt
150 				endpointConfigurations
151 						.add(buildTcpEndpoint(builder.copy().setSecurityPolicy(SecurityPolicy.Basic256Sha256)
152 								.setSecurityMode(MessageSecurityMode.SignAndEncrypt), bindPort));
153 
154 				/*
155 				 * It's good practice to provide a discovery-specific endpoint with no security.
156 				 * It's required practice if all regular endpoints have security configured.
157 				 *
158 				 * Usage of the "/discovery" suffix is defined by OPC UA Part 6:
159 				 *
160 				 * Each OPC UA Server Application implements the Discovery Service Set. If the
161 				 * OPC UA Server requires a different address for this Endpoint it shall create
162 				 * the address by appending the path "/discovery" to its base address.
163 				 */
164 
165 //				EndpointConfiguration.Builder discoveryBuilder = builder.copy().setPath("/discovery")
166 //						.setSecurityPolicy(SecurityPolicy.None).setSecurityMode(MessageSecurityMode.None);
167 //
168 //				endpointConfigurations.add(buildTcpEndpoint(discoveryBuilder));
169 
170 			}
171 		}
172 
173 		return endpointConfigurations;
174 	}
175 
176 	private static EndpointConfiguration buildTcpEndpoint(EndpointConfiguration.Builder base, int bindPort) {
177 		return base.copy().setTransportProfile(TransportProfile.TCP_UASC_UABINARY).setBindPort(bindPort).build();
178 	}
179 
180 	public OpcUaServer getServer() {
181 		return server;
182 	}
183 
184 	public CompletableFuture<OpcUaServer> startup() {
185 		return server.startup();
186 	}
187 
188 	public CompletableFuture<OpcUaServer> shutdown() {
189 		return server.shutdown();
190 	}
191 
192 //	public List<MultikinematikNamespace> getNamespaces() {
193 //		return Collections.unmodifiableList(namespaces);
194 //	}
195 }