Reload Netty Server's SSL Context for gRPC - ssl

Can someone tell me how to reload the SSLContext when a server certificate it refreshed/renewed without restarting the gRPC server?
I have this code to build and start a gRPC server.
The method certificateRefreshed() gets called whenever a certificate changes which is when I create a new SSL context, but this doesn't work unless I restart the grpc server.
public class ServerWithTls {
Server server;
SslContext sslContext;
public ServerWithTls() {
this.sslContext = getSslContext();
NettyServerBuilder serverBuilder = NettyServerBuilder
.forPort(settings.port())
.executor(executorService)
.addService(myService);
.sslContext(this.sslContext);
server = serverBuilder.build();
server.start();
}
public io.netty.handler.ssl.SslContext getSslContext() {
// returns ssl context based on cert and key
}
// gets notified when a server cert changes
public void certificateRefreshed() {
// create a new SSL context when cert changes
this.sslContext = getSslContext();
}
}

I'm unsure if there are easier alternatives, but I see two potentially possible ways.
Make your own SslContext, mimicking DelegatingSslContext. You would swap to a different SslContext (especially during newEngine) when you want a different certificate.
Use a KeyManagerFactory whose key material can change over time. I'm not aware of a pre-existing implementation of such a factory, so you probably would need to implement a KeyManagerFactorySpi that delegates to a KeyManagerFactory. You could then swap out the KeyManagerFactory over time.
I will warn that it would have been easy for me to miss something that would invalidate the approaches.

This question is similar to this question: Reloading a java.net.http.HttpClient's SSLContext
This option is unfortunately not available by default. After you have supplied the SSLContext to the Server and build the Server you cannot change the SSLContext. You will need to create a new SSLContext and a new Server.
I had the same challenge for one of my projects and I solved it by using a custom trustmanager and keymanager which wraps around the actual trustmanager and keymanager while having the capability of swapping the actual trustmanager and trustmanager. So you can use the following setup if you still want to accomplish it without the need of recreating the Server and SSLContext:
SSLFactory baseSslFactory = SSLFactory.builder()
.withDummyIdentityMaterial()
.withDummyTrustMaterial()
.withSwappableIdentityMaterial()
.withSwappableTrustMaterial()
.build();
Runnable sslUpdater = () -> {
SSLFactory updatedSslFactory = SSLFactory.builder()
.withIdentityMaterial(Paths.get("/path/to/your/identity.jks"), "password".toCharArray())
.withTrustMaterial(Paths.get("/path/to/your/truststore.jks"), "password".toCharArray())
.build();
SSLFactoryUtils.reload(baseSslFactory, updatedSslFactory);
};
// initial update of ssl material to replace the dummies
sslUpdater.run();
// update ssl material every hour
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(sslUpdater, 1, 1, TimeUnit.HOURS);
SslContext sslContext = NettySslUtils.forServer(sslFactory).build();
Server server = NettyServerBuilder
.forPort(8443)
.executor(executorService)
.addService(myService)
.sslContext(sslContext)
.build();
server.start();
See here for the documentation of this option: Reloading ssl at runtime
And here for an actual working example with Jetty (similar to Netty): Example swapping certificates at runtime with Jetty Server
You can add the library to your project with:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart-for-netty</artifactId>
<version>7.4.4</version>
</dependency>
You can view the full documentation and other examples here: GitHub - SSLContext Kickstart
By the way I need to add a small disclaimer I am the maintainer of the library.

Related

Trust anchor for certification path not found Hue Certificate

