How should I go about authenticating an application via HTTP? - java

I'm making an application that requires it to be authenticated on startup with a "secret key" as defined in a configuration file. I can't have the app call to a database so I'm thinking I need to do a HTTP request, however I am not sure how I should go about doing this and how it can't be exploited.
I just need somewhere to start, thanks!

You can use SSL. Then to manage trusted certs, you can implement your own javax.net.ssl.X509TrustManager. You can use the javax.net.ssl.X509TrustManager to manage your .keystore file via standard Java mechanisms. The keystore will contain private keys.
From JavaDoc:
Instance of this interface manage which X509 certificates may be used to authenticate the >remote side of a secure socket. Decisions may be based on trusted certificate authorities, >certificate revocation lists, online status checking or other means.
Also note that:
TrustManagers are responsible for managing the trust material that is used when making trust
decisions, and for deciding whether credentials presented by a peer should be accepted.
If you extend the X509TrustManager, you can read in certificates your private .keystore file as well as certificates trusted by Java. Those live in JAVA_HOME\lib\security\cacerts

Related

Configure additional truststore in Spring Boot Tomcat Server

I have a spring boot client application and a server application. I am implementing MTLS client authentication part. I have a client certificate that is self signed and this needs to be added to a custom truststore in the server. Basically I am looking for a mechanism to add my custom truststore at runtime.
I want the default truststore and the custom truststore to be picked up. I implemented this using TomcatServletWebServerFactory but I did not have any luck. Can someone please help. I am looking for a programmatic solution.
I do not want to change the default java truststore. I still need that.
Since you do not want to replace the existing truststore with your custom one (that could be done by running the JVM with a few system properties) you need to add code that trusts a peer if the builtin or a custom trusttore approve the client.
For this you need to create and install your own Trustmanager. See an example here: https://community.oracle.com/tech/developers/discussion/1535342/java-ssl-trustmanager
If the checkClientTrusted and checkServerTrusted methods do not raise an exception the certificate is approved. Leaving the methods empty would even turn off any certificate validation - not to be recommended.
In order to establish a SSL connection with client certificates between a Spring client application and a Spring server application, there are a few things to note:
Involved Trust Stores
There are two very different trust stores relevant in this context:
The trust store used by the client to determine whether it can trust the server's SSL certificate. When using the java.net package for client requests, the trust store is configured via the JVM property "javax.net.ssl.trustStore" - see e.g. https://docs.oracle.com/javadb/10.8.3.0/adminguide/cadminsslclient.html
The trust store used by Tomcat (the underlying servlet container used by Spring Boot in its default configuration) to determine whether it shall trust a client. This is configured via the spring application property "server.ssl.trust-store" - see https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.server.server.ssl.trust-store
In the context of a Spring server application the first one is relevant for connections which the server makes to a second server - so only when the server acts as client as well - e.g. when acting as a proxy server.
The second trust store is relevant for connections between the server and its clients. This is the one you need to deal with.
Certificates in the Tomcat Trust Store
The trust store which is used by Tomcat does NOT contain the client certificates. Instead, it must contain the certificate of the Certificate Authority who has signed the client certificate. So when using self-signed certificates, you need to install the CA certificate which you have generated on your own.
The big advantage of this is, that you do not need to change the server trust store, when you grant access to new clients.
The downside is, that you have to handle a certificate revocation. The CRL (certificate revocation list) is also stored in the trust store. See:
CRL Explained: What Is a Certificate Revocation List?
Merging the Default Trust Store and the Custom Trust Store
As now should be clear, this is not a good idea and would constitute a major security flaw, as it would allow any client which has a certificate signed by a CA in the default trust store (which can be easily obtained) to connect to the server. The server trust store must only contain those certificates which you use to sign your client certificates!
And there is no need to merge the two, as this are totally separate trust stores with totally different functions.
But, of course it is technically feasible to merge trust stores. This can all be done by keytool - see below.
Managing Trust Stores
Adding, deleting, exporting, listing, and printing certificates and CRLs can all be done via the Oracle command line tool "keytool", which is part of the OpenJDK. See
keytool Reference

