© Prabath Siriwardena 2020
P. SiriwardenaAdvanced API Securityhttps://doi.org/10.1007/978-1-4842-2050-4_3

3. Securing APIs with Transport Layer Security (TLS)

Prabath Siriwardena1 
(1)
San Jose, CA, USA
 

Securing APIs with Transport Layer Security (TLS) is the most common form of protection we see in any API deployment. If you are new to TLS, please check Appendix C first, which explains TLS in detail and how it works. In securing APIs, we use TLS to secure or encrypt the communication—or protect the data in transit—and also we use TLS mutual authentication to make sure only the legitimate clients can access the APIs.

In this chapter, we discuss how to deploy an API implemented in Java Spring Boot, enable TLS, and protect an API with mutual TLS.

Setting Up the Environment

In this section, we’ll see how we can develop an API using Spring Boot from scratch. Spring Boot (https://projects.spring.io/spring-boot/) is the most popular microservices development framework for Java developers. To be precise, Spring Boot offers an opinionated1 runtime for Spring, which takes out a lot of complexities. Even though Spring Boot is opinionated, it also gives developers to override many of its default picks. Due to the fact that many Java developers are familiar with Spring, and the ease of development is a key success factor in the microservices world, many adopted Spring Boot. Even for Java developers who are not using Spring, still it is a household name. If you have worked on Spring, you surely would have worried how painful it was to deal with large, chunky XML configuration files. Unlike Spring, Spring Boot believes in convention over configuration—no more XML hell! In this book, we use Spring Boot to implement our APIs. Even if you are not familiar with Java, you will be able to get started with no major learning curve, as we provide all the code examples.

To run the samples, you will need Java 8 or latest, Maven 3.2 or latest, and a git client. Once you are successfully done with the installation, run the following two commands in the command line to make sure everything is working fine. If you’d like some help in setting up Java or Maven, there are plenty of online resources out there.
>java -version
java version "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
>mvn -version
Apache Maven 3.5.0 (ff8f5e7444045639af65f6095c62210b5713f426; 2017-04-03T12:39:06-07:00)
Maven home: /usr/local/Cellar/maven/3.5.0/libexec
Java version: 1.8.0_121, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/jre Default locale: en_US, platform encoding: UTF-8 OS name: "mac os x", version: "10.12.6", arch: "x86_64", family: "mac
All the samples used in this book are available in the https://github.com/apisecurity/samples.git git repository. Use the following git command to clone it. All the samples related to this chapter are inside the directory ch03.
> git clone https://github.com/apisecurity/samples.git
> cd samples/ch03

To anyone who loves Maven, the best way to get started with a Spring Boot project would be with a Maven archetype. Unfortunately, it is no more supported. One option we have is to create a template project via https://start.spring.io/ –which is known as the Spring Initializer. There you can pick which type of project you want to create, project dependencies, give a name, and download a maven project as a zip file. The other option is to use the Spring Tool Suite (STS).2 It’s an IDE (integrated development environment) built on top of the Eclipse platform, with many useful plugins to create Spring projects. However, in this book, we provide you all the fully coded samples in the preceding git repository.

Note

If you find any issues in building or running the samples given in this book, please refer to the README file under the corresponding chapter in the git repository: https://github.com/apisecurity/samples.git. We will update the samples and the corresponding README files in the git repository, to reflect any changes happening, related to the tools, libraries, and frameworks used in this book.

Deploying Order API

This is the simplest API ever. You can find the code inside the directory ch03/sample01. To build the project with Maven, use the following command:
> cd sample01
> mvn clean install

Before we delve deep into the code, let’s have a look at some of the notable Maven dependencies and plugins added into ch03/sample01/pom.xml.

Spring Boot comes with different starter dependencies to integrate with different Spring modules. The spring-boot-starter-web dependency brings in Tomcat and Spring MVC and, does all the wiring between the components, making the developer’s work to a minimum. The spring-boot-starter-actuator dependency helps you monitor and manage your application.
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
In the pom.xml file, we also have the spring-boot-maven-plugin plugin, which lets you start the Spring Boot API from Maven itself.
<plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
Now let’s have a look at the checkOrderStatus method in the class file src/main/java/com/apress/ch03/sample01/service/OrderProcessing.java. This method accepts an order id and returns back the status of the order. There are three notable annotations used in the following code. The @RestController is a class-level annotation that marks the corresponding class as a REST endpoint, which accepts and produces JSON payloads. The @RequestMapping annotation can be defined both at the class level and the method level. The value attribute at the class-level annotation defines the path under which the corresponding endpoint is registered. The same at the method level appends to the class-level path. Anything defined within curly braces is a placeholder for any variable value in the path. For example, a GET request on /order/101 and /order/102 (where 101 and 102 are the order ids), both hit the method checkOrderStatus. In fact, the value of the value attribute is a URI template.3 The annotation @PathVariable extracts the provided variable from the URI template defined under the value attribute of the @RequestMapping annotation and binds it to the variable defined in the method signature.
@RestController
@RequestMapping(value = "/order")
public class OrderProcessing {
  @RequestMapping(value = "/{id}", method = RequestMethod.GET)
  public String checkOrderStatus(@PathVariable("id") String orderId)
  {
    return ResponseEntity.ok("{'status' : 'shipped'}");
  }
}
There is another important class file at src/main/java/com/apress/ch03/sample01/OrderProcessingApp.java worth having a look at. This is the class which spins up our API in its own application server, in this case the embedded Tomcat. By default the API starts on port 8080, and you can change the port by adding, say, for example, server.port=9000 to the sample01/src/main/resources/application.properties file. This will set the server port to 9000. The following shows the code snippet from OrderProcessingApp class, which spins up our API. The @SpringBootApplication annotation, which is defined at the class level, is being used as a shortcut for four other annotations defined in Spring: @Configuration, @EnableAutoConfiguration, @EnableWebMvc, and @ComponentScan.
@SpringBootApplication
public class OrderProcessingApp {
      public static void main(String[] args) {
             SpringApplication.run(OrderProcessingApp.class, args);
      }
}
Now, let’s see how to run our API and talk to it with a cURL client. The following command executed from ch03/sample01 directory shows how to start our Spring Boot application with Maven.
> mvn spring-boot:run
To test the API with a cURL client, use the following command from a different command console. It will print the output as shown in the following, after the initial command.
> curl http://localhost:8080/order/11
{"customer_id":"101021","order_id":"11","payment_method":{"card_type":"VISA","expiration":"01/22","name":"John Doe","billing_address":"201, 1st Street, San Jose, CA"},"items": [{"code":"101","qty":1},{"code":"103","qty":5}],"shipping_address":"201, 1st Street, San Jose, CA"}

Securing Order API with Transport Layer Security (TLS)

To enable Transport Layer Security (TLS), first we need to create a public/private key pair. The following command uses keytool that comes with the default Java distribution to generate a key pair and stores it in keystore.jks file. This file is also known as a keystore, and it can be in different formats. Two most popular formats are Java KeyStore (JKS) and PKCS#12. JKS is specific to Java, while PKCS#12 is a standard, which belongs to the family of standards defined under Public Key Cryptography Standards (PKCS). In the following command, we specify the keystore type with the storetype argument, which is set to JKS.
> keytool -genkey -alias spring -keyalg RSA -keysize 4096 -validity 3650 -dname "CN=foo,OU=bar,O=zee,L=sjc,S=ca,C=us" -keypass springboot -keystore keystore.jks -storeType jks -storepass springboot

The alias argument in the preceding command specifies how to identify the generated keys stored in the keystore. There can be multiple keys stored in a given keystore, and the value of the corresponding alias must be unique. Here we use spring as the alias. The validity argument specifies that the generated keys are only valid for 10 years or 3650 days. The keysize and keystore arguments specify the length of the generated key and the name of the keystore, where the keys are stored. The genkey is the option, which instructs the keytool to generate new keys; instead of genkey, you can also use genkeypair option. Once the preceding command is executed, it will create a keystore file called keystore.jks, which is protected with the password springboot.

The certificate created in this example is known as a self-signed certificate. In other words, there is no external certificate authority (CA). Typically, in a production deployment, either you will use a public certificate authority or an enterprise-level certificate authority to sign the public certificate, so any client, who trusts the certificate authority, can verify it. If you are using certificates to secure service-to-service communications in a microservices deployment or for an internal API deployment, then you need not worry about having a public certificate authority; you can have your own certificate authority. But for APIs, which you expose to external client applications, you would need to get your certificates signed by a public certificate authority.

To enable TLS for the Spring Boot API, copy the keystore file (keystore.jks), which we created earlier, to the home directory of the sample (e.g., ch03/sample01/) and add the following to the sample01/src/main/resources/application.properties file. The samples that you download from the samples git repository already have these values (and you only need to uncomment them), and we are using springboot as the password for both the keystore and the private key.
server.ssl.key-store: keystore.jks
server.ssl.key-store-password: springboot
server.ssl.keyAlias: spring
To validate that everything works fine, use the following command from ch03/sample01/ directory to spin up the Order API and notice the line which prints the HTTPS port.
> mvn spring-boot:run
Tomcat started on port(s): 8080 (https) with context path "
To test the API with a cURL client, use the following command from a different command console. It will print the output as shown in the following, after the initial command. Instead of HTTP, we are using HTTPS here.
> curl –k https://localhost:8080/order/11
{"customer_id":"101021","order_id":"11","payment_method":{"card_type":"VISA","expiration":"01/22","name":"John Doe","billing_address":"201, 1st Street, San Jose, CA"},"items": [{"code":"101","qty":1},{"code":"103","qty":5}],"shipping_address":"201, 1st Street, San Jose, CA"}
We used the -k option in the preceding cURL command. Since we have a self-signed (untrusted) certificate to secure our HTTPS endpoint, we need to pass the –k parameter to advise cURL to ignore the trust validation. In a production deployment with proper certificate authority–signed certificates, you do not need to do that. Also, if you have a self-signed certificate, you can still avoid using –k, by pointing cURL to the corresponding public certificate.
  • > curl --cacert ca.crt https://localhost:8080/order/11
You can use the following keytool command from ch03/sample01/ to export the public certificate of the Order API to ca.crt file in PEM (with the -rfc argument) format.
  • > keytool -export -file ca.crt -alias spring –rfc -keystore keystore.jks -storePass springboot
The preceding curl command with the ca.crt will result in the following error. It complains that the common name in the public certificate of the Order API, which is foo, does not match with the hostname (localhost) in the cURL command.
  • curl: (51) SSL: certificate subject name 'foo' does not match target host name 'localhost'
Ideally in a production deployment when you create a certificate, its common name should match the hostname. In this case, since we do not have a Domain Name Service (DNS) entry for the foo hostname, you can use the following workaround, with cURL.
  • > curl --cacert ca.crt https://foo:8080/order/11 --resolve foo:8080:127.0.0.1

Protecting Order API with Mutual TLS

In this section, we’ll see how to enable TLS mutual authentication between the Order API and the cURL client. In most of the cases, TLS mutual authentication is used to enable system-to-system authentication. First make sure that we have the keystore at sample01/keystore.jks, and then to enable TLS mutual authentication, uncomment the following property in the sample01/src/main/resources/application.properties file.
server.ssl.client-auth:need
Now we can test the flow by invoking the Order API using cURL. First, use the following command from ch03/sample01/ directory to spin up the Order API and notice the line which prints the HTTPS port.
> mvn spring-boot:run
Tomcat started on port(s): 8080 (https) with context path ''
To test the API with a cURL client, use the following command from a different command console.
> curl –k https://localhost:8080/order/11
Since we have protected the API with TLS mutual authentication, the preceding command will result in the following error message, which means the API (or the server) has refused to connect with the cURL client, because it didn’t present a valid client certificate.
curl: (35) error:1401E412:SSL routines:CONNECT_CR_FINISHED:sslv3 alert bad certificate

To fix this, we need to create a key pair (a public key and a private key) for the cURL client and configure Order API to trust the public key. Then we can use the key pair we generated along with the cURL command to access the API, which is protected with mutual TLS.

To generate a private key and a public key for the cURL client, we use the following OpenSSL command. OpenSSL is a commercial-grade toolkit and cryptographic library for TLS and available for multiple platforms. You can download and set up the distribution that fits your platform from www.openssl.org/source. If not, the easiest way is to use an OpenSSL Docker image. In the next section, we discuss how to run OpenSSL as a Docker container.
> openssl genrsa -out privkey.pem 4096
Now, to generate a self-signed certificate, corresponding to the preceding private key (privkey.pem), use the following OpenSSL command.
> openssl req -key privkey.pem -new -x509 -sha256 -nodes -out client.crt -subj "/C=us/ST=ca/L=sjc/O=zee/OU=bar/CN=client"
Let’s take down the Order API, if it is still running, and import the public certificate (client.crt) we created in the preceding step to sample01/keystore.jks, using the following command.
> keytool -import -file client.crt -alias client -keystore keystore.jks -storepass springboot
Now we can test the flow by invoking the Order API using cURL. First, use the following command from ch03/sample01/ directory to spin up the Order API.
> mvn spring-boot:run
Tomcat started on port(s): 8080 (https) with context path ''
To test the API with a cURL client, use the following command from a different command console.
> curl -k --key privkey.pem --cert client.crt https://localhost:8080/order/11
In case we use a key pair, which is not known to the Order API, or in other words not imported into the sample01/keystore.jks file, you will see the following error, when you execute the preceding cURL command.
curl: (35) error:1401E416:SSL routines:CONNECT_CR_FINISHED:sslv3 alert certificate unknown

Running OpenSSL on Docker

In the last few years, Docker revolutionized the way we distribute software. Docker provides a containerized environment to run software in self-contained manner. A complete overview of Docker is out of the scope of this book—and if you are interested in learning more, we recommend you check out the book Docker in Action (Manning Publications, 2019) by Jeff Nickoloff and Stephen Kuenzli.

Setting up Docker in your local machine is quite straightforward, following the steps in Docker documentation available at https://docs.docker.com/install/. Once you get Docker installed, run the following command to verify the installation, and it will show the version of Docker engine client and server.
> docker version
To start OpenSSL as a Docker container, use the following command from the ch03/sample01 directory.
> docker run -it -v $(pwd):/export prabath/openssl
#

When you run the preceding command for the first time, it will take a couple of minutes to execute and ends with a command prompt, where you can execute your OpenSSL commands to create the keys, which we used toward the end of the previous sections. The preceding docker run command starts OpenSSL in a Docker container, with a volume mount, which maps ch03/sample01 (or the current directory, which is indicated by $(pwd) in the preceding command) directory from the host file system to the /export directory of the container file system. This volume mount helps you to share part of the host file system with the container file system. When the OpenSSL container generates certificates, those are written to the /export directory of the container file system. Since we have a volume mount, everything inside the /export directory of the container file system is also accessible from the ch03/sample01 directory of the host file system.

To generate a private key and a public key for the cURL client, we use the following OpenSSL command.
# openssl genrsa -out /export/privkey.pem 4096
Now, to generate a self-signed certificate, corresponding to the preceding private key (privkey.pem), use the following OpenSSL command.
# openssl req -key /export/privkey.pem -new -x509 -sha256 -nodes -out client.crt -subj "/C=us/ST=ca/L=sjc/O=zee/OU=bar/CN=client"

Summary

  • Transport Layer Security (TLS) is fundamental in securing any API.

  • Securing APIs with TLS is the most common form of protection we see in any API deployment.

  • TLS protects data in transit for confidentiality and integrity, and mutual TLS (mTLS) protects your APIs from intruders by enforcing client authentication.

  • OpenSSL is a commercial-grade toolkit and cryptographic library for TLS and available for multiple platforms.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.47.253