Security is a vast topic that encompasses many areas. Some of these are part of the language itself, like access modifiers and class loaders. Furthermore, others are available as services, which include data encryption, secure communication, authentication, and authorization, to a name a few.
Therefore, it’s not practical to gain meaningful insight into all of these in this tutorial. However, we’ll try to gain at least a meaningful vocabulary.
Above all, security in Java begins right at the level of language features. This allows us to write secure code, as well as benefit from many implicit security features:
This is not a complete list of security features that Java provides, but it’s good enough to give us some assurance!
Before we begin to explore specific areas, let’s spend some time understanding the core architecture of security in Java.
The core principles of security in Java are driven by interoperable and extensible Provider implementations. A particular implementation of Provider may implement some or all of the security services.
For example, some of the typical services a Provider may implement are:
Java ships with many built-in providers. Also, it’s possible for an application to configure multiple providers with an order of preference.
Consequently, the provider framework in Java searches for a specific implementation of a service in all providers in the order of preference set on them.
Moreover, it’s always possible to implement custom providers with pluggable security functions in this architecture.
Cryptography is the cornerstone of security features in general and in Java. This refers to tools and techniques for secure communication in the presence of adversaries.
The Java Cryptographic Architecture (JCA) provides a framework to access and implement cryptographic functionalities in Java, including:
Most importantly, Java makes use of Provider-based implementations for cryptographic functions.
Moreover, Java includes built-in providers for commonly used cryptographic algorithms like RSA, DSA, and AES, to name a few. We can use these algorithms to add security to data in rest, in use, or in motion.
A very common use case in applications is to store user passwords. We use this for authentication at a later point in time. Now, it’s obvious that storing plain text passwords compromises security.
So, one solution is to scramble the passwords in such a way that the process is repeatable, yet only one-way. This process is known as the cryptographic hash function, and SHA1 is one such popular algorithm.
So, let’s see how we can do this in Java:
MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hashedPassword = md.digest("password".getBytes());
Here, MessageDigest is a cryptographic service that we are interested in. We’re using the method getInstance() to request this service from any of the available security providers.
Public Key Infrastructure (PKI) refers to the setup that enables the secure exchange of information over the network using public-key encryption. This setup relies on trust that is built between the parties involved in the communication. This trust is based on digital certificates issued by a neutral and trusted authority known as a Certificate Authority (CA).
Java platform has APIs to facilitate the creation, storage, and validation of digital certificates:
Java has a built-in trust-store called “cacerts” that contains certificates for well known CAs.
Java has some really handy tools to facilitate trusted communication:
Let’s see how we can work with certificates in Java to establish a secure connection using SSL. A mutually authenticated SSL connection requires us to do two things:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); char[] keyStorePassword = "changeit".toCharArray(); try(InputStream keyStoreData = new FileInputStream("keystore.jks")){ keyStore.load(keyStoreData, keyStorePassword); }
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); // Load the trust-store from filesystem as before
We rarely have to do this programmatically and normally pass system parameters to Java at runtime:
-Djavax.net.ssl.trustStore=truststore.jks -Djavax.net.ssl.keyStore=keystore.jks
Authentication is the process of verifying the presented identity of a user or machine based on additional data like password, token, or a variety of other credentials available today.
Java APIs makes use of pluggable login modules to provide different and often multiple authentication mechanisms to applications. LoginContext provides this abstraction, which in turn refers to configuration and loads an appropriate LoginModule.
While multiple providers make available their login modules, Java has some default ones available for use:
One of the most common mechanisms of authentication is the username and password. Let’s see how we can achieve this through JndiLoginModule.
This module is responsible for getting the username and password from a user and verifying it against a directory service configured in JNDI:
LoginContext loginContext = new LoginContext("Sample", new SampleCallbackHandler()); loginContext.login();
Here, we are using an instance of LoginContext to perform the login. LoginContext takes the name of an entry in the login configuration — in this case, it’s “Sample”. Also, we have to provide an instance of CallbackHandler, using the LoginModule that interacts with the user for details like username and password.
Let’s take a look at our login configuration:
Sample { com.sun.security.auth.module.JndiLoginModule required; };
Simple enough, it suggests that we’re using JndiLoginModule as a mandatory LoginModule.
Communication over the network is vulnerable to many attack vectors. For instance, someone may tap into the network and read our data packets as they’re being transferred. Over the years, the industry has established many protocols to secure this communication.
Java provides APIs to secure network communication with encryption, message integrity, and both client and server authentication:
Let’s now see how we can open a secure connection with other parties in Java using SSLSocket:
SocketFactory factory = SSLSocketFactory.getDefault(); try (Socket connection = factory.createSocket(host, port)) { BufferedReader input = new BufferedReader( new InputStreamReader(connection.getInputStream())); return input.readLine(); }
Here, we are using SSLSocketFactory to create SSLSocket. As part of this, we can set optional parameters like cipher suites and which protocol to use.
For this to work properly, we must have created and set our key-store and trust-store as we saw earlier.
Access Control refers to protecting sensitive resources like a filesystem or codebase from unwarranted access. This is typically achieved by restricting access to such resources.
We can achieve access control in Java using classes Policy and Permission mediated through the SecurityManager class. SecurityManager is part of the “java.lang” package and is responsible for enforcing access control checks in Java.
When the class loader loads a class in the runtime, it automatically grants some default permissions to the class encapsulated in the Permission object. Beyond these default permissions, we can grant more leverage to a class through security policies. These are represented by the class Policy.
During the sequence of code execution, if the runtime encounters a request for a protected resource, SecurityManager verifies the requested Permission against the installed Policy through the call stack. Consequently, it either grants permission or throws SecurityException.
Java has a default implementation of Policy that reads authorization data from the properties file. However, the policy entries in these policy files have to be in a specific format.
Java ships with “policytool”, a graphical utility to compose policy files.
Let’s see how we can restrict access to a resource like a file in Java:
SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { securityManager.checkPermission( new FilePermission("/var/logs", "read")); }
Here, we’re using SecurityManager to validate our read request for a file, wrapped in FilePermission.
But, SecurityManager delegates this request to AccessController. AccessController internally makes use of the installed Policy to arrive at a decision.
Let’s see an example of the policy file:
grant { permission java.security.FilePermission <<ALL FILES>>, "read"; };
We are essentially granting read permission to all files for everyone. But, we can provide much more fine-grained control through security policies.
It’s worth noting that a SecurityManager might not be installed by default in Java. We can ensure this by always starting Java with the parameter:
-Djava.security.manager -Djava.security.policy=/path/to/sample.policy
XML signatures are useful in securing data and provide data integrity. W3C provides recommendations for governance of XML Signature. We can use XML signature to secure data of any type, like binary data.
Java API supports generating and validating XML signatures as per the recommended guidelines. Java XML Digital Signature API is encapsulated in the package “java.xml.crypto“.
The signature itself is just an XML document. XML signatures can be of three types:
Certainly, Java supports creating and verifying all the above types of XML signatures.
Now, we’ll roll up our sleeves and generate an XML signature for our data. For instance, we may be about to send an XML document over the network. Hence, we would want our recipient to be able to verify its integrity.
So, let’s see how we can achieve this in Java:
XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM"); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); Document document = documentBuilderFactory .newDocumentBuilder().parse(new FileInputStream("data.xml")); DOMSignContext domSignContext = new DOMSignContext( keyEntry.getPrivateKey(), document.getDocumentElement()); XMLSignature xmlSignature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo); xmlSignature.sign(domSignContext);
To clarify, we’re generating an XML signature for our data present in the file “data.xml”. Meanwhile, there are a few things to note about this piece of code:
As a result, the XML document will now contain the Signature element, which can be used to verify its integrity.
As we have seen by now, the Java platform provides a lot of the necessary functionality to write secure applications. However, sometimes, these are quite low-level and not directly applicable to, for example, the standard security mechanism on the web.
For example, when working on our system, we generally don’t want to have to read the full OAuth RFC and implement that ourselves. We often need quicker, higher-level ways to achieve security. This is where application frameworks come into the picture – these help us achieve our objective with much less boilerplate code.
And, on the Java platform – generally that means Spring Security. The framework is part of the Spring ecosystem, but it can actually be used outside of pure Spring application.
In simple terms, it helps is achieve authentication, authorization and other security features in a simple, declarative, high-level manner.
In short, in this tutorial, we went through the high-level architecture of security in Java. Also, we understood how Java provides us with implementations of some of the standard cryptographic services.
We also saw some of the common patterns that we can apply to achieve extensible and pluggable security in areas like authentication and access control.
To sum up, this just provides us with a sneak peek into the security features of Java. Consequently, each of the areas discussed in this tutorial merits further exploration. But hopefully, we should have enough insight to get started in this direction!
Thanks for reading ❤
If you liked this post, share it with all of your programming buddies!
Follow us on Facebook | Twitter
☞ Java Programming Masterclass for Software Developers
☞ Selenium WebDriver with Java -Basics to Advanced+Frameworks
☞ Java In-Depth: Become a Complete Java Engineer!
☞ Top 4 Spring Annotations for Java Developer in 2019
☞ Java Tutorial for Absolute Beginners
☞ 100+ Java Interview Questions and Answers In 2019
☞ Python vs Java: Understand Object Oriented Programming
☞ Securing RESTful API with Spring Boot, Security, and Data MongoDB
☞ Spring Security Tutorial: Simple Authentication with Spring Boot
☞ Build Web App Authentication using Spring Boot, Spring Security, MongoDB and Angular 8
#java #security #spring-boot