Creating an ephemeral SSL / TLS socket using a fresh key pair

Every tutorial I have found explains how to create an SSL/TLS connection with SSLSockets using a keystore file and truststore to store an SSL certificate after creating it manually with the keytool utility.
I would love to know how to do the same thing but with a public key/certificate generated on the fly (ephemeral).
It's so easy to create an RSA/EC key pair in Java so why can't we just use it for an SSLSocket as well? This has two big advantages in my opinion: it allows for forward secrecy and at the same time I don't have to deal with passwords, utilities, file readers and keystore converters for Android.
This has two big advantages in my opinion: it allows for forward secrecy
Forward secrecy typically deals with the key agreement like ECDHE or DHE (though the latter is now widely deprecated due to logjam).
It is good practice to re-key certificates from time to time, though a new one for every TLS session is not necessary.
It's so easy to create an RSA/EC key pair in Java so why can't we just use it for an SSLSocket as well?
You need an X509 certificate too, not a just a key. You can still make those on the fly, but you are going to have to sign the certificate. Either the certificate will be self signed, or you need a certificate authority that can sign certificates very quickly, on demand. Practically, the latter doesn't exist and would mean very slow handshakes.
If the certificate is self-signed, browsers or any other user agent will not know how to trust the certificate, and using a self signed certificates severely impacts the use of TLS.
As a technically correct but fairly useless alternative:
TLS actually defines 'anonymous' keyexchange methods DH_anon and ECDH_anon (and ciphersuites using them) which do forward-secret key agreement using ephemeral keypairs, and NO authentication, thus not requiring any certificate or keystore on the server or (potentially) truststore on the client.
Being unauthenticated, they are easily vulnerable to active attack, and thus people (and authorities) who actually want security consider them unacceptable for use. Java (JSSE) does implement them, but does not enable them by default; your code must call .setEnabledCiphers with an appropriately modified or set list. Most other SSL/TLS implementations either similarly disable them or don't implement them at all.
But technically that is a valid way to do TLS with no long-term keys and certs, and thus no manual effort for adminstering them.
As an aside, I believe this is why creating an (SSLContext and) SSLSocketFactory with (KeyManager for) a keystore not containg a valid privateKeyEntry does not give an error, because in principle it could create sockets (or engines) used for anonymous connections. Instead, since in practice anonymous keyexchange is never used, these factories cause all subsequent connections to fail in a fashion many nonexpert programmers have trouble diagnosing. (In contrast trying to create a validator for an empty truststore throws a specific exception, something like 'trust anchor set must not be empty'.)

X.509 certificates for authentication without using client SSL