I would like to ask you something related with Philips Hue Smart Lamps integration with Kotlin.
I am working with Hue API v2 and after follow all the steps I was able to connect my mobile with Hue bridge and send orders to lights without problems, at least for me.
After generate the PROD application, some teammates have found problems during the bridge linking, they are receiving the error:
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
This error happen when Android can’t find the trusted certificated to make the bridge call (This call attacks an IP with HTTPS, so make sense the error), however, I am adding the expected certificated into the network_security_config file (Hue certificate is .crt file) and as I said, this works for me and I tested it on 6 different devices.
The point here is, how is possible that some user have problems to generate the trusted certificate and other don’t have any problems? I checked mobile specs for each use case and I couldn’t find a pattern, OS version isn’t related (mobiles with the same OS works and fails), model is not representative either...
My only option about this... could this be related with user's Wifi? Because when an user said that it is not working, the user can’t do the flow in any device, all of them fails, but, when it works (like my case) I can install and do the flow in all my devices.
I tried to obtain the Hue certificate in differents ways, first time I did this:
`
private fun createCertificate(trustedCertificateIS: InputStream): SSLContext? {
val cf = CertificateFactory.getInstance("X.509")
val ca: Certificate = trustedCertificateIS.use { trustedCertificateIS ->
cf.generateCertificate(trustedCertificateIS)
}
// creating a KeyStore containing our trusted CAs
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType)
keyStore.load(null, null)
keyStore.setCertificateEntry("ca", ca)
// creating a TrustManager that trusts the CAs in our KeyStore
val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val tmf = TrustManagerFactory.getInstance(tmfAlgorithm)
tmf.init(keyStore)
// creating an SSLSocketFactory that uses our TrustManager
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, tmf.trustManagers, null)
return sslContext
}
`
This works for me without problems, the app is able to find the Hue file and use it to generate the SSLContext, but, this is not working for some users.
In order to find another solution, I created a network_security_config file, same result.
`
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="#raw/huecert"/>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
`
At this point, I am not sure what else can I try... so, if you have any feedback or idea about this I would really appreciate it.
Thanks!

Teiid 2-way SSL- connection successful even without the Truststore

I have a teiid embedded server and I am trying to connect to a vds on that server through 2-way SSL from my remote client by passing the teiid SSL properties in SystemProperties as per the teiid documentation at: http://teiid.github.io/teiid-documents/12.3.x/content/client-dev/SSL_Client_Connections.html
The connection is successful even without the truststore which is a mandatory property.
Code snippet to replicate this issue:
Properties properties = new Properties();
properties.put("user", "admin");
properties.put("password", "admin");
System.setProperty("org.teiid.ssl.keyStore", "C:/truststore.p12");
System.setProperty("org.teiid.ssl.keyStorePassword", "testssl");
System.setProperty("org.teiid.ssl.trustAll", "false");
DriverManager.registerDriver(new TeiidDriver());
Connection connection1 = DriverManager.getConnection("jdbc:teiid:testvds#mms://localhost:32750", properties);
if (connection.isValid(1000))
{
System.out.println("Connection success");
}
In this case it should have failed. Can you please let me know if this is an issue or I am missing something on my end.
Thanks,
Megha
Can you elaborate on the server side settings? As the other user is getting at, if the server key is already trusted by the default java trust store you don't need additional client settings.

How do I get FiddlerCore programmatic Certificate Installation to 'stick'?

