I'm having trouble when I try to connect my local Photon Server with Unity3D WebGL Build, using secure websocket connection. I can establish the connection with websockets (not the secure one) and any other environment other than WebGL builds (Even in Unity's play mode with WebGL configuration, it just won't work when I get a build). I'm guessing that the problem is related with my certificate but I'm not entirely sure. I tried self-signed one, and a real one.
Here is the error:
WebSocket connection to 'wss://localhost:19091' failed: Error in
connection establishment: net::ERR_INSECURE_RESPONSE
I already tried 127.0.0.1 instead of localhost, tried to change the port.
I got my socket code from Photon's website, link is here:
https://www.photonengine.com/sdks#onpremiseunity3d
My Client code(uses Photon's SocketWebTcp class) is like this:
using ExitGames.Client.Photon;
using UnityEngine;
public class HiveClient : IPhotonPeerListener
{
public PhotonPeer PhotonClient { get; set; }
public HiveClient() { }
public void Connect()
{
this.PhotonClient = new PhotonPeer(this, ConnectionProtocol.WebSocketSecure);
this.PhotonClient.SocketImplementationConfig.Add(ConnectionProtocol.WebSocketSecure, typeof(SocketWebTcp));
this.PhotonClient.Connect("wss://localhost:19091", "Hive");
}
public void DebugReturn(DebugLevel level, string message)
{
Debug.Log("DebugReturn: " + level.ToString() + " Message: " + message);
}
public void OnEvent(EventData eventData)
{
Debug.Log("OnEvent: " + eventData.Code);
}
public void OnOperationResponse(OperationResponse operationResponse)
{
Debug.Log("OnOperationResponse: " + operationResponse.DebugMessage);
}
public void OnStatusChanged(StatusCode statusCode)
{
Debug.Log("OnStatusChanged: " + statusCode.ToString());
}}
Here is app's websocket listener in photon server config:
<WebSocketListeners>
<WebSocketListener IPAddress="0.0.0.0" Port="19091" DisableNagle="true" InactivityTimeout="10000" Secure = "true" StoreName = "MY" CertificateName = "DESKTOP-PQ845BC" UseMachineStore = "true">
</WebSocketListener>
</WebSocketListeners>
Lastly, here is my self-signed certificate:
Thank you.
Solved this, It seems certificates doesn't work with ip adresses like 127.0.0.1. Certificate expects some address (like xxx.photon.com)
As a temporary solution, one can send a https request to defined ip and insert the ip chrome's allowed ips. This temp solution relates to this answer:
https://stackoverflow.com/a/43493521/3013806
Related
I'm trying to create a websocket in java that listens to a local application on the endpoint f.ex. "wss://localhost.localapp.com:8080/".
The application do send through that websocket information about what is happening.
When I run the web-application and it tries to connect to the secure websocket it throws this error:
XNIO000100: 'https' URL scheme chosen but no SSL provider given
Here is my code for connecting to the client endpoint:
#ClientEndpoint
public class ClientEndpoint {
private Logger logger = Logger.getLogger(getClass().getName());
public ClientEndpoint() {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
container.connectToServer(this, new URI("wss://localhost.localapp.com:8080/"));
} catch (DeploymentException | URISyntaxException | InterruptedException | IOException e) {
System.out.println("Connection error occured!: " + e);
}
}
#OnOpen
public void onOpen(Session session) {
this.logger.info("New websocket session opened: " + session.getId());
}
#OnClose
public void onClose(Session session) {
this.logger.info("Websoket session closed: " + session.getId());
}
#OnMessage
public void onMessage(Session session, String message) throws IOException, EncodeException {
this.logger.info("Message recieved: " + message);
}
#OnError
public void error(Session session, Throwable t) {
t.printStackTrace();
}
}
So the errors happends on this code line:
container.connectToServer(this, new URI("wss://localhost.localapp.com:8080/"));
And the stack trace output this:
ERROR localhost jboss7.1 [RunnerReadFacade] Application was stopped due to exception. Transaction is rollbacked.: java.lang.IllegalArgumentException: XNIO000100: 'https' URL scheme chosen but no SSL provider given
at org.xnio.http.HttpUpgrade$HttpUpgradeState.doUpgrade(HttpUpgrade.java:253)
at org.xnio.http.HttpUpgrade$HttpUpgradeState.access$100(HttpUpgrade.java:165)
at org.xnio.http.HttpUpgrade.performUpgrade(HttpUpgrade.java:129)
at io.undertow.websockets.client.WebSocketClient$ConnectionBuilder.connectImpl(WebSocketClient.java:323)
at io.undertow.websockets.client.WebSocketClient$ConnectionBuilder.connect(WebSocketClient.java:211)
at io.undertow.websockets.jsr.ServerWebSocketContainer.connectToServerInternal(ServerWebSocketContainer.java:463)
at io.undertow.websockets.jsr.ServerWebSocketContainer.connectToServerInternal(ServerWebSocketContainer.java:457)
at io.undertow.websockets.jsr.ServerWebSocketContainer.connectToServer(ServerWebSocketContainer.java:211)
How should I solve this?
With JavaScript I do not need a SSL provider and can simply create the connection with the websocket just by running:
websocket = new WebSocket("wss://localhost.localapp.com:8080/");
Why does the error only occure to Java?
UPDATE:
This probably cannot be done because the application is on your local computer and the server is not local.
But could this be done if you would use a local server?
You can either switch to Programmatic Client Endpoint or define Thread local SSL Context. See details at https://issues.jboss.org/browse/THORN-2131 (wsstest-master.zip includes demo app). 'Implement io.undertow.websockets.jsr.WebsocketClientSslProvider and add a META-INF/services entry' didn't work for me.
We are using spring integration application for data receiption from gps devices. For current configuration we are able to receive data from device also respose sent back to device through same connection
current configuration is as
#SpringBootApplication
#IntegrationComponentScan
public class SpringIntegrationApplication extends SpringBootServletInitializer{
private Integer TIMEOUT=1000*60*10;
#Value("${TCP_PORT}")
private Integer TCP_PORT;
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext ctx = SpringApplication.run(SpringIntegrationApplication.class, args);
System.in.read();
ctx.close();
}
#Bean
TcpNetServerConnectionFactory cf(){
TcpNetServerConnectionFactory connectionFactory=new TcpNetServerConnectionFactory(TCP_PORT);
connectionFactory.setSerializer(new CustomSerializerDeserializer());
connectionFactory.setDeserializer(new CustomSerializerDeserializer());
connectionFactory.setSoTimeout(TIMEOUT);
return connectionFactory;
}
#Bean
TcpInboundGateway tcpGate(){
TcpInboundGateway gateway=new TcpInboundGateway();
gateway.setConnectionFactory(cf());
gateway.setRequestChannel(requestChannel());
gateway.setRequestTimeout(TIMEOUT);
return gateway;
}
#Bean
public MessageChannel requestChannel(){
return new DirectChannel();
}
}
and message end point
#MessageEndpoint
public class Echo {
#ServiceActivator(inputChannel="requestChannel")
public byte[] echo(byte[] in,#SuppressWarnings("deprecation") #Header("ip_address") String ip){
//here we receive packet data in bytes from gps device
return "".getBytes();//string will contains expected result for device.
}
Above configuartion works fine for one way communication. but we want to implement two way communication. What we want after connection established between server and device we want to send message explicitely.To send command through server we dont know ip and port of device, so how can we send command through server to connected device.
I am trying following solution
created oubound channel adapter
#Bean
public TcpSendingMessageHandler tcpSendingMessageHandler() {
System.out.println("Creating outbound adapter");
TcpSendingMessageHandler outbound = new TcpSendingMessageHandler();
return outbound;
}
then created gateway for explicite message send, this will be called from service where we want to send data explicitely
#MessagingGateway(defaultRequestChannel="toTcp")
public static interface tcpSendService {
public byte [] send(String string);
}
After calling gate way following service activator invoked where we are setting connection ip and port, these ip and ports will be from connection established while receiving data from device
#ServiceActivator(inputChannel="toTcp", outputChannel="fromTcp")
public String send(String in){
System.out.println(new String(in));
TcpNetClientConnectionFactory factory = new TcpNetClientConnectionFactory(ip_extracted_from_inbound_connection, port_extarcted_from_inbound_connection);
factory.start();
tcpSendingMessageHandler.setConnectionFactory(factory);
return in;
}
// for ip and port extraction i am using following service which is inbound sevice
#ServiceActivator(inputChannel="requestChannel")
public byte[] echo(byte[] in,#Header("ip_address") String ip){
System.out.println(new String(in)+ " ; IP : "+ip);
for (String connectionId : factory.getOpenConnectionIds()) {
if(!lastConection.contains(ip))
lastConection = connectionId;
}
return "hello".getBytes();
}
For service activator i am setting new TcpNetClientConnectionFactory every time service called. Ip and port are extracted from TcpNetServerConnectionFactory. whenever device connects with server i am saving its connection ip and port, using these ip and port for data transmission through server but i am getting connection timeout issue.
Kindly help me out and suggest me a solution over it.
Thank you.
Replace the gateway with a pair of Collaborating Outbound and Inbound Channel Adapters.
In order to send arbitrary messages to a connection, you must set the ip_connectionId header.
The challenge, though, is how to direct the reply to the gateway. You would need to capture the replyChannel header from the request and, when a reply is received for that ip_connectionId, set the replyChannel headers.
This will only work, though, if you have only one request/reply outstanding to each device at a time, unless there is some data in the reply that can be used to correlate it to a request.
Another challenge is race conditions, where the device and the server initiate a request at the same time. You would need to look at data in the inbound message to see if it's a request or reply.
I have a Java Key Store where I store certificates for each of my customer's sub-domain. I am planning to use the server alias to differentiate between multiple customers in the key store as suggested here. Play framework 1.2.7 uses Netty's SslHandler to support SSL on the server-side. I tried implementing a custom SslHttpServerContextFactory that uses this solution.
import play.Play;
import javax.net.ssl.*;
import java.io.FileInputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Properties;
public class CustomSslHttpServerContextFactory {
private static final String PROTOCOL = "SSL";
private static final SSLContext SERVER_CONTEXT;
static {
String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
if (algorithm == null) {
algorithm = "SunX509";
}
SSLContext serverContext = null;
KeyStore ks = null;
try {
final Properties p = Play.configuration;
// Try to load it from the keystore
ks = KeyStore.getInstance(p.getProperty("keystore.algorithm", "JKS"));
// Load the file from the conf
char[] certificatePassword = p.getProperty("keystore.password", "secret").toCharArray();
ks.load(new FileInputStream(Play.getFile(p.getProperty("keystore.file", "conf/certificate.jks"))),
certificatePassword);
// Set up key manager factory to use our key store
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(ks, certificatePassword);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(ks);
final X509KeyManager origKm = (X509KeyManager) kmf.getKeyManagers()[0];
X509KeyManager km = new X509KeyManagerWrapper(origKm);
// Initialize the SSLContext to work with our key managers.
serverContext = SSLContext.getInstance(PROTOCOL);
serverContext.init(new KeyManager[]{km}, tmf.getTrustManagers(), null);
} catch (Exception e) {
throw new Error("Failed to initialize the server-side SSLContext", e);
}
SERVER_CONTEXT = serverContext;
}
public static SSLContext getServerContext() {
return SERVER_CONTEXT;
}
public static class X509KeyManagerWrapper implements X509KeyManager {
final X509KeyManager origKm;
public X509KeyManagerWrapper(X509KeyManager origKm) {
this.origKm = origKm;
}
public String chooseServerAlias(String keyType,
Principal[] issuers, Socket socket) {
InetAddress remoteAddress = socket.getInetAddress();
//TODO: Implement alias selection based on remoteAddress
return origKm.chooseServerAlias(keyType, issuers, socket);
}
#Override
public String chooseClientAlias(String[] keyType,
Principal[] issuers, Socket socket) {
return origKm.chooseClientAlias(keyType, issuers, socket);
}
#Override
public String[] getClientAliases(String s, Principal[] principals) {
return origKm.getClientAliases(s, principals);
}
#Override
public String[] getServerAliases(String s, Principal[] principals) {
return origKm.getServerAliases(s, principals);
}
#Override
public X509Certificate[] getCertificateChain(String s) {
return origKm.getCertificateChain(s);
}
#Override
public PrivateKey getPrivateKey(String s) {
return origKm.getPrivateKey(s);
}
}
}
But, this approach did not work for some reason. I get this message in my SSL debug log.
X509KeyManager passed to SSLContext.init(): need an X509ExtendedKeyManager for SSLEngine use
This is the SSL trace, which fails with "no cipher suites in common". Now, I switched the wrapper to:
public static class X509KeyManagerWrapper extends X509ExtendedKeyManager
With this change, I got rid of the warning, but I still see the same error as before "no cipher suites in common" and here is the SSL trace. I am not sure why the delegation of key manager won't work.
Some more information that may be useful in this context.
Netty uses javax.net.ssl.SSLEngine to support SSL in NIO server.
As per the recommendation in this bug report, it is intentional that X509ExtendedKeyManager must be used with an SSLEngine. So, the wrapper must extend X509ExtendedKeyManager.
This is hindering me to move further with the custom alias selection logic in X509KeyManagerWrapper. Any clues on what might be happening here? Is there any other way to implement this in Netty/Play? Appreciate any suggestions.
SSLEngine uses the chooseEngineServerAlias method to pick the certificate to use (in server mode) - not the chooseServerAlias method.
The default chooseEngineServerAlias implementation actually returns null, which is what causes the "no cipher suites in common" message - you need a certificate to know which cipher suites can be used (e.g. ECDSA can only be used for authentication if the certificate has an ECC public key, etc.) There are actually some cipher suites which can be used without a certificate, however, these are typically disabled as they are vulnerable to MITM attacks.
Therefore, you should also override chooseEngineServerAlias, and implement your logic to select the certificate based on the IP address there. As Netty only uses SSLEngine, what chooseServerAlias does doesn't matter - it'll never be called.
Java 8 also has support for server-side SNI, which allows you to use several certificates across many hostnames with a single IP address. Most web browsers support SNI - the notable exceptions are IE running on Windows XP and some old versions of Android, however, usage of these is declining. I have created a small example application demonstrating how to use SNI in Netty on GitHub. The core part of how it works is by overriding chooseEngineServerAlias - which should give you enough hints, even if you want to use the one certificate per IP address technique instead of SNI.
(I posted a similar answer to this on the Netty mailing list, where you also asked this question - however, my post seems to have not yet been approved, so I thought I'd answer here too so you can get an answer sooner.)
How can I test for localhost on an ActionFilterAttribute with ASP.NET Web API? I want to skip the SSL check.
public class RequireHttpsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var request = actionContext.Request;
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
throw new ValidationException(new SecureConnection());
}
base.OnActionExecuting(actionContext);
}
}
If you just want to test whether the request URI is localhost, then IsLoopback on the URI should work fine.
But here's the thing... request URIs can easily be spoofed by computers that aren't the local computer. So any remote computer can actually send you a request with a localhost Host header.
A better way is to use Filip's suggestion in his blog post:
http://www.strathweb.com/2013/01/adding-request-islocal-to-asp-net-web-api/
That should work for both selfhost and webhost and whether the IP address of the client is a loopback.
Looks like this works. Let me know if I am incorrect.
request.RequestUri.IsLoopback
How can I get the IP address of the remote peer in Websocket API for Java Glassfish ?
Another method, based on this answer to another question, is to get the headers of the HandshakeRequest. The headers you are looking for are either of the following.
origin: [IP Address]
x-forwarded-for: [Possibly a separate IP]
Just for clarity, here's my setup, and how I discovered this:
Wamp 2.5 on MyMachine:6060. This hosts a client HTML page.
Wamp 2.5 on LabMachine:6060 (normal connections) and LabMachine:6443 (secure connections). This acts as a proxy.
GlassFish 4.0 on MyMachine:8080 (normal) and MyMachine:8181 (SSL). This is the endpoint.
I connected to the client page via my own machine, the lab machine, and a colleague's machine. In every case, the origin header of the WebSocket request was
http://MyMachine:6060
However, in each case the x-forwarded-host header was different, matching the IP addresses of the actual client.
See getRemoteAddr()
You will need to cast your socket Session instance to a TyrusSession, which implements the standard JSR-356 Session interface.
You might need to upgrade Tyrus, as I am not sure if the version you have supports this method. I am using Tyrus 1.7 and it works fine for me. Here is how to upgrade.
WebSockets are based on HTTP requests. Therefore you are probably extending an HttpServlet or a WebSocketServlet somewhere, so the usual way of getting the IP from the HttpServletRequest should work:
Example:
public class WebSocketsServlet extends HttpServlet {
private final WebSocketApplication app = new WebSocketApplication();
#Override
public void init(ServletConfig config) throws ServletException {
WebSocketEngine.getEngine().register(
config.getServletContext().getContextPath() + "/context", app);
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("IP: " + req.getRemoteAddr());
super.doGet(req, resp);
}
}