I'm trying to improve some code that enables logging in to our application using digital certificates, probably certificates stored on PKCS11 tokens.
It's a Java client server application, with the server on JBoss [Wildfly], and a rich Java thick client. We also have a GWT/Javascript based web client, but this doesn't yet support certificate auth.
The current implementation uses 2-way SSL authentication if certificate authentication is configured, i.e. the server will require a client certificate when the connection is opened. This causes some problems, and in trying to find ways to address them I've been searching madly to see if there is a standard, 'Right Way To Do PKI Auth To A JBoss Application'.
However just about everything I have found on the subject seems also to revolve around using two-way SSL, which kind of implies that is the Right Way to Do It.
It seems undesirable to me, in that the network transport is quite a low-level concern, heavily separated from the application logic and stuff like authentication and user management.
In order to prove the client is a valid user of the system (as opposed to merely someone with credentials endorsed by a CA in the server trust store), the server application logic has to rummage around looking to find the certificate that was used on the incoming connection in order to scrape the Common Name off it. I've discovered that javax.servlet.request.X509Certificate is a standard-ish parameter one can query on the servlet, so it ought at least to be possible.
The other architectural problem this causes is that our app requires reauthentication for the lifetime of certain sensitive operations. If one is using the SSL connection to prove the user has the private key, then logically that would require opening a whole separate connection.
Logically, authenticating with a certificate would seem to require
The server generating a nonce
The client encrypting the nonce using the client's private key
The client sending that encrypted value to the server with the accompanying public certificate [or certificate chain].
Now, that is exactly what happens during an SSL handshake, but obviously a whole load of other baggage comes with it that is irrelevant to the application-level concern of authenticating the user.
I thought about implementing the steps directly myself, but this would seem to violate the first rule of crypto (Don't implement your own crypto).
If the server generates random nonces then that introduces a level of chattiness and statefulness to the process, which is doable but a pain when you are striving for a stateless and clusterable server.
Time-based One-Time Password implementations circumvent this, and seem to be a standardized mechanism for 2-factor authentication that is getting support from Google+ and the like.
However I can't find anything in the way of out-of-the-box libraries that will let me build an implementation using certificates directly from an imposed PKI.

Java SSL Certificate

I need to communicate between two desktop Java app, and the best way to do this is to use SSL (to prevent sniffers). I control both the client and server so self-signed is okay.
My question is if a certificate can be made and used from scratch programmatically (i.e. the end user does not have to physically do anything himself).
If this is possible, could you give me some pointers?
My question is if a certificate can be made and used from scratch
programmatically (i.e. the end user does not have to physically do
anything himself).
Before you think of generating certificates (programmatically or not), you need to decide how you're going to verify them. In the context of two desktop applications, the traditional way of verifying a server certificate might not be suitable.
The purpose of the certificates is to give a way to verify the identity of the remote party, so as to prevent Man-In-The-Middle attacks. The verifying party uses something it already trusts to make this verification; not doing so makes using certificates pointless.
In the traditional model (with a fixed server), the server certificate is part of a PKI and issued by a CA. The client verifies its authenticity against a set of CA certificates it trusts (typically using the rules described in RFC 5280, and it verifies the certificate is valid for the host name is was looking for (how this is done depends on the protocol, but best and historical practices are described in RFC 6125). Both steps are necessary to prevent MITM attacks. It's similar to verifying someone's identity using a passport: you want to check that the passport is real and from an authority you trust, and you want to check that the name (or picture) matches the name you're looking for (or the face in front of you).
When establishing a communication between two desktop applications, you're certainly going to have problems with both aspects: how to let the client verify the cert was issued by an entity you trust, and was issued to the entity you want to communicate with. If you generate the certificate programmatically no either side, it's certainly going to be self-signed, which will make verifying its authenticity from the other side difficult without another means of exchange (independent of this SSL/TLS communication). In addition, desktops tend not to have fixed host names, so a DNS-based (or even an IP address) identifier might be inadequate in this context.
You'll need to think of a way to publish the certificate in a way that the remote party can verify it trusts it, and to think of an identifying scheme to make sure the certificate belongs to the right entity (this is typically what goes in the Subject DN or Subject Alternative Name extension of the certificate).
Once you've made these decisions, you can use BouncyCastle's org.bouncycastle.x509.X509V3CertificateGenerator to generate your certificate (an X.509 v3 certificate should let you add extensions to the certificate, e.g. for key usage purpose, if you need them). There are various examples (for v1, v3 and/or self-signed, i.e. where Subject = Issuer) on the BouncyCastle wiki. I'd say that using this is the easy bit unfortunately (the administrative side of trust would be the hardest).
If both desktop applications are in fact both part of a more central application, you could run a service that issues this certificate, from a certificate request (CSR) generated within your application. A central server would effectively run your own CA, and your desktop applications would trust that CA. Depending on the complexity of your organisation, there are tools available to do this, or you could also use BouncyCastle to implement it, using the same classes (it would even be better if you implemented CRL/OSCP to be able to revoke certificates). In this case, you could make your application generate a CSR and submit it to your central CA. CSRs can be generated with BouncyCastle using PKCS10CertificationRequest. Again, how your CA verify the CSR comes from the right party, using external information, is also an administrative problem, perhaps you can tie it to an e-mail verification scheme or something like that.
Once you've generated your certificates, you'll be able to use Java's JSSE as your SSL/TLS stack (typically using SSLSocket). You may have to use custom X509TrustManagers to implement the certificate verification (depending on how you've designed your scheme to verify the certificate, if you're unable to use a traditional CA model). Just make sure you don't use trust managers that don't do anything in their check* methods; there are a number of examples of this around: you might as well not use certificates at all in this case, if you don't do anything to verify them (this would make the connections vulnerable to MITM attacks).
You could use a JCE based solution with SSLSocket, docs for jdk6 and its implementation: SSLSocketImpl.
Java SSL SSH:
You have Jsch lib to connect to any server with SSH protocol.
Java Toolbox for SSL:
You have Bouncy Castle.
Look at their resources and their documentation. The wiki provides some usefull examples with the latest releases:
ContentSigner sigGen = ...;
PublicKey publicKey = ....;
Date startDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000);
Date endDate = new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000);
X509v1CertificateBuilder v1CertGen = new JcaX509v1CertificateBuilder(
new X500Principal("CN=Test"),
BigInteger.ONE,
startDate, endDate,
new X500Principal("CN=Test"),
publicKey);
X509CertificateHolder certHolder = v1CertGen.build(sigGen);