I'm using FiddlerCore to capture HTTP requests. Everything is working including SSL Captures as long as the Fiddler certificate is manually installed. I've been using manual installation through Fiddler's Options menu and that works fine.
However, if I use the FiddlerCore provided CertMaker class static methods to add the Fiddler certificate I find that I can use the certificate added to the cert root only in the current session. As soon as I shut down the application and start back up, CertMaker.rootCertExists() returns false.
I use the following code to install the certificate for the current user (from an explicit menu option at this point):
public static bool InstallCertificate()
{
if (!CertMaker.rootCertExists())
{
if (!CertMaker.createRootCert())
return false;
if (!CertMaker.trustRootCert())
return false;
}
return true;
}
The cert gets installed and I see it in the root cert store for the current user. If I capture SSL requests in the currently running application it works fine.
However, if I shut down the running exe, restart and call CertMaker.certRootExists() it returns false and if I try to capture SSL requests the SSL connection fails in the browser. If I recreate the cert and then re-run the requests in the browser while the app stays running it works again. I now end up with two certificates in the root store.
After exiting and relaunching certMaker.certRootExists() again returns false. Only way to get it to work is to register the cert - per exe session.
What am I doing wrong to cause the installation to not stick between execution of the same application?
I was able to solve this problem and create persistent certificates that are usable across EXE sessions, by removing the default CertMaker.dll and BcMakeCert.dll assemblies that FiddlerCore installs and using and distributing the makecert.exe executable instead.
makecert.exe appears to create certificates in such a way that they are usable across multiple runs of the an application, where the included assemblies are valid only for the current application's running session.
Update:
If you want to use the CertMaker.dll and BcMakeCert.dll that FiddlerCore installs by default, you have to effectively cache and set the certificate and private key, using Fiddlers internal preferences object. There are a couple of keys that hold the certificate after it's been created and you need to capture these values, and write them into some sort of configuration storage.
In the following example I have a static configuration object that holds the certificate and key (persisted to a config file when the app shuts down):
public static bool InstallCertificate()
{
if (!CertMaker.rootCertExists())
{
if (!CertMaker.createRootCert())
return false;
if (!CertMaker.trustRootCert())
return false;
// persist Fiddlers certificate into app specific config
App.Configuration.UrlCapture.Cert =
FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.bc.cert", null);
App.Configuration.UrlCapture.Key =
FiddlerApplication.Prefs.GetStringPref("fiddler.certmaker.bc.key", null);
}
return true;
}
public static bool UninstallCertificate()
{
if (CertMaker.rootCertExists())
{
if (!CertMaker.removeFiddlerGeneratedCerts(true))
return false;
}
// persist Fiddlers certificate into app specific config
App.Configuration.UrlCapture.Cert = null;
App.Configuration.UrlCapture.Key = null;
return true;
}
After installing a certificate this code captures the certificate and private key into the configuration object which persists that value later. For uninstallation, the values are cleared.
At the beginning of the application or the beginning of the capture process, prior to calling CertMaker.rootCertExists() the keys are set from the configuration values. I do this at the beginning of my capture form:
public FiddlerCapture()
{
InitializeComponent();
// read previously saved Fiddler certificate from app specific config
if (!string.IsNullOrEmpty(App.Configuration.UrlCapture.Cert))
{
FiddlerApplication.Prefs.SetStringPref("fiddler.certmaker.bc.key",
App.Configuration.UrlCapture.Key);
FiddlerApplication.Prefs.SetStringPref("fiddler.certmaker.bc.cert",
App.Configuration.UrlCapture.Cert);
}
}
Using this mechanism for saving and then setting the capture settings makes the certificates persist across multiple EXE sessions when using CertMaker.dll.
More detailed info is available this detailed blog post on FiddlerCore.
If anyone is still interested, I found an easier solution based on the demo that Fiddler provides. This demo simply calls CertMaker.trustRootCert(), and strangely enough, it sticks! The first time it will ask whether you want to install the certificate, but after that, the function just returns true and will not cause the pop-up to show.
Unlike your and mine original program, the certificate sticks without having to go to the trouble of letting it stick yourself, so I analysed the differences with the demo. One of the differences I noticed was that the demo didn't have a reference to CertMaker.dll and BCMakeCert.dll. After removing these references from my own solution, I got the same behaviour as the demo.
Unfortunately, I don't have an explanation to why this works, but I hope this still helps some people.

EJB lookup problem

