Is there simply way to dinamic generate untrusted ssl certificate without domain and applay it to server socket - all from code, no commandline or additional files?
Purpose is secure connection between two hosts witch know only IP and port to communicate each other - certificates generated randomly at server start and used as "untrusted", no domain so no verification (if I'm not wrong). I think this can be usefull in secureing data transfer between datacenters in third party apps.
This is working code for not encrypted simply client-server test.
package study.benchmark.utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import org.junit.Test;
public class DynamicSSLTest {
#Test
public void sslServerSocketTest() throws Exception {
System.out.println("ssl server test");
final int port = 8750;
// server
Thread th = new Thread() {
#Override
public void run() {
try {
//ServerSocketFactory factory = SSLServerSocketFactory.getDefault();
ServerSocketFactory factory = ServerSocketFactory.getDefault();
ServerSocket server = factory.createServerSocket(port);
Socket socket = server.accept();
OutputStream out = socket.getOutputStream();
out.write("some data".getBytes());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
th.start();
//client
//SocketFactory factory = SSLSocketFactory.getDefault();
SocketFactory factory = SocketFactory.getDefault();
Socket socket = factory.createSocket("localhost", port);
InputStream is = socket.getInputStream();
StringBuffer sb = new StringBuffer();
int data;
while ((data = is.read()) >= 0) {
System.out.print((char) data);
}
System.out.println();
socket.close();
th.join();
}
}
You can generate a self-signed certificate dynamically using a library such as BouncyCastle (essentially, for the certificate to be self-signed, you using the same issuer DN as the subject DN and you sign with the private key corresponding to the certificate's public key). Then, you'll need to put it in a KeyStore (in memory at least, not necessarily on file) and build an SSLContext from it, so as to be able to build an SSLSocketFactory.
This can be useful for testing, but this will not make your application secure. Generally speaking, encryption without authentication of the remote party isn't secure. You can exchange information as "secretly" as you want with a remote party, but if you haven't verified its identity you're not really sure your secrets are given to the intended recipient.
If your certificate is generated dynamically, you would need to find a way for the client to know it's indeed the legitimate certificate, before making any calls to that server.
The general SSH approach (where one assumes few people actually check the fingerprint they get in the first connection -- some people actually do check out of band) is a compromise whereby clients tend to accept the key (more or less blindly) the first time but will be warned if it has changed. You could implement this sort of approach for handling X.509 certificate trust too, but if you re-generate a new self-signed certificate every time you restart your server, you're back to the initial problem.
You could address this problem by having some sort of online/dynamic CA, where the servers would request and be issued a certificate dynamically based on something they could prove to that CA dynamically (to prove they're one of your servers, perhaps based on some configuration parameter known by both), and then have the client trust that CA, but that's a more complex scenario.
Why? It's not secure, and who will trust it anyway? Why can't you create a server certificate per deployment offline?
For your stated purpose of securing communications between two hosts, both of which are under your control, SSH would be a better solution. You can generate a key pair shared only with your other machine. Google "java ssh" for a host of options.
Related
I've been researching all over the internet for 3 weeks now for how to make Netty enable TLS (any version of it) on Android (at first, I took it on as a challenge). But as of now, I couldn't get it to work, not even once. I have read a LOT of articles, gists and docs about how TLS works, and certificates (inc. self-signed certificates and two-way authentification), key stores, the handshake itself, and even opportunistic TLS, but this is all confusing to me I don't know where else to ask. I need guidance to piece things together.
I just can't put all the pieces together. Here's what I have:
A Twisted server (Python), which doesn't belong to me, will present his certificates to the client. The certificates are issued by Let's Encrypt's global CAs (If you don't know Let's Encrypt, that's fine, all you have to know is that they're globally trusted and also compatible with Android although I don't know what they meant exactly with that). The server does not support self-signed certificates
And a Netty client that establishes bare unencrypted connection with the server (Which works just fine because the server will accept the bare connection as is if opportunistic TLS isn't triggered. The client code (More precisely, the bootstrapping initialization part, which matters the most) is here:
viewModelScope.launch(Dispatchers.IO) {
val group: EventLoopGroup = NioEventLoopGroup()
val b = Bootstrap()
b.group(group) /* Assigning the event loop group to the bootstrap */
.channel(NioSocketChannel::class.java) /* We want a NIO Socket Channel */
.handler(object : ChannelInitializer<SocketChannel>() {
override fun initChannel(ch: SocketChannel) {
val p: ChannelPipeline = ch.pipeline()
/** Adding TLS stuff ? There's literally no good doc on this */
val engine: SSLEngine? = null
engine?.useClientMode = true
p.addLast("ssl", SslHandler(engine))
p.addLast(
"framer",
DelimiterBasedFrameDecoder(8192, *Delimiters.lineDelimiter())
)
p.addLast(StringDecoder())
p.addLast(StringEncoder())
p.addLast(Reader())
}
})
/** The following line works for non-TLS, but for TLS, nothing happens */
val f: ChannelFuture = b.connect(serverHost, serverPort)
}
How exactly do certificates work on Android? Should I create a key store ? or a certificate ? or both ? I don't really understand how to get this done. I heard that Okhttp has SSL built-in, so how does it actually initialize that SSL connection ?
Edit: Removed startHandshake(); as it's irrelevant to the question and rarely needed (for example, not in my case)
I have a rather specific and rare client-server protocol (over TCP).
I've implemented it using SSLSocket.
Now, I foresee that I might need to use the same protocol over an un-encrypted connection.
My problem is that the class implementing the protocol has a field: public SSLSocket currentSocket;
(and then the methods in my client class do all sorts of .read(), .write(), flush()...)
I thought about changing the field type, like so: public Socket currentSocket;
However, then, the problem is that my connection procedure is incompatible:
public static void connect () {
currentSocket = SslUtils.getSSLsocket(host, port, keystoreFile, keystorePass, pkPass);
...
java.net.Socket 's default constructor obviously doesn't accept keystore stuff
I don't want to re-implement my whole client just for this difference...
One thought I have is, when I need a plaintext Socket, to create an SSLSocket with no encryption.
I don't know if that's a professional way of doing it or if it will even work (the server will expect a plaintext client socket in the new use case)
My other idea is to define two fields, one for plaintext socket, one for SSL socket and then use logic to link the in/out streams to the correct field, as needed. However, that will result in a "hanging" field. If you use SSL, there will be a redundant field Socket plaintextSocket and vice-versa...
Is there a way to make my currentSocket field more abstract, so that I can define it in the same client, then instruct a slightly different client code path depending on a known variable (something like needSSLsocket=true) for the instantiation and connection?
SSLSocket extends Socket, so you can assign an SSLSocket object to a Socket variable. You are right to change your currentSocket field to a Socket. Simply use another variable to handle the SSLSocket when needed, eg:
public static void connect () {
if (needSSLsocket) {
SSLSocket ssl = SslUtils.getSSLsocket(host, port, keystoreFile, keystorePass, pkPass);
ssl.startHandshake();
...
currentSocket = ssl;
/* or:
currentSocket = SslUtils.getSSLsocket(host, port, keystoreFile, keystorePass, pkPass);
((SSLSocket) currentSocket).startHandshake();
...
*/
} else {
currentSocket = new Socket(host, port);
}
...
}
I have found several java STUN implementations
Java and Which Stun libraries i should use?
There is
JSTUN: http://jstun.javawi.de/
STUN: http://java.net/projects/stun
See also: STUN, TURN, ICE library for Java
But it is jars with many classes. I wish to find something simple in form of single method or at least single small class. Like following python code.
https://github.com/jtriley/pystun/blob/develop/stun/init.py
Reasonable answer why STUN in Java is so huge is also acceptable.
Reasonable answer why STUN in Java is so huge is also acceptable.
It's a reasonable question. 99% of what STUN is just a simple echo/response protocol for a client to self-discover the IP and port mapping as a result of NAT between it and the public internet. Having built a STUN library in C++, I have some insight.
Let's think about what is required of a STUN library:
A message writer that generates the STUN messages with an attribute field schema that not only allows for fields to appear in any order, it also allows for custom attributes to be added as well.
A message parser that can read such messages back and convert a data
structure reasonable for code to use. It needs to do this securely and avoid unhandled exceptions.
Socket networking code to send/receive such messages. And STUN servers are technically required to listen on 2 IPs and 2 ports, so that makes the networking code for the server a bit more complex.
If we just care about binding requests and binding responses, we'd be
done. But the STUN RFCs also define a set of NAT classification tests. So additional state machine logic is needed to make any such library complete.
And if the STUN library is going to go all the way with the security options afforded by the protocol, it would need some amount of crypto code for hashing and signing of messages
So combining all this into a library that anyone can use for all the different purposes of STUN including mapped address discovery, NAT classification, and ICE negotiation, it starts to get big quick.
You could easily just roll some socket code that hardcodes the bytes of a binding request and then some hacked up parsing to parse the response. That might meet your own needs, but a well established open source library would never be written this way.
JSTUN is a good start. I've shared some interop and bug fixing code with the original author. He doesn't actively maintain it, but it's a good implementation of RFC 3489. I even hacked it up once to run on Android.
To generate a STUN binding request in JSTUN.
MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
sendMH.generateTransactionID();
// add an empty ChangeRequest attribute. Not required by the standard, but JSTUN server requires it
ChangeRequest changeRequest = new ChangeRequest();
sendMH.addMessageAttribute(changeRequest);
byte[] data = sendMH.getBytes();
// not shown - sending the message
Then to parse the response back:
byte [] receivedData = new byte[500];
// not shown - socket code that receives the messages into receivedData
receiveMH.parseAttributes(receivedData);
MappedAddress ma = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
Then combine the above with some socket code. The best example of combining the above with socket code can be found in the DiscoveryTest.java source file. You really just need the code in the test1() method of this class.
MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
// sendMH.generateTransactionID();
// add an empty ChangeRequest attribute. Not required by the
// standard,
// but JSTUN server requires it
ChangeRequest changeRequest = new ChangeRequest();
sendMH.addMessageAttribute(changeRequest);
byte[] data = sendMH.getBytes();
s = new DatagramSocket();
s.setReuseAddress(true);
DatagramPacket p = new DatagramPacket(data, data.length, InetAddress.getByName("stun.l.google.com"), 19302);
s.send(p);
DatagramPacket rp;
rp = new DatagramPacket(new byte[32], 32);
s.receive(rp);
MessageHeader receiveMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingResponse);
// System.out.println(receiveMH.getTransactionID().toString() + "Size:"
// + receiveMH.getTransactionID().length);
receiveMH.parseAttributes(rp.getData());
MappedAddress ma = (MappedAddress) receiveMH
.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
System.out.println(ma.getAddress()+" "+ma.getPort());
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import de.javawi.jstun.attribute.ChangeRequest;
import de.javawi.jstun.attribute.ChangedAddress;
import de.javawi.jstun.attribute.ErrorCode;
import de.javawi.jstun.attribute.MappedAddress;
import de.javawi.jstun.attribute.MessageAttribute;
import de.javawi.jstun.attribute.MessageAttributeException;
import de.javawi.jstun.attribute.MessageAttributeParsingException;
import de.javawi.jstun.header.MessageHeader;
import de.javawi.jstun.header.MessageHeaderParsingException;
import de.javawi.jstun.util.UtilityException;
public class StunTest { public static void main(String[] args) throws UtilityException, IOException {
MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
// sendMH.generateTransactionID();
// add an empty ChangeRequest attribute. Not required by the
// standard,
// but JSTUN server requires it
ChangeRequest changeRequest = new ChangeRequest();
sendMH.addMessageAttribute(changeRequest);
byte[] data = sendMH.getBytes();
DatagramSocket s = new DatagramSocket();
s.setReuseAddress(true);
DatagramPacket p = new DatagramPacket(data, data.length, InetAddress.getByName("stun.l.google.com"), 19302);
s.send(p);
DatagramPacket rp;
rp = new DatagramPacket(new byte[32], 32);
s.receive(rp);
MessageHeader receiveMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingResponse);
// System.out.println(receiveMH.getTransactionID().toString() + "Size:"
// + receiveMH.getTransactionID().length);
try {
receiveMH.parseAttributes(rp.getData());
} catch (MessageAttributeParsingException e) {
e.printStackTrace();
}
MappedAddress ma = (MappedAddress) receiveMH
.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
System.out.println(ma.getAddress()+" "+ma.getPort());
}
}
I want my Java Code to search for CA certificate of the server in one keystore... if it is unable to find the specific certificate (which I think will be known only when I try to connect via LDAP to Directory Server), it should look for the certificate in another keystore, whose path I know.
I tried this:
System.setProperty("javax.net.ssl.trustStore", System.getProperty("java.home") + "/lib/security/cacerts" + System.getProperty("path.separator") + path/to/second/keystore);
But it didn't seem to work.
Adding only one path (either of them) works, i.e. it runs like charm if certificate is found and fails if not.
So my question is:
Is there a method to add multpile keystore paths to javax.net.ssl.trustStore?
If it is not possible how should I write my code (I am asking for the algorithm) so that it just not throw Exception after first search itself and fail?
P.S. : I am not much familiar with Java.
Below is the relevant section of my code:
if(useSSL)
{
try
{
SSLContext se = SSLContext.getInstance("TLS");
Security.addProvider(se.getProvider());
}
catch(NoSuchAlgorithmException e) { }
System.setProperty("javax.net.ssl.trustStore", System.getProperty("java.home") + "/lib/security/cacerts");
com.org.ldap.LDAPSocketFactory ssf = new LDAPJSSESecureSocketFactory();
LDAPConnection.setSocketFactory(ssf);
}
try
{
lc = new LDAPConnection();
lc.connect( ldapServer, ldapPort);
lc.bind( ldapVersion, ldapUser, (userInfo[1]).getBytes() );
}
catch (LDAPException le)
{
le.printStackTrace();
}
You can't have multiple paths for javax.net.ssl.trustStore.
The easiest would be to make a local copy of the JRE's cacerts and import the certificates from your other store into it (effectively merging them). (See keytool -importkeystore.)
Otherwise, if you know in advance that all your LDAP connections will use your second keystore (and you also want to be able to use the default truststore for other, non-related connections), you could configure that trust store for that SSLSocketFactory only. I'm not familiar with com.org.ldap.LDAPSocketFactory, but it might have an option to do so. (Otherwise, you could create your custom SSLContext initialised with your second truststore and get an SSLSocketFactory, as described in this answer).
Another, more complicated way, would be to create a custom X509TrustManager that wraps the default trust manager, catches its exceptions and tries again with another trust manager initialised using your second store. It's feasible, but you'd need to make sure it still throws the exception if neither trust managers accept your certificate (otherwise, there would be a security hole). If you're not familiar with the JSSE API (or Java altogether), it's probably not the best option.
In addition, be careful when you use System.setProperty("javax.net.ssl.trustStore", ...) in your code: it is what's read to initialise the default SSLContext, but the default SSLContext is only initialised once, the first time it's required. Setting this system property afterwards would have no effect (unless of course, other classes from other libraries also rely on this value).
It's also not clear what you're trying to achieve with this, since you'll always ever succeed to add a security provider that's already there:
try
{
SSLContext se = SSLContext.getInstance("TLS");
Security.addProvider(se.getProvider());
}
catch(NoSuchAlgorithmException e) { }
No, just import all the certificates from one truststore into the other, and use the second.
Although it is a bit late, I would like to make a remark on the answer which the others have provided. It was nearly impossible indeed and with a lot of custom code it would be possible actually, we have seen it within an answer of a similar question before: Using a custom truststore in java as well as the default one
After encountering the same challenge for multiple projects I thought it would be handy to create a library and also make it publicly available to contribute back to the community. Please have a look here: Github - SSLContext-Kickstart
The usage for your use case would be for loading the jdk truststore and your own truststore:
import nl.altindag.sslcontext.SSLFactory;
import javax.net.ssl.SSLContext;
import java.security.cert.X509Certificate;
import java.util.List;
public class App {
public static void main(String[] args) {
String trustStorePath = ...;
char[] password = "password".toCharArray();
SSLFactory sslFactory = SSLFactory.builder()
.withDefaultTrustMaterial()
.withTrustMaterial(trustStorePath, password)
.build();
SSLContext sslContext = sslFactory.getSslContext();
List<X509Certificate> trustedCertificates = sslFactory.getTrustedCertificates();
}
}
I need to restrict specific user's roles to use the application while are accessing it from a specific place lets say "Users with role employee can only access the application from the office or its branches"
IP checking? it is changeable
How about if I follow private/public key? the disadvantage of that is that if I put the key in the cookie they can read it or may clear their cookies.
Based on mac address ? it is changeable
You can't trust IPs and MAC addresses are even more useless, your only friend here is cryptography. Assuming your user will authenticate using his credentials you need to somehow authenticate the machine as well. This is done by placing a different certificate on each machine and having the client prove his "identity" to the server by using his certificate.
If your client-server communication is based on SSL, you can require client authentication - have a look at Java HTTPS client certificate authentication, http://rap.ucar.edu/staff/paddy/cacerts/ or http://docs.oracle.com/cd/E11035_01/wls100/security/SSL_client.html.
If your communication is not based on SSL, or you wish to authenticate on the application level - you can still use the certificate. Load it from the truststore and prove your identity by proving you have access to the private key (usually the server sends a challenge, something random encrypted with the public key, you answer by decrypting it with the private key and sending it back. That way you prove you have the private key without having to show it).
If you do not wish to store certificates, you can just place a different encrypted file on each machine. The client will be able to decrypt it (using a hard coded key) and send something akin to a password to the server.
How do you protect these certificates? Read-only permissions for the users on the file...
Several notes -
You can't really EVER trust a client machine. A resourceful hostile user will break anything. The more resources you "enemies" have the more effort you need to put into your defence.
You didn't specify details regarding your environment. I'm sure there are system level solutions which I'm not aware of. For example - your server may connect to the Active Directory and monitor user logins on specific machines.
Sometimes the best solution may not come from the software level. For example, if your server uses a designated port for your communication. You could allow\block this traffic on your firewall\router\personal firewall - in places more adequate to resolve this issue than your server. If you have application control enforcement, you can allow the client itself to run on only specific machines.
You can also look for ways to create some unique PC fingerprint (motherboard Id, cpu id, SID in Active Directory, HDD id, MAC address...) - your server could then store a list of allowed fingerprints and your client will send the currently calculated fingerprint. This still comes back to - how well do you trust your clients?
Restricting by IP only works if people are coming from places that have static IPs. Anywhere like at home where you have dynamic it doesn't work.
If you can't use static and still want to restrict by IP you could use a service like http://dyn.com/dns/ to assign a FQDN to your IP. Then you could do lookup by FQDN to see if it returns an IP that matches the one in the request. This lookup could be cached so you're only doing every few hours. The tricky part to this is that each location would have to setup of a dynamic DNS client. Some routers now have this built in.
You can't get the MAC address through the HttpServlet class. And if could you'd get the MAC address from the device that is talking to your server which most likely would be something like a router, load balance, switch. MAC address aren't not route'able.
Re: Keys, you can use x509 certs - http://static.springsource.org/spring-security/site/docs/3.0.x/reference/x509.html
I just want to address this part of your Question:
I am trying to implement the IP method but it runs into following error.
java.lang.IncompatibleClassChangeError: com.project.Default and
com.project.Default$IpCheckService disagree on InnerClasses attribute
An IncompatibleClassChangeError means that there is a conflict between what the types were at compile time and what they are at runtime. In this case, it seems to be that you have (had) a nested IpCheckService class that has changed from static to non-static (or vice-versa!), and somehow you've managed to load an old version of one of the classes.
This is a build or deployment problem. If you can figure out what is going wrong here there is a good chance that your code will work. (At least, you won't get this exception any more.)
UPDATE:
The only way to restrict users in a certain place is the following:
You must define Fixed IP's in the Office!
Or, at least, a subnet mask for the office and it's branches.
In your application, check the subnet mask from the request and compare it to fixed preconfigured office subnet mask.
Thus, either you put those fixed IP's into webconf.xml or the subnet mask for the IPs;
In any event, the solution will always be connected to a network solution.
You could try something like this to check a fixed IP:
public class TestFilter implements Filter{
public void destroy() {}
public void init(FilterConfig arg0) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filter) throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
IpAddressMatcher matcher = new IpAddressMatcher("192.168.1.0/24");
try {
if(!matcher.matches(req.getHeader("X-Forwarded-For"))){
res.sendRedirect("AnotherPage.jsp");
}
} catch (UnsupportedOperationException e) {
//Handle IT
}
}
}
Also, you may need check the following, to catch every situation:
request.getHeader("Proxy-Client-IP");
request.getHeader("WL-Proxy-Client-IP");
request.getHeader("HTTP_CLIENT_IP");
request.getHeader("HTTP_X_FORWARDED_FOR");
request.getRemoteAddr();
I've found the following code from here, you can identify them based on their mac address.
This one is also helpful.
package com.mkyong;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
public class App{
public static void main(String[] args){
InetAddress ip;
try {
ip = InetAddress.getLocalHost();
System.out.println("Current IP address : " + ip.getHostAddress());
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
byte[] mac = network.getHardwareAddress();
System.out.print("Current MAC address : ");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
}
System.out.println(sb.toString());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e){
e.printStackTrace();
}
}
}
To set up such kind of authorization rules, the first thing need to be defined is:
What is the definition of "office or its branches" in terms of computer identification?
Usually it is computers from some subnetwork as written in answers above - in that case the solution is obvious.
Second possibility - some guy came to the office with his personal computer or laptop or touchpad, etc. If this is allowed by security policy, the only entity we can authenticate is user. We might still want to differ access from the office (as physical location, e.g. building)/from home. In that case I'd recommend to look at one-time-password generation devices that should be available to user only in the office.
"Users with role employee can only access the application from the office or its branches"
Use a site-to site VPN. This effectively transforms the problem into an intranet login problem, which is trivial to solve.
By having a site-to-site VPN, you can be assured of the identity of the remote sites, since connection setup and authentication is generally performed by routers on site, the configuration of which users at the site do not need to know (or have on their computers, so cannot take away).
Once transformed into an intranet problem, just bind the application to an intranet address and secure it as you would any other intranet resource.