SSL and Tomcat using Java

I'm new to SSL connections so here goes my question.
I have a desktop Java program in a JAR file. This JAR sends sensitive information over the internet to a remote Tomcat server. Of course I need to encrypt the data.
If I purchase an SSL cerfiticate say from Verisign, will the data sent over SSL be automatically encrypted?
I mean in my JAR, will I still need to do extra work like use Java encryption extensions API to manually encrypt my data over the SSL connection?
Thank you.
I mean in my JAR, will I still need to do extra work like use Java encryption extensions API to manually encrypt my data over the SSL connection?
Encryption will be done for you (with the Java Secure Socket Extension). Just establish your connection using https://. Maybe have a look at HTTP Client for a higher level API.
By the way, the certificate goes on the server side (unless you want to do client-authentication too in which case, well, you'll need a client certificate too).
And yes, you could use a self-signed certificate but one of the benefits of using a certificate signed by a well known Certificate Authority (CA) like Verisign, Thawte, etc is that you won't have to add it to the trust store of the client VM (unless you disable the verification mechanism).
Follow the SSL Configuration HOW-TO on how to setup https.
If your goal is just to get the encryptian, you don't need to buy a certificate. You can make your own. Buying a certificate just creates the verification chain back to verisign (or whomever) to give users a warm fuzzy that you're really who you say you are.
SSLSocket should handle most of the work for you.
All data sent over SSL is by definition encrypted, you do not need to worry about encryption at all. Also, you do not need to by a certificate to achieve that: you can issue one on your own.
If you'll set up the SSL on Tomcat and send your data over HTTPS then the encryption will be done for you. But you don't actually need to purchase a certificate if you only need encryption for your data channel, you could generate a self-signed certificate. Have a look at this page http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html on how to configure SSL for Tomcat. But note that HTTPS can be configured not to use encryption at all (at least on Apache httpd).
To answer your question, SSL implementations automatically encrypt the data. You don't need to worry about using additional encryption routines.
It might be easiest to purchase an SSL certificate because SSL implementations provide easy certification authentication using common root certificates and provide a verification service. However, you could save some money by using a self-signed certificate.
Even with a self-signed certificate, it's important to validate the signature on the server certificate from the desktop application when you connect to the server. This will prevent man in the middle attacks.
You won't have to add your self signed certificate to the store because you should be able to disable the automatic verification mechanism and use your own.

Categories

Resources