I have a Glassfish v2.1.1 cluster setup. I deployed an EAR file consisting a single stateless bean to stand alone server. It has an IIOP port 3752.
My client application which will be communicating with this bean is deployed on cluster. When i lookup bean's name, i get NameNotFoundException. Code looks as below:
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
if (logger.isDebugEnabled()) {
logger.debug("Looking for bean from location : " + PropertiesService.instance().getSchedulerOrbHost() + ":"
+ PropertiesService.instance().getSchedulerOrbPort());
}
props.setProperty("org.omg.CORBA.ORBInitialHost", PropertiesService.instance().getSchedulerOrbHost());
props.setProperty("org.omg.CORBA.ORBInitialPort", PropertiesService.instance().getSchedulerOrbPort());
InitialContext context = null;
try {
context = new InitialContext(props);
} catch (NamingException e) {
e.printStackTrace();
}
String beanName = "test.OperationControllerRemote";
OperationControllerRemote remote = (OperationControllerRemote) context.lookup(beanName);
Note that i checked JNDI tree and name "test.OperationControllerRemote" is there.
Any opinions please?
Here are the ways I have got it to work with a GF 2.1.1 cluster and a Swing client. I'm currently going with the Standalone option because of client launch speed, but the ACC might work for you.
Standalone
The way you're doing it is considered standalone.
http://glassfish.java.net/javaee5/ejb/EJB_FAQ.html#StandaloneRemoteEJB
http://blogs.oracle.com/dadelhardt/entry/standalone_iiop_clients_with_glassfish
ACC
Another way to approach this is to launch the client with the ACC. This means packaging the client code into the ear as an Application Client and either launching using the JNLP method or manually installing a bundled ACC (mini glassfish really) on client machines. In GF 2.1, either way works ok, but both are pretty fat and JNLP method can make startup times a bit longer. Supposedly in GF 3.1 they've reworked the ACC and it starts up faster. Something that may not be obvious is that with the ACC you get the list of servers in the cluster provided automatically at client startup.
http://blogs.oracle.com/theaquarium/entry/java_ee_clients_with_or
http://download.oracle.com/docs/cd/E18930_01/html/821-2418/beakv.html#scrolltoc
http://download.oracle.com/docs/cd/E18930_01/html/821-2418/gkusn.html
Lookups
Either of the above ways provides RMI/CORBA failover and load balancing for the client.
Either way, when you have the right dependencies on your classpath and the com.sun.appserv.iiop.endpoints system property set (like node1:33700,node2:33701), you'll only need the no-args InitialContext because Glassfish's stuff autoregisters their connection properties, etc as described in the first link:
new InitialContext()
And lookups will work. For my remote session beans (EJB 3.0) I typically do it like:
#Stateless(mappedName="FooBean")
public class FooBean implements FooBeanRemote {}
#Remote
public interface FooBeanRemote {}
then in client code:
FooBeanRemote foo = (FooBeanRemote) ctx.lookup("FooBean");

How to set up SSL on an embedded Jetty?

I've got jetty 7.x embedded. Basically just creating a SelectChannelConnector to listen on port 80 and WebAppContext to deploy a single WAR directory.
I need to add SSL now (all the keystore stuff is done), and I would have guessed to just add an SslSelectChannelConnector, but all the methods are deprecated without any javadocs to explain why, and what to do instead. And the Jetty/SSL docs only show some XML without describing what to do with it.
Can anyone get me the entry point here to setting up SSL an an embedded instance of Jetty? I don't think this will be complicated, I just don't know what class to start with in the current release.
A response from the Jetty Users Email Group:
David,
You need to create an instance of
SslContextFactory and configure it
with your keystore parameters. After
that you'll need to pass that instance
to the SslSelectChannelConnector's
constructor. Recently modified
configuration file jetty-ssl.xml shows
how it is done in XmlConfiguration,
and could be easily translated into
code. This will be documented in Jetty
Wiki as soon as we get a chance.
-Michael
I've been using this and it works just fine for me thus far:
//Set up SSL keystore
SslContextFactory sslContextFactory = new SslContextFactory("/etc/mykeystore");
sslContextFactory.setKeyStorePassword("yourpassword");
SslSelectChannelConnector selectChannelConnector = new SslSelectChannelConnector(sslContextFactory);
selectChannelConnector.setPort(4567); //your port