use of org.eclipse.milo.opcua.stack.core.security.SecurityPolicy in project milo by eclipse.
the class UascServerAsymmetricHandler method onOpenSecureChannel.
private void onOpenSecureChannel(ChannelHandlerContext ctx, ByteBuf buffer) throws UaException {
// Skip messageType
buffer.skipBytes(3);
char chunkType = (char) buffer.readByte();
if (chunkType == 'A') {
chunkBuffers.forEach(ByteBuf::release);
chunkBuffers.clear();
headerRef.set(null);
} else {
// Skip messageSize
buffer.skipBytes(4);
final long secureChannelId = buffer.readUnsignedIntLE();
final AsymmetricSecurityHeader header = AsymmetricSecurityHeader.decode(buffer, stackServer.getConfig().getEncodingLimits());
if (!headerRef.compareAndSet(null, header)) {
if (!header.equals(headerRef.get())) {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "subsequent AsymmetricSecurityHeader did not match");
}
}
if (secureChannelId != 0) {
if (secureChannel == null) {
throw new UaException(StatusCodes.Bad_TcpSecureChannelUnknown, "unknown secure channel id: " + secureChannelId);
}
if (secureChannelId != secureChannel.getChannelId()) {
throw new UaException(StatusCodes.Bad_TcpSecureChannelUnknown, "unknown secure channel id: " + secureChannelId);
}
}
if (secureChannel == null) {
secureChannel = new ServerSecureChannel();
secureChannel.setChannelId(stackServer.getNextChannelId());
String securityPolicyUri = header.getSecurityPolicyUri();
SecurityPolicy securityPolicy = SecurityPolicy.fromUri(securityPolicyUri);
secureChannel.setSecurityPolicy(securityPolicy);
if (securityPolicy != SecurityPolicy.None) {
secureChannel.setRemoteCertificate(header.getSenderCertificate().bytesOrEmpty());
CertificateValidator certificateValidator = stackServer.getConfig().getCertificateValidator();
certificateValidator.validateCertificateChain(secureChannel.getRemoteCertificateChain());
CertificateManager certificateManager = stackServer.getConfig().getCertificateManager();
Optional<X509Certificate[]> localCertificateChain = certificateManager.getCertificateChain(header.getReceiverThumbprint());
Optional<KeyPair> keyPair = certificateManager.getKeyPair(header.getReceiverThumbprint());
if (localCertificateChain.isPresent() && keyPair.isPresent()) {
X509Certificate[] chain = localCertificateChain.get();
secureChannel.setLocalCertificate(chain[0]);
secureChannel.setLocalCertificateChain(chain);
secureChannel.setKeyPair(keyPair.get());
} else {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "no certificate for provided thumbprint");
}
}
}
int chunkSize = buffer.readerIndex(0).readableBytes();
if (chunkSize > maxChunkSize) {
throw new UaException(StatusCodes.Bad_TcpMessageTooLarge, String.format("max chunk size exceeded (%s)", maxChunkSize));
}
chunkBuffers.add(buffer.retain());
if (maxChunkCount > 0 && chunkBuffers.size() > maxChunkCount) {
throw new UaException(StatusCodes.Bad_TcpMessageTooLarge, String.format("max chunk count exceeded (%s)", maxChunkCount));
}
if (chunkType == 'F') {
final List<ByteBuf> buffersToDecode = chunkBuffers;
chunkBuffers = new ArrayList<>();
headerRef.set(null);
serializationQueue.decode((binaryDecoder, chunkDecoder) -> {
ByteBuf message;
long requestId;
try {
ChunkDecoder.DecodedMessage decodedMessage = chunkDecoder.decodeAsymmetric(secureChannel, buffersToDecode);
message = decodedMessage.getMessage();
requestId = decodedMessage.getRequestId();
} catch (MessageAbortException e) {
logger.warn("Received message abort chunk; error={}, reason={}", e.getStatusCode(), e.getMessage());
return;
} catch (MessageDecodeException e) {
logger.error("Error decoding asymmetric message", e);
ctx.close();
return;
}
try {
OpenSecureChannelRequest request = (OpenSecureChannelRequest) binaryDecoder.setBuffer(message).readMessage(null);
logger.debug("Received OpenSecureChannelRequest ({}, id={}).", request.getRequestType(), secureChannelId);
sendOpenSecureChannelResponse(ctx, requestId, request);
} catch (Throwable t) {
logger.error("Error decoding OpenSecureChannelRequest", t);
ctx.close();
} finally {
message.release();
buffersToDecode.clear();
}
});
}
}
}
use of org.eclipse.milo.opcua.stack.core.security.SecurityPolicy in project milo by eclipse.
the class UascServerAsymmetricHandler method openSecureChannel.
private OpenSecureChannelResponse openSecureChannel(ChannelHandlerContext ctx, OpenSecureChannelRequest request) throws UaException {
SecurityTokenRequestType requestType = request.getRequestType();
if (requestType == SecurityTokenRequestType.Issue) {
secureChannel.setMessageSecurityMode(request.getSecurityMode());
String endpointUrl = ctx.channel().attr(UascServerHelloHandler.ENDPOINT_URL_KEY).get();
EndpointDescription endpoint = stackServer.getEndpointDescriptions().stream().filter(e -> {
boolean transportMatch = Objects.equals(e.getTransportProfileUri(), transportProfile.getUri());
boolean pathMatch = Objects.equals(EndpointUtil.getPath(e.getEndpointUrl()), EndpointUtil.getPath(endpointUrl));
boolean securityPolicyMatch = Objects.equals(e.getSecurityPolicyUri(), secureChannel.getSecurityPolicy().getUri());
boolean securityModeMatch = Objects.equals(e.getSecurityMode(), request.getSecurityMode());
return transportMatch && pathMatch && securityPolicyMatch && securityModeMatch;
}).findFirst().orElseThrow(() -> {
String message = String.format("no matching endpoint found: transportProfile=%s, " + "endpointUrl=%s, securityPolicy=%s, securityMode=%s", transportProfile, endpointUrl, secureChannel.getSecurityPolicy(), request.getSecurityMode());
return new UaException(StatusCodes.Bad_SecurityChecksFailed, message);
});
ctx.channel().attr(ENDPOINT_KEY).set(endpoint);
}
if (requestType == SecurityTokenRequestType.Renew && secureChannel.getMessageSecurityMode() != request.getSecurityMode()) {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "secure channel renewal requested a different MessageSecurityMode.");
}
long channelLifetime = request.getRequestedLifetime().longValue();
channelLifetime = Math.min(channelLifetime, stackServer.getConfig().getMaximumSecureChannelLifetime().longValue());
channelLifetime = Math.max(channelLifetime, stackServer.getConfig().getMinimumSecureChannelLifetime().longValue());
ChannelSecurityToken newToken = new ChannelSecurityToken(uint(secureChannel.getChannelId()), uint(stackServer.getNextTokenId()), DateTime.now(), uint(channelLifetime));
SecurityKeys newKeys = null;
if (secureChannel.isSymmetricSigningEnabled()) {
// Validate the remote nonce; it must be non-null and the correct length for the security algorithm.
ByteString remoteNonce = request.getClientNonce();
NonceUtil.validateNonce(remoteNonce, secureChannel.getSecurityPolicy());
ByteString localNonce = generateNonce(secureChannel.getSecurityPolicy());
secureChannel.setLocalNonce(localNonce);
secureChannel.setRemoteNonce(remoteNonce);
newKeys = ChannelSecurity.generateKeyPair(secureChannel, secureChannel.getRemoteNonce(), secureChannel.getLocalNonce());
}
ChannelSecurity oldSecrets = secureChannel.getChannelSecurity();
SecurityKeys oldKeys = oldSecrets != null ? oldSecrets.getCurrentKeys() : null;
ChannelSecurityToken oldToken = oldSecrets != null ? oldSecrets.getCurrentToken() : null;
ChannelSecurity newSecrets = new ChannelSecurity(newKeys, newToken, oldKeys, oldToken);
secureChannel.setChannelSecurity(newSecrets);
/*
* Cancel the previous timeout, if it exists, and start a new one.
*/
if (secureChannelTimeout == null || secureChannelTimeout.cancel()) {
final long lifetime = channelLifetime;
secureChannelTimeout = Stack.sharedWheelTimer().newTimeout(timeout -> {
logger.debug("SecureChannel renewal timed out after {}ms. id={}, channel={}", lifetime, secureChannel.getChannelId(), ctx.channel());
ctx.close();
}, channelLifetime, TimeUnit.MILLISECONDS);
}
ResponseHeader responseHeader = new ResponseHeader(DateTime.now(), request.getRequestHeader().getRequestHandle(), StatusCode.GOOD, null, null, null);
return new OpenSecureChannelResponse(responseHeader, uint(PROTOCOL_VERSION), newToken, secureChannel.getLocalNonce());
}
use of org.eclipse.milo.opcua.stack.core.security.SecurityPolicy in project milo by eclipse.
the class OpcServerHttpRequestHandler method channelRead0.
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {
String host = httpRequest.headers().get(HttpHeaderNames.HOST);
String uri = httpRequest.uri();
String contentType = httpRequest.headers().get(HttpHeaderNames.CONTENT_TYPE);
String securityPolicyUri = httpRequest.headers().get("OPCUA-SecurityPolicy");
logger.debug("host={} uri={} contentType={} securityPolicy={}", host, uri, contentType, securityPolicyUri);
SecurityPolicy securityPolicy = securityPolicyUri != null ? SecurityPolicy.fromUri(securityPolicyUri) : SecurityPolicy.None;
MessageSecurityMode securityMode = securityPolicy == SecurityPolicy.None ? MessageSecurityMode.None : MessageSecurityMode.Sign;
EndpointDescription endpoint = stackServer.getEndpointDescriptions().stream().filter(e -> {
// TODO use contentType to determine which TransportProfile to match
boolean transportMatch = Objects.equals(e.getTransportProfileUri(), TransportProfile.HTTPS_UABINARY.getUri());
boolean pathMatch = Objects.equals(EndpointUtil.getPath(e.getEndpointUrl()), uri);
boolean securityPolicyMatch = Objects.equals(e.getSecurityPolicyUri(), securityPolicy.getUri());
boolean securityModeMatch = Objects.equals(e.getSecurityMode(), securityMode);
return transportMatch && pathMatch && securityPolicyMatch && securityModeMatch;
}).findFirst().orElseThrow(() -> new UaException(StatusCodes.Bad_TcpEndpointUrlInvalid, "unrecognized endpoint uri: " + uri));
ServerSecureChannel secureChannel = new ServerSecureChannel();
// TODO shared id per endpoint URL / path?
secureChannel.setChannelId(0L);
secureChannel.setSecurityPolicy(securityPolicy);
secureChannel.setMessageSecurityMode(securityMode);
ByteString thumbprint = ByteString.of(DigestUtil.sha1(endpoint.getServerCertificate().bytesOrEmpty()));
Optional<X509Certificate[]> certificateChain = stackServer.getConfig().getCertificateManager().getCertificateChain(thumbprint);
Optional<KeyPair> keyPair = stackServer.getConfig().getCertificateManager().getKeyPair(thumbprint);
certificateChain.ifPresent(chain -> {
secureChannel.setLocalCertificateChain(chain);
secureChannel.setLocalCertificate(chain[0]);
});
keyPair.ifPresent(secureChannel::setKeyPair);
OpcUaBinaryStreamDecoder decoder = new OpcUaBinaryStreamDecoder(stackServer.getSerializationContext());
decoder.setBuffer(httpRequest.content());
try {
UaRequestMessage request = (UaRequestMessage) decoder.readMessage(null);
UInteger requestHandle = request.getRequestHeader().getRequestHandle();
InetSocketAddress remoteSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
ServiceRequest serviceRequest = new ServiceRequest(stackServer, request, endpoint, secureChannel.getChannelId(), remoteSocketAddress.getAddress(), null);
serviceRequest.getFuture().whenComplete((response, fault) -> {
if (response != null) {
sendServiceResponse(ctx, request, response);
} else {
sendServiceFault(ctx, requestHandle, fault);
}
});
stackServer.onServiceRequest(uri, serviceRequest);
} catch (Throwable t) {
logger.error("Error decoding UaRequestMessage", t);
sendServiceFault(ctx, null, t);
}
}
use of org.eclipse.milo.opcua.stack.core.security.SecurityPolicy in project milo by eclipse.
the class AbstractX509IdentityValidator method validateX509Token.
@Override
protected T validateX509Token(Session session, X509IdentityToken token, UserTokenPolicy tokenPolicy, SignatureData tokenSignature) throws UaException {
ByteString clientCertificateBs = token.getCertificateData();
X509Certificate identityCertificate = CertificateUtil.decodeCertificate(clientCertificateBs.bytesOrEmpty());
// verify the algorithm matches the one specified by the tokenPolicy or else the channel itself
if (tokenPolicy.getSecurityPolicyUri() != null) {
SecurityPolicy securityPolicy = SecurityPolicy.fromUri(tokenPolicy.getSecurityPolicyUri());
if (!securityPolicy.getAsymmetricSignatureAlgorithm().getUri().equals(tokenSignature.getAlgorithm())) {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "algorithm in token signature did not match algorithm specified by token policy");
}
} else {
SecurityPolicy securityPolicy = session.getSecurityConfiguration().getSecurityPolicy();
if (!securityPolicy.getAsymmetricSignatureAlgorithm().getUri().equals(tokenSignature.getAlgorithm())) {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "algorithm in token signature did not match algorithm specified by secure channel");
}
}
SecurityAlgorithm algorithm = SecurityAlgorithm.fromUri(tokenSignature.getAlgorithm());
if (algorithm != SecurityAlgorithm.None) {
verifySignature(session, tokenSignature, identityCertificate, algorithm);
}
return authenticateIdentityCertificateOrThrow(session, identityCertificate);
}
use of org.eclipse.milo.opcua.stack.core.security.SecurityPolicy in project milo by eclipse.
the class SessionFsmFactory method createSession.
@SuppressWarnings("Duplicates")
private static CompletableFuture<CreateSessionResponse> createSession(FsmContext<State, Event> ctx, OpcUaClient client) {
UaStackClient stackClient = client.getStackClient();
EndpointDescription endpoint = stackClient.getConfig().getEndpoint();
String gatewayServerUri = endpoint.getServer().getGatewayServerUri();
String serverUri;
if (gatewayServerUri != null && !gatewayServerUri.isEmpty()) {
serverUri = endpoint.getServer().getApplicationUri();
} else {
serverUri = null;
}
ByteString clientNonce = NonceUtil.generateNonce(32);
ByteString clientCertificate = stackClient.getConfig().getCertificate().map(c -> {
try {
return ByteString.of(c.getEncoded());
} catch (CertificateEncodingException e) {
return ByteString.NULL_VALUE;
}
}).orElse(ByteString.NULL_VALUE);
ApplicationDescription clientDescription = new ApplicationDescription(client.getConfig().getApplicationUri(), client.getConfig().getProductUri(), client.getConfig().getApplicationName(), ApplicationType.Client, null, null, null);
CreateSessionRequest request = new CreateSessionRequest(client.newRequestHeader(), clientDescription, serverUri, client.getConfig().getEndpoint().getEndpointUrl(), client.getConfig().getSessionName().get(), clientNonce, clientCertificate, client.getConfig().getSessionTimeout().doubleValue(), client.getConfig().getMaxResponseMessageSize());
LOGGER.debug("[{}] Sending CreateSessionRequest...", ctx.getInstanceId());
return stackClient.sendRequest(request).thenApply(CreateSessionResponse.class::cast).thenCompose(response -> {
try {
SecurityPolicy securityPolicy = SecurityPolicy.fromUri(endpoint.getSecurityPolicyUri());
if (securityPolicy != SecurityPolicy.None) {
if (response.getServerCertificate().isNullOrEmpty()) {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "Certificate missing from CreateSessionResponse");
}
List<X509Certificate> serverCertificateChain = CertificateUtil.decodeCertificates(response.getServerCertificate().bytesOrEmpty());
X509Certificate serverCertificate = serverCertificateChain.get(0);
X509Certificate certificateFromEndpoint = CertificateUtil.decodeCertificate(endpoint.getServerCertificate().bytesOrEmpty());
if (!serverCertificate.equals(certificateFromEndpoint)) {
throw new UaException(StatusCodes.Bad_SecurityChecksFailed, "Certificate from CreateSessionResponse did not " + "match certificate from EndpointDescription!");
}
client.getConfig().getCertificateValidator().validateCertificateChain(serverCertificateChain, endpoint.getServer().getApplicationUri(), EndpointUtil.getHost(endpoint.getEndpointUrl()));
SignatureData serverSignature = response.getServerSignature();
byte[] dataBytes = Bytes.concat(clientCertificate.bytesOrEmpty(), clientNonce.bytesOrEmpty());
byte[] signatureBytes = serverSignature.getSignature().bytesOrEmpty();
SignatureUtil.verify(SecurityAlgorithm.fromUri(serverSignature.getAlgorithm()), serverCertificate, dataBytes, signatureBytes);
}
return completedFuture(response);
} catch (UaException e) {
return failedFuture(e);
}
});
}
Aggregations