Security - Java 8 Recipes, 2th Edition (2014)

Java 8 Recipes, 2th Edition (2014)

CHAPTER 22. Security

Security is one of the most important portions of application development. Providing applications that function properly is only one part of successful application development. Providing a secure application is another important task that many developers overlook. The Java language makes it easy to develop secure applications by providing many options and features that make applications more secure. In Java 8, there have been additions and enhancements across the spectrum of security options, making the Java language even more robust and secure. The following is a list of some those security features:

· TLS 1.1 and TLS 1.2 enabled by default

· Limited doPrivileged() block permissions

· Password-based encryption (SunJCE) now provides stronger algorithms

· SSL/TLS Server Name Indication (SNI) extension support

· SunJCE provider is enhanced to support AES/GCM/NoPadding cipher implementation as well as GCM algorithm parameters

· SunJCE provider enhanced to support AEAD mode-based cipher suites

· -importpassword option added to keytool

· DKS keystore support

· Cryptographic algorithms have been enhanced with the SSH-224 variant of SHA-2

· Enhance support for NSA Suite B cryptography

· SecureRandom now provides better support for high entropy random number generation

· New PKIXRevocationChecker class

· PKCS 11 Windows support now includes 64-bit

· New rcache types added to Kerberos 5

· Kerberos 5 protocol transition supported in same realm as constrained delegation

· DES-related Kerberos 5 encryption types not supported by default

· Unbound SASL for the GSS-API/Kerberos 5 mechanism

· SASL service can support multiple host names

· JNI bridge to native JGSS on OS X

· Stronger ephemeral DH keys supported

· Cipher suites preference customization

This chapter contains recipes that cover a handful of these security features.

22-1. Asserting a Subset of Privileges

Problem

You want to run an application with a regular set of privileges, but a portion of the application requires a subset of privileges in order to prevent a possible security risk.

Solution

Write a privileged block and assert a subset of privileges to the block of code that requires additional security. In the following code block, standard permissions execute the application, but only a subset of permissions writes the file. This is done by placing the code in anAccessController.doPrivileged block, which specifies the least amount of privileges required to complete the task.

public static String writeFile(final String filename) {
return AccessController.doPrivileged(
(PrivilegedAction<String>) () -> {

try (PrintWriter writer = new PrintWriter(filename, "UTF-8")) {
writer.println("The first line");
writer.println("The second line");
} catch (FileNotFoundException | UnsupportedEncodingException ex) {
System.out.println(ex);
}
return null;
},
null,
new java.io.FilePermission(filename, "write"));
}

How It Works

In Java 8, the AccessController.doPrivileged method has been revamped, allowing you to specify a subset of privileges that can be used in a block, rather than executing a heightened set of privileges. The API for Privileged Blocks can be used in the Java language to specify a different set of permissions to execute a portion of code. To specify the permissions, you must use an AccessController.doPrivileged block, passing a PrivilegedAction that encapsulates the actions that require the modified permission set. The following code demonstrates the typical use:

AccessController.doPrivileged(new PrivilegedAction<void>(){
public void run(){
// do something privileged
}
});

The new variation of this technique allows you to specify a subset of privileges, rather than a superset. In the recipe, a subset of privileges writes a file to disk. The new doPrivileged method accepts three arguments, as you can see in the following pseudocode:

AccessController.doPrivileged(
PrivilegedAction,
AccessControlContext,
java.security.Permission varargs);

The first argument is the same as the standard AccessController.doPrivileged, which is a PrivilegedAction. The PrivilegedAction can be in the format of a class or a lambda expression. In the recipe example, a lambda expression is the first argument. In either case, the class or lambda must implement the PrivilegedAction functional interface, which contains the run() method. The run() method returns a value, and this example simply returns a null since there is no relevant value to return.

The second argument is an AccessControlContext object, which can be used to perform an additional security check. The AccessControlContext instance can be obtained by calling on the AccessControlContext.getInstance() from a particular calling context. In the example, null is specified, which indicates that no additional security check is required.

The third argument is a varargs parameter that’s used to specify the type of permission to be granted. The argument accepts one or more Permission objects or an array of Permission objects.

The new AccessController.doPrivileged method can be used to indicate least privilege or more privilege for the task at hand. This method provides a secure way to allow an application running with standard permissions to execute tasks using a lesser or higher set of permissions. To learn more about this feature, take a look at the online documentation at http://docs.oracle.com/javase/8/docs/technotes/guides/security/doprivileged.html#asserting_a_subset_of_privileges.

22-2. Indicating a Server Name for Handshaking

Problem

You want to verify a server name to ensure that it is valid prior to initiating an SSL connection with the server.

Solution

Make use of the Server Name Indication (SNI) extension to determine what server name a client is attempting to connect to during a handshake. The SNI extension is used in the handshake process to specify which host a client is attempting to connect to. In the following example, a client is created that indicates a server host for a valid connection in its SSL parameter list.

public class SocketClient {

public static Socket socket = null;
public static PrintWriter out;
public static BufferedReader in;

public static void main(String[] args) {
createConnection("gmail.com",443);
}

public static void createConnection(String host, int port) {

try {
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket sslSocket = (SSLSocket) factory.createSocket(host, port);

SNIHostName serverName = new SNIHostName(host);
List<SNIServerName> serverNames = new ArrayList<>(1);
serverNames.add(serverName);

SSLParameters params = sslSocket.getSSLParameters();
params.setServerNames(serverNames);
sslSocket.setSSLParameters(params);
//Create socket connection
System.out.println("Starting handshake...");
sslSocket.startHandshake();
System.out.println("Handshake complete...");
System.out.println("Peer Host: " + sslSocket.getSession().getPeerHost());
sslSocket.close();
System.exit(1);
} catch (UnknownHostException e) {
System.out.println(e);
System.exit(1);
} catch (IOException e) {
System.out.println(e);
System.exit(1);
}
}

}

Given the client, the server must use a SNIMatcher class to indicate how to recognize the server name. The following code demonstrates a server side implementation for determining whether a host name matches a SNIMatcher object.

final int httpd = 443;
SSLServerSocket sslServerSocket = null;
try {
ServerSocketFactory sslFactory = SSLServerSocketFactory.getDefault();
sslServerSocket = (SSLServerSocket) sslFactory.createServerSocket(httpd);
SNIMatcher matcher = SNIHostName.createSNIMatcher("www\\.gmail\\.(com|org)");
Collection<SNIMatcher> matchers = new ArrayList<>(1);
matchers.add(matcher);

SSLParameters params = sslServerSocket.getSSLParameters();
params.setSNIMatchers(matchers);
sslServerSocket.setSSLParameters(params);

SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();

System.out.println("closing socket");
sslSocket.close();
sslServerSocket.close();
} catch (IOException e) {
System.out.println(e);
} finally {
try {
sslServerSocket.close();
} catch (IOException ex) {
System.out.println(ex);
}
}

How It Works

The Server Name Indication (SNI) extension provides a server with a list of valid servers for connection when attempting a handshake. The SNI extension uses SSL/TLS protocols to provide this functionality. Given a specified server name, an application can determine whether it is a valid address for opening an SSLSocket or SSLEngine connection.

To indicate a server name, use the SSLParameters.setServerNames(List<SNIServerName> serverNames) method and pass a list containing valid server names. In the recipe example, the server name of gmail.com is set as valid using the following lines of code:

SNIHostName serverName = new SNIHostName(host);
List<SNIServerName> serverNames = new ArrayList<>(1);
serverNames.add(serverName);

SSLParameters params = sslSocket.getSSLParameters();
params.setServerNames(serverNames);
sslSocket.setSSLParameters(params);

An ArrayList is created containing SNIServerName objects that each indicate a valid server name. In the example, one SNIServerName object is added to the list. A list of valid servers is then passed to the SSLParameters.setServerNames() method.

22-3. Specifying a Keystore Password

Problem

You want to secure an SSL keystore with an additional password for added security.

Solution

Specify the new importpassword command option when creating a keystore.

keytool -genkey -alias java8recipes -keyalg RSA -keystore keystore.jks -keysize 2048

Use the following to import a passphrase:

keytool –importpassword –alias java8recipes

How It Works

The keytool utility’s new –importpassword command allows you to store a passphrase in the new KeyStore.SecretKeyEntry. This password provides another level of security beyond the standard keypass because the keypass is used to protect it. When users issue this command, they must provide the –keypass option along with the keystore password, otherwise they will be prompted for it.

22-4. Generating the Strongest Random Number Possible

Problem

You are generating random numbers for use with some secure context, such as a RSA key.

Solution

Utilize the SecureRandom.getInstanceStrong() method to produce a SecureRandom object that has been selected by using the algorithms or providers that are specified in the SecureRandom.strongAlgorithmsSecurity property. In the following example, aSecureRandom object is generated using the getInstanceStrong() method. A seed is then generated from the SecureRandom instance, which can later be used for some secure purpose.

SecureRandom strongSecure = SecureRandom.getInstanceStrong();
byte seed[] = strongSecure.generateSeed(20);

How It Works

In certain situations, a secure random key is required. One such situation is for generating RSA public and private keys. In these situations, the SecureRandom getInstanceStrong() method returns a SecureRandom object that was selected by using the algorithms and providers specified in the secureRandom.strongAlgorithms security policy. You can obtain the algorithms by calling on the Security.getAlgorithms() method and passing the Java cryptographic service name.

22-5. Granting Permissions for Rich Internet Applications

Problem

You want to govern permissions for an application based on HTTP, rather than on any type of TCP connection. Furthermore, you want to specify host names for the HTTP permissions, rather than individual IP addresses.

Solution

Make use of the java.net.URLPermission class, which was introduced in Java 8, in order to define a higher-level permission than a standard java.net.SocketPermission offers. Doing so will allow you to express permissions in the context of HTTP request methods and URL scheme. In the following example, the URLPermission class is used to access an author URL at the http://www.apress.com website. The URLPermission class represents the permission to access resources defined by a given URL, and also for a set of user-settable request methods and request headers. In this case, the URLPermission class represents the resources defined at the author URL on the apress.com site.

public static void main(String[] a) {
URLPermission urlPermission = new URLPermission("https://www.apress.com/index.php/author/author/view/id/1866");
try {
AccessController.checkPermission(urlPermission);
System.out.println("Ok to open socket");
} catch (AccessControlException ace) {
System.out.println(ace);
}
}

In this case, permission would be denied, as this URL is to a private host. However, if you had built an application that performed a callback to a remote application server, the host would perform a validity check and then provide access to open the socket for valid requests.

How It Works

Oftentimes, sandboxed applications require the use of permissions to access remote resources. Prior to Java 8, the java.net.SocketPermission was used to perform access control for such resources. Utilization of the SocketPermission was problematic because this permission is unable to distinguish between HTTP connections, and some other TCP connection that may not be permitted. SocketPermissions also operate in terms of IP addresses, rather than host names. This limitation prevents access control given a specified host name, rather than a single IP address. In the case where more than one virtual server is being hosted at the same IP address, only one could be accessed using a SocketPermission, whereas many could be accessed if host name resolution were possible.

The java.net.URLPermission was introduced with the release of Java 8, solving some of the issues that were presented with the use of the SocketPermission. The java.net.URLPermission class is used to represent permission for access to a resource or set of resources that are defined by a specified URL. Along with the specification of a URL, the required HTTP access can be specified via user-definable setters and request headers. As such, the URLPermission class contains two constructors, one that accepts a string-based URL, and the other that accepts a string-based URL and a string of actions.

The URL that is passed to the URLPermission class can be in multiple formats, so the string may vary depending on what resource is being accessed. In its simplest format, the URL should use the following format:

scheme://authority/path
Eg.("http://www.apress.com/*")

The scheme of a URL is usually http or https, but it is not restricted. The authority can contain a number of details. At the minimum, the authority will be the host name of the remote resource. However, it could also contain user information, host range, and/or port range. If specified, the optional user information would be a privileged username followed by the @ character.

The host range is specified via a fully qualified DNS name, along with IPv4 address or IPv6 address portions, each separated by a (.). The host range DNS name may contain a wildcard character (*) to indicate portions of a DNS name. For example, you can use the following to indicate a host name that matches only the rightmost domain labels: "*.apress.com".

The port range portion of the URL is used to specify a single port number or a bounded or unbounded range of ports that the permission grants access against. Any invalid port number or range will result in the use of the default port of 80 for HTTP or 443 for HTTPS. You can also specify a wildcard (*) with the port range to indicate all ports.

The path portion of the URL is composed of a sequence of paths in the host server, separated by the / character. The path is very similar to the FilePermission, but it can be left blank to indicate permission for the root of the host resource. The path can be used to specify access to a single resource by listing that resource at the end. For example, for access to a single HTML file, you may specify www.mydns.com/mypage.html. The path may also contain the wildcard characters * and -. The * specifies all resources in the same directory, whereas the – specifies all resources recursively below the given path.

The second argument to URLPermission is an actions string. This string is a concatenation of an actions list and a request headers list. The actions string contains lists of permitted request methods and permitted request headers of the permission, respectively. The request method options are the HTTP request methods PUT, GET, and DELETE. The request headers are optional, but if present, they are separated from the request methods via a colon (:). No whitespace is permitted in the actions string and it is case-sensitive. You can also use a wildcard (*) to represent all request methods or headers, respectively. Here are a few examples:

· No request methods: "PUT,GET,DELETE"

· Request methods and request headers: "PUT,GET:MyHeader1,MyHeader2"

· All request methods: "*"

The URLPermission class provides a more restrictive level of security than SocketPermissions, as it is targeted more toward URL and host access. For sandboxed Rich Internet Applications, SocketPermissions for the origin host is no longer permitted in Java 8, which means that calls from JavaScript code to the application are not granted SocketPermissions as of Java 8. The Permissions attribute is now required in the JAR file manifest for the main JAR of all Rich Internet Applications at all levels.

Summary

There have been many new security features added or enhanced in Java 8. This chapter covers only a handful of the new features, including recipes on providing limited privileges for doPrivilege blocks, SNI for secure handshaking, and more control over sandboxed Rich Internet Application permissions. For more information about these new security features, refer to the online documentation so that your applications are using the most robust security options possible.