SFS2X Docs / GettingStarted / cryptography-old
» Protocol Cryptography
Starting from version 2.10 SmartFoxServer 2X supports connection cryptography which allows to protect all communications with standard TLS protocol.
In this article we're going to discuss the basics of how encrypted traffic works in SmartFoxServer, how to setup an X.509 certificate on your server, how to test your connection and some platform specific notes.
NOTE: SSL for websocket clients is already supported since version 2.9.x, in this article we are discussing only SSL TCP sockets only. For websockets please refer to this article instead.
» How it works, in a nutshell
Adding full blown encryption to the client's session only requires one call after the connection event. The diagram below exemplifies the process:
First, a persistent connection is established with the server. Then the new InitCrypto() method is called to perform the cryptography handshake. If the the request is successful the client and server have correctly agreed on a private key that will be used to encrypt the messages.
From this point all communications are protected by 128 bit symmetric encryption.
» Under the hood
The SmartFox.InitCrypto() call works in conjunction with the embedded Jetty web-server to exchange a unique and secure token over HTTPS. By using an already secure channel for the exchange we can make sure the server is running on a domain with valid SSL certificate, to avoid man-in-the-middle attacks.
The SFSEvent.CRYPTO_INIT is used to signal the response from the server with a boolean value representing the success of the operation.
Once the private key is acquired the rest of the communication will be transparently encrypted by the API, using AES 128 bit cryptography.
» Server setup
1) Enable HTTPS
To activate cryptography in SmartFoxServer 2.10 and higher the first step is activating HTTPS under the ServerSettings > Web server of the AdminTool.
The default HTTPS port is 8443. Whether you keep this value or you customize it, always make sure that you don't have a firewall blocking the communication.
Additionally, if it's a production server, you will need a valid SSL certificate installed in SmartFoxServer.
2) Configure your Zone's encryption settings
Under the AdminTool > ZoneConfigurator choose your working Zone. Under the first tab you will find this:
Turn on the Use Encryption flag and restart the server to activate the changes.
» SSL Certificates
SmartFoxServer 2X is installed by default with a self-signed certificate that can be used to test your games locally. This, however, won't work for a production server. For a live server a signed security certificate is necessary.
» Local testing
The simplest way for testing without incurring in security woes is to disable encryption in the dev environment and just test the encryption on the production server, where it is really required. A simple way to do this is by organizing your connection code as in this example:
public class BasicClientExample implements IEventListener { // Use this to turn Cryptography ON and OFF private boolean useCrypto = false; private SmartFox sfs; public BasicClientExample() { sfs = new SmartFox(); sfs.addEventListener(SFSEvent.CONNECTION, evtListener); sfs.addEventListener(SFSEvent.CRYPTO_INIT, evtListener); sfs.addEventListener(SFSEvent.LOGIN, evtListener); ConfigData cfg = new ConfigData(); cfg.setHost("localhost"); cfg.setZone("BasicExamples"); cfg.setDebug(false); sfs.connect(cfg); } private void sendLogin() { sfs.send(new LoginRequest("", "", sfs.getCurrentZone())); } @Override public void dispatch(BaseEvent event) { if (event.getType().equals(SFSEvent.CONNECTION)) { boolean success = (Boolean) event.getArguments().get("success"); if (success) { if (useCrypto) sfs.initCrypto(); else sendLogin(); } } else if (event.getType().equals(SFSEvent.CRYPTO_INIT)) { boolean success = (Boolean) event.getArguments().get("success"); if (success) sendLogin(); else System.err.println(">>> CRYPTO INIT FAIL: " + event.getArguments().get("errorMsg")); } else if (event.getType().equals(SFSEvent.LOGIN)) { System.out.println("Logged In: " + sfs.getMySelf().getName()); } } }
With the use of a private boolean flag (useCrypto), we can make it very simple to switch the cryptography on and off for the local environment vs production.
» Local testing with a self-signed certificate
In general we don't recommend to test locally with the default self-signed certificate. Since it isn't signed by an authority every runtime (browser, OS, device, etc) will complain in one way or another, and stop your application. There may or may not be ways to force the system to use a non signed certificate but it can be a tricky process.
We highly recommend instead of testing locally without encryption, and testing online with a regular, signed certificate.
If you still want to run local tests with cryptography, we recommend it only if you really understand how the security of your runtime works.
For browser-plugin environments, this is the equivalent of connecting to an HTTPS website with an invalid certificate. The browser will stop the navigation and show a prominent warning. The user is then able to leave or override the warning and continue, ignoring the security risk.
This however will not show up, because the connection is done inside the plugin, so the error may go undetected.
Here are a few tips that will help local testing:
- Web based: when testing with a web-based plugin such as the Flash Player, you can overcome this problem by adding a security Exception to your browser before testing. Point the browser to https://<testHost>:<portNumber> and manually override the exception.
- Java based: with a little extra code it is possible to make the JVM ignore the SSL exception. See the first answer to this question on this StackOverflow discussion.
- C# based: we are not aware of ways to circumvent the security errors, therefore we don't recommend to run local tests with the self-signed certificate. Instead use a valid SSL certificate on one of your domains.
» Online testing with authentic SSL certificate
For production servers a valid SSL certificate is mandatory. You can acquire one from many different providers or use an existing one and import it into SmartFoxServer.
Certificates comes in several different file formats. Typically you will get 2 or 3 files:
- Certificate (.pem)
- Private key (.pem)
- (optional) Intermediary certificate (.pem)
Via an online tool such as this we can create a .pfx (or p12) file which can then be imported into a keystore via the JDK's keytool utility.
keytool -importkeystore -srckeystore cert.pfx -srcstoretype pkcs12 -destkeystore key-store-name.jks -deststoretype JKS
where:
- cert.pxf is the file obtained via the online tool
- pkcs12 specifies the file format of the .pfx/.p12 file
- key-store-name.jks is the name of our final keystore file
The keystore file is the one we're going to deploy in SmartFoxServer 2X, with these steps:
- copy the keystore to SFS2X/lib/jetty/etc;
- edit the jetty/start.d/ssl.ini file and configure the keystore file name and password.
This is an example of the ssl.ini
# # Initialize module ssl # --module=ssl ## SSL Keystore Configuration # define the port to use for secure redirection jetty.secure.port=8443 # Setup a demonstration keystore and truststore jetty.keystore=etc/myCertificate.jks jetty.truststore=etc/myCertificate.jks # Set the demonstration passwords. # Note that OBF passwords are not secure, just protected from casual observation # See http://www.eclipse.org/jetty/documentation/current/configuring-security-secure-passwords.html jetty.keystore.password=myPassword jetty.keymanager.password=myPassword jetty.truststore.password=myPassword
Since this file contains a password that is critical for the security of the system we recommend to make sure it is protected by adequate file permissions. The link in the comments provide additional information on how to obscure the password, if required.
Finally, to verify that the SSL certificate is working correctly, you can point your browser to https//:<your-host>:<ssl-port> and verify that your browser reports a successful connection (e.g. the green lock below).
» Platform specific notes
» Adobe Flash
The Flash IDE will always fail connecting with encryption to a local SmartFoxServer with a self-signed certificate. Similarly does the standalone Flash Player. The only way to test in such setup is to use the browser by pre-authorizing the domain, as explained previously in the article.
» Unity
In Unity the SmartFox.InitCrypto method must be executed as a coroutine, hence using MonoBehaviour.StartCoroutine method. As this is not supported in Windows Store (SDK 8.1, Phone 8.1, Universal 8.1) applications, when building for this platform a conditional statement is required as in the following example:
// Initialize encrypted connection #if UNITY_WINRT && !UNITY_EDITOR sfs.InitCrypto(); #else StartCoroutine(sfs.InitCrypto()); #endif
For Windows Store IL2CPP builds, change the conditional compilation above as described in this post.
» Unity WebPlayer
Do not use Socket.PrefetchPolicy in your code.
It forces to use an IP address instead of a domain name, which is needed if you want to use SSL, since the certificate is (typically) bound to the domain name.
Instead you can let the WebPlayer auto-fetch the cross-domain policy from the default TCP port 843. In order to do this you will need to add a listener for such port in the SFS2X AdminTool > Server Configurator.
For Linux / Mac OS X users, this requires running the server as root, since port 843 is within the 0-1024 range.
This method is not available when building for WebGL: use WSS connection instead.
» Unity WebGL
The SmartFox.InitCrypto method is not available when building for WebGL: use WSS connection instead (see SmartFox(UseWebSocket useWebSocket) constructor API documentation).