How to secure your REST service? Part 3 – Accessing SSL-secure service via cURL

If you access the HTTPS url via cURL, you will get the below error. This is curl’s way of warning you that the server certificate is self-signed and could not be validated against it’s ‘CA bundle’ (list of pre-downloaded certificates that are safe).

curl: (60) SSL certificate problem: self signed certificate
 More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
 If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
 If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

In our case since it is a self-signed certificate we created for manual purposes, we need to let cUrl know that is ok to communicate with this URL. There are 2 ways to do it:

  1. Using curl -k option to connect to the SSL site without certificate. Of course, this in not safe.
  2. Add our self-signed certificate to the default CA certificate bundle. Curl’s default CA bundle is stored in a file called ca-bundle.crt. If you don’t find it in your machine, there are couple of ways to get it.

(A) Download the generic version from curl website.
curl http://curl.haxx.se/ca/cacert.pem -o C:\Fig\ca-bundle.crt
(B) If you are not comfortable using the generic version, you can download the source code for curl from github and generate it locally as follows: (Reference: https://gist.github.com/jjb/996292)

git clone https://github.com/bagder/curl.git
cd curl/lib

//edit mk-ca-bundle.pl and change http to https in the below line:
my $url = 'http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1';

perl ./mk-ca-bundle.pl //creates the ca-bundle.crt file.

Before appending our certificate to the above ca-bundle.crt file, we need to first download the self-signed certificate from the server.
(1) Downloading server certificate locally

(a) From browser you can save the certificate directly in a .cer file. This file needs to be converted to PEM format using

C:\OpenSSL-Win64\bin\openssl x509 -inform DES -in c:\figserver.cer -out C:\figservercert.pem -text

(b) Via OpenSSL: enter below command with your own server name and HTTPS port. After the certificate details are printed on the console, type QUIT.

C:\apps\OpenSSL-Win64\bin\openssl s_client -connect localhost:8443]
(2) Copy & save the content between the BEGIN & END CERTIFICATE blocks (including BEGIN & END) and append it to ca-bundle.crt file.

Run the curl command now pointing to the local ca-bundle.crt file and access the SSL site.
curl -D- --cacert ./ca-bundle.crt -H "Authorization: Basic Zml6YWxhZG1pbjpmaXphbGFkbWlu" -X GET https://localhost:8443/Fig-0.0.1/fig/task/z4

How to secure your REST service? Part 2 – Using Certificates for encryption

As we saw in the previous article, a simple BASIC authentication over HTTP can protect your REST web service with a username and password. But since they are transmitted over the wire as Base64 encoded text, anybody could misuse it. Using encryption techniques like SSL, the data being sent on wire could be encrypted.

SSL stands for Secure Socket Layer, and it’s a protocol that does two things:

  1. Encrypts your data, which means no hacker can see what your browser sends to the server nor what the server sends to the browser.
  2. Authenticates your website, which means it tells your browser “This website really is who it claims to be.” For example, that when you type your username and password into your PayPal account, that the website really is PayPal, and not a hacker posing as PayPal.

HTTPS just means “HTTP with SSL.” Just as http:// means “this is a website,” seeing https:// means “this is a website, and it’s using SSL to encrypt data and authenticate the website.

In order to enable SSL, we need a certificate which is either self-signed or signed by an external Certificate Authority like Verisign, Thawte, etc.. For testing purposes, let us create a self-signed certificate. For more details on SSL & Certificates, check Tomcat SSL Configuration How-To.

How to create a self-signed certificate?

"%JAVA_HOME%\bin"\keytool -genkeypair -alias fig -keyalg RSA -keypass fig_store_pass -storepass fig_store_pass -keystore C:\Fig\fig.keystore

After answering a few more questions about your name, organization, etc., the self-signed certificate named ‘fig.keystore’ is created.

Note that the keypass and storepass passwords should be same. Otherwise, you get the following error “java.io.IOException: Cannot recover key“.

Verify if the certificate is created properly by this command.

"%JAVA_HOME%\bin"\keytool -list -keystore C:\Fig\fig.keystore

<Enter storepass password from above command to list entries in the given keystore>

How to enable SSL in Tomcat?

Uncomment the below line in $TOMCAT_HOME/conf/server.xml and add the keystore file location and password.

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
 maxThreads="150" scheme="https" secure="true"
 clientAuth="false" sslProtocol="TLS" 
 keystoreFile="C:\Fig\fig.keystore"
 keystorePass="fig_store_pass" />

Also change the <transport-guarantee> value in web.xml to CONFIDENTIAL.

Now if you access the url in a browser, you will be asked for username and password. This is because of the BASIC authentication enabled in Tomcat. You may notice a crossed icon appear before the https protocol. This is because the server runs with a self-signed certificate and browser is warning the client that the server may not be whom it claims it is. In production environment, you should consider getting a signed certificate from trusted Certificate Authorities like Verisign or sign it with your own CA server.

Even if you try accessing the url via http protocol, you will be automatically redirected to https port.

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

Pros & Cons

(+): Using certificates is the most secure way of communicating between client and server.
(-): Involves more work to create and set up certificates and at times expensive. User credentials are stored in the server in plain text which could be potentially compromised.

How to secure your REST service? Part 1 – Using BASIC Authentication

In this series of articles, I will be explaining in detail about securing REST services. It includes configuring authentication, authorization and encryption. To begin with, this article talks about setting up BASIC authentication for a simple REST service running on Apache Tomcat.

Authentication is a process of validating the identity of a client who is trying to access the service. There are different authenticating a client, and the simplest protocol is the BASIC authentication over HTTP.

Steps involved in BASIC authentication

(1) Client sends a request without authentication details. e.g.,

Request: curl -D- -X GET localhost:8080/Fig-0.0.1/fig/task/123

(2) Server responds back with error code 401 which denotes unauthorized request. e.g.,

 HTTP/1.1 401 Unauthorized
 Server: Apache-Coyote/1.1
 WWW-Authenticate: Basic realm="jaxrs"
 Content-Type: text/html;charset=utf-8
 Content-Length: 951
 Date: Thu, 05 Dec 2013 00:12:37 GMT

(3) Client sends Base64-encoded username & password in request. e.g.,

curl -D- -H "Authorization: Basic bXl1c2VybmFtZTpteXBhc3Njb2Rl" -X GET localhost:8080/Fig-0.0.1/fig/task/123

(4) Server receives the request, decodes the credentials and compares it with the server version. If the credentials match, then it responds back with status code 200.

 HTTP/1.1 200 OK
 Server: Apache-Coyote/1.1
 Cache-Control: private
 Expires: Wed, 31 Dec 1969 19:00:00 EST
 Content-Type: text/plain
 Content-Length: 325
 Date: Thu, 05 Dec 2013 05:39:46 GMT

Steps to enable BASIC authentication in Apache Tomcat

How to add users, roles and passwords?

Under your tomcat installation, look for $TOMCAT_HOME/conf/tomcat-users.xml file. Add the below lines which defines 2 users and assigns specific roles to them.

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
   <role rolename="admin"/>
   <role rolename="developer"/>
   <user username="dev1" password="devpassword1" roles="developer"/>
   <user username="admin1" password="adminpassword1" roles="admin"/>
</tomcat-users>

How to enable BASIC authentication in Tomcat?

Now that the users and roles are added, let us activate the authentication in our web application. Open your application’s web.xml and add the following.

  <security-constraint>
    <!-- Specifies the part of the application to be authenticated --> 
    <web-resource-collection>
       <web-resource-name>Secure Task Services</web-resource-name>

       <!-- Denotes which URI patterns needs to be protected. -->
       <url-pattern>/task/*</url-pattern>

       <!-- Only POST, PUT & DELETE calls are authenticated. Omitting http-method tag 
       altogether will secure all access to the url-pattern above -->
       <http-method>POST</http-method>
       <http-method>PUT</http-method>
       <http-method>DELETE</http-method>
    </web-resource-collection>

    <!-- Specifies which roles defined in tomcat-users.xml have access to the resources. -->
    <auth-constraint>
       <role-name>admin</role-name>
    </auth-constraint>

    <!-- Requests are processed without encryption -->
    <user-data-constraint>
       <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
 </security-constraint>

 <!-- Denotes the authentication method, which in our case is BASIC -->
 <login-config>
    <auth-method>BASIC</auth-method>
 </login-config>

That’s about it. If you bounce the Tomcat and try to access the resources via POST/PUT/DELETE method, you will be access for credentials.

Advantages of BASIC authentication: Very easy to set up authentication.
Disadvantages of BASIC authentication: Username/password can be easily sniffed out by attackers since it is just encoded, and not encrypted.

Encrypted HTTP Request

Using an encrypted HTTP connection like HTTPS where packets are encrypted with SSL solves the downside of BASIC authentication. To enable HTTPS, uncomment the below lines in $TOMCAT_HOME/conf/server.xml .

<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
 maxThreads="150" scheme="https" secure="true"
 clientAuth="false" sslProtocol="TLS" />

and change the <transport-guanrantee> value in web.xml to CONFIDENTIAL instead of NONE.

How to write a simple RESTful web service using Jersey (Part 3)?

In the previous 2 parts, we saw how to read something via RESTful web service. Here we are going to see how to pass in data from client to the web service? In our “Hello, World” example, what if I want to display a custom greeting instead of a “Hello”?

Create the method that takes in user input parameter.

@GET
@Produces({MediaType.TEXT_PLAIN})
@Path("/plain")
public String getPlain(@DefaultValue("Hello") @QueryParam("greeting") String greeting) {
	return greeting + " World!!!";
}

That’s it. Now try adding custom greeting to your local URL as shown below:
http://localhost:8080/PlayArea-1.0/helloworldapp/helloworldresources/plain?greeting=Welcome

As you can see, the custom parameter “greeting” is passed in via the URL as “?greeting=Welcome” (Multiple parameters can be passed in separated by &). On the server side, the method which processes this request takes in a method parameter annotated with @QueryParam(“greeting”). JAX-RS binds the query parameter value from URL to this method parameter variable. A default value can be assigned to this parameter via @DefaultValue annotation.

Using HTTP, there are several ways to pass data from client to the service. When to use what is a separate topic for another day. For now, let us stick the example above which uses technique #1.

  1. via URL query parameter
  2. via URL path
  3. via HTTP Header
  4. via HTTP POST body
  5. via cookie

JAX-RS provides various mechanisms to process the data from client requests in your service. Here are few:

  1. @QueryParam – to read query parameters from URL
  2. @HeaderParam – to read data from HTTP header
  3. @FormParam – to read data from HTTP POST form
  4. @PathParam – to read from the URL path
  5. @MatrixParam – to read from the URI matrix values
  6. @CookieParam – to read cookie data sent from the client
  7. @Context UriInfo – to get all the URI related information via a single object
  8. @Context HttpHeaders – to read all the header information via a single object
  9. @BeanParam – to read data from one of the aforementioned ways and bind it to a custom bean.
  10. MultivaluedMap<String, String> formParams – to read all the form parameters as a map without using annotations

How to write a simple RESTful web service using Jersey (Part 2)?

In Part 1, we saw how to create a simple REST service that returns “Hello World!!!” as a plain text. In this part, we are going to see how to transform a Java object into XML/JSON based on user request.

Prerequisites

  • Familiarity with the code in Part 1
  • Include the Jackson library for JSON tranformation
 'com.fasterxml.jackson.core:jackson-core:2.2.3'
 'com.fasterxml.jackson.core:jackson-databind:2.2.3'

Step 1: Create a domain/POJO Java class named “HelloWorld.java”
Description: Please note that the POJO class is annotated with JAX-B annotation. JAX-B helps transform the Java class to XML by linking a Java type like String to XML type, in this case xsd:string.

package webservices.ch01;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "greet")
public class HelloWorld {
    private String greeting = "Hello";
    private String receiver = "World";

    public HelloWorld() {
    }

    public HelloWorld(String greeting, String receiver) {
        this.greeting = greeting;
        this.receiver = receiver;
    }

    public String getGreeting() { return greeting; }

    public void setGreeting(String greeting) { this.greeting = greeting; }

    public String getReceiver() { return receiver; }

    public void setReceiver(String receiver) { this.receiver = receiver; }

    @Override
    public String toString() {
        return greeting + " " + receiver + "!!!";
    }
}

Step 2: Add getXML() method in HelloWorldResources.java 
Description: Using JAXB, the HelloWorld object is transformed into an XML document. Note here the return data format is annotated as MediaType.TEXT_XML, and the method return type is JAXBElement.

@GET
@Produces({MediaType.TEXT_XML})
@Path("/xml")
public JAXBElement<HelloWorld> getXML() {
	return new JAXBElement<>(new QName("helloworldxml"), HelloWorld.class, new HelloWorld()) ;
}

Step 3: Add getJson() method in HelloWorldResources.java
Description: Using the Jackson library, a new HelloWorld is converted into a JSON string. Return data format is annotated as MediaType.APPLICATION_JSON.

@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/json")
public String getJson() throws JsonProcessingException {
	final HelloWorld helloWorld = new HelloWorld("Hi", "Buddy");
	return new ObjectMapper().writeValueAsString(helloWorld);
}

Step 4: Try the URLs now

URL: http://localhost:8080/PlayArea-1.0/helloworldapp/helloworldresources/xml

XML Output:

<helloworldxml>
   <greeting>Hello</greeting>
   <receiver>World</receiver>
</helloworldxml>

URL: http://localhost:8080/PlayArea-1.0/helloworldapp/helloworldresources/json

JSON Output:

{"greeting":"Hi","receiver":"Buddy"}

(Source code for the above can be found in https://github.com/fizalihsan/PlayArea/)

How to write a simple RESTful web service using Jersey (Part 1)?

In this series, I’m going to show you how to write simple RESTful web services using the JAX-RS reference implementation named Jersey (and other implementations of JAX-RS, if time permits).

In part 1, we are going to see how to write a “Hello World” RESTful service that just returns “Hello World!!!” in plain text.

As a prerequisite, you need the below before executing it in your local PC.

  • A servlet container (like Tomcat) or a Java Application Server (like Glassfish) is up and running in your machine under port 8080.
  • Download following libraries from Jersey homepage  and copy under /WEB-INF/lib

‘javax.ws.rs:javax.ws.rs-api:2.0’
‘org.glassfish.jersey.containers:jersey-container-servlet:2.4’
‘org.glassfish.jersey.core:jersey-server:2.4’

Step 1: Add the below lines in web.xml

Description: By adding the below lines, Jersey ServletContains scans for implementation of Jersey Application classes which is shown in step 2.

<servlet>
	<servlet-name>jersey</servlet-name>
	<servlet-class> com.glassfish.jersey.servlet.ServletContainer </servlet-class>
	<!-- load an instance of the Jersey ServletContainer during the WAR bootstrap process; the critical role of the
	ServletContainer is to scan the deployed WAR file for Jersey Application classes. -->
	<load-on-startup>1</load-on-startup>
</servlet>

Step 2: Implement the Jersey Application class

Description: Method getClasses() iterates through the list of resource classes found. In our case, there is only one such class called “HelloWorldResources”.

package webservices;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

@ApplicationPath("/helloworldapp")
public class HelloWorldApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> set = new HashSet<>();
        set.add(HelloWorldResources.class);
        return set;
    }
}

Step 3: Implement the class that serves the resources

Description: @Path annotation at class level says that all the resources in this class is accessed by prefixing the path “/helloworldresources”.

The method getPlain() serves a resource by the name “/plain” in data format “text/plain” via HTTP GET request.

package webservices;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/helloworldresources")
public class HelloWorldResources {
    @GET
    @Produces({MediaType.TEXT_PLAIN})
    @Path("/plain")
    public String getPlain() {
        return "Hello World!!!";
    }
}

Step 4: Create WAR file and deploy

Create WAR file (named PlayTest-1.0.war) and deploy in the server. Now enter the below URL in your browser, and voila, “Hello World!!!” is displayed in plain text.

http://localhost:8080/PlayArea-1.0/helloworldapp/helloworldresources/plain

(Source code for the above can be found in https://github.com/fizalihsan/PlayArea/)