AWS Database Blog

Create an SSL connection to Amazon RDS for Db2 in Java without KeyStore or Keytool

Connecting to a database over SSL is a security best practice, but it often comes with complexity. In many Java applications, enabling SSL means adjusting the keytool utility, creating Java KeyStores, and managing trust stores. However, if you’re connecting to IBM Db2, such as an Amazon Relational Database Service (Amazon RDS) for Db2 instance, there’s a simpler way.

In this post, we show you a straightforward approach to connect to a Db2 database using SSL in Java, without needing a trust store or invoking keytool. This solution offers the following benefits:

  • Simplicity – You can avoid dealing with keytool, KeyStore creation, and certificate conversions
  • Automation-friendly – The solution is ideal for containerized environments or continuous integration and delivery (CI/CD) pipelines
  • Security – It still performs proper TLS 1.2 negotiation and server certificate validation

Solution overview

Instead of using a traditional Java TrustStore, the IBM JDBC driver supports the following configuration:

properties.put("sslCertLocation", "/path/to/certchain.pem");

This tells the driver to use a PEM-formatted server certificate directly—you don’t need to convert it or import it into a .jks file.

We also specify the JDBC driver connection properties.

sslConnection=true
sslVersion=TLSv1.2

The above driver properties make the connection encrypted and uses a secure protocol.

This solution is ideal for environments like AWS where Amazon RDS provides a PEM-formatted certificate bundle.

We tested this solution with the following configuration:

  • IBM Db2 JDBC Driver: db2jcc4.jar v4.33.31
  • Java 11+
  • A PEM certificate from Amazon RDS
  • Check the code in GitHub repo.

Prerequisites

We assume you already have the following resources:

  • An Amazon RDS for Db2 server instance with SSL enabled
  • A certificate chain PEM file such as AWS us-east-1-bundle.pem, which you can download from AWS for a specific region.
  • A recent IBM Data Server Driver (db2jcc4.jar version 4.33 or later)
  • Java 8+ (with TLS 1.2 support)

The Java program

The following code is the full source of a Java program (Db2SSLTest.java) that connects to Amazon RDS for Db2 using SSL:

import java.sql.*;
import java.util.Properties;
public class Db2SSLTest {
  public static void main(String[] args) {
    if (args.length != 6) {
      System.out.println("Usage: java Db2SSLTest " +
        " <certchain.pem> " +
        "  <hostname> <port> <database> <userid> <password>");
      System.exit(1);
    }
    Properties properties = new Properties();
    String certPath = args[0];
    String hostname = args[1];
    String port = args[2];
    String database = args[3];
    String userid = args[4];
    String password = args[5];
    properties.put("sslConnection", "true");
    properties.put("sslVersion", "TLSv1.2");
    properties.put("sslCertLocation", certPath);
    properties.put("user", userid);
    properties.put("password", password); 
    String url = "jdbc:db2://" + hostname + ":" + 
        port + "/" + database;
    try {
      Class.forName("com.ibm.db2.jcc.DB2Driver");
      Connection conn = DriverManager.getConnection(url, 
                          properties);
      Statement stmt = conn.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT CURRENT " +
          " TIMESTAMP " +
          " FROM SYSIBM.SYSDUMMY1");
      if (rs.next()) {
        System.out.println("SSL Connection successful!");
        System.out.println("Current timestamp: " + 
           rs.getString(1));
      }
      rs.close();
      stmt.close();
      conn.close();
    } catch (Exception e) {
      System.err.println("Error: " + e.getMessage());
      e.printStackTrace();
    }
  }
}

Compile and run

Assuming your IBM JDBC driver is located in ~/sqllib/java/db2jcc4.jar, the following code illustrates how to compile and run:

#!/usr/bin/env bash
#
# Retrieves the master user password for a specified DB instance.
# 
# This function attempts to obtain the master user password for the provided
# DB instance ID. It first checks if the password can be retrieved from the 
# AWS Secrets Manager. If a valid secret is not found, it prompts the user 
# to manually enter the password.
# 
# Args:
#     DB_INSTANCE_ID (str): The database instance identifier.
# 
# Environment Variables:
#     REGION: The AWS region where the DB instance is located.
# 
# Exports:
#     MASTER_USER_PASSWORD: The retrieved or entered master user password.
# 
# Returns:
#     int: Returns 1 if the password retrieval fails, otherwise 0.
get_master_password() {
  DB_INSTANCE_ID=$1
  SECRET_ARN=$(aws rds describe-db-instances \
  --db-instance-identifier "$DB_INSTANCE_ID" \
  --region $REGION \
  --query "DBInstances[0].MasterUserSecret.SecretArn" \
  --output text)
  if [[ -z "$SECRET_ARN" || "$SECRET_ARN" == "None" ]]; then
    read -rsp "Enter Master User password: " MASTER_USER_PASSWORD
    echo
  else
    SECRET_JSON=$(aws secretsmanager get-secret-value \
      --secret-id "$SECRET_ARN" \
      --query "SecretString" \
      --region $REGION \
      --output text)
    MASTER_USER_PASSWORD=$(jq -r '.password' <<< "$SECRET_JSON") 
    if [[ -z "$MASTER_USER_PASSWORD" ]]; then
      echo "Failed to get password from secret manager '$SECRET_ARN'. Exiting..."
      return 1
    fi
    export MASTER_USER_PASSWORD=$MASTER_USER_PASSWORD
  fi
}
# Retrieves the master user name for a specified DB instance.
#
# This function queries AWS RDS to obtain the master user name for the provided
# DB instance identifier. If the master user name is not found, it returns an
# error message.
#
# Environment Variables:
#     DB_INSTANCE_IDENTIFIER: The database instance identifier.
#     REGION: The AWS region where the DB instance is located.
#
# Exports:
#     MASTER_USER_NAME: The retrieved master user name.
#
# Returns:
#     int: Returns 1 if the master user name is not found, otherwise 0.
get_master_user_name() {
  local master_user_name=($(aws rds describe-db-instances \
    --db-instance-identifier "$DB_INSTANCE_IDENTIFIER" \
    --region $REGION \
    --query "DBInstances[0].MasterUsername" \
    --output text))
  if [ "$master_user_name" = "None" ]; then
    echo "Not found"
    return 1
  else
    export MASTER_USER_NAME=$master_user_name
  fi
}
# Retrieves the database address for a specified DB instance.
#
# This function queries AWS RDS to obtain the database endpoint address for the
# provided DB instance identifier. If the address is not found, it returns an
# error message.
#
# Environment Variables:
#     DB_INSTANCE_IDENTIFIER: The database instance identifier.
#     REGION: The AWS region where the DB instance is located.
#
# Exports:
#     DB_ADDRESS: The retrieved database endpoint address.
#
# Returns:
#     int: Returns 1 if the database address is not found, otherwise 0.
get_db_address() {
  local db_address=($(aws rds describe-db-instances \
    --db-instance-identifier "$DB_INSTANCE_IDENTIFIER" \
    --region $REGION \
    --query "DBInstances[0].Endpoint.Address" \
    --output text))
  if [ -z "$db_address" ]; then
    echo "Not found"
    return 1
  else
    export DB_ADDRESS=$db_address
  fi
}
# Retrieves the SSL port number for a specified DB instance.
#
# This function queries AWS RDS to obtain the parameter group name associated
# with the provided DB instance identifier, and then queries the parameter
# group to obtain the SSL port number. If the SSL port is not found, it returns
# an error message.
#
# Environment Variables:
#     DB_INSTANCE_IDENTIFIER: The database instance identifier.
#     REGION: The AWS region where the DB instance is located.
#
# Exports:
#     SSL_PORT: The retrieved SSL port number.
#
# Returns:
#     int: Returns 1 if the SSL port is not found, otherwise 0.
get_ssl_port() {
  SSL_PORT=""
  DB_PARAM_GROUP_NAME=$(aws rds describe-db-instances \
      --db-instance-identifier "$DB_INSTANCE_IDENTIFIER" \
      --region $REGION \
      --query "DBInstances[0].DBParameterGroups[0].DBParameterGroupName" \
      --output text)
  if [ "$DB_PARAM_GROUP_NAME" != "" ]; then
    SSL_PORT=$(aws rds describe-db-parameters \
        --db-parameter-group-name "$DB_PARAM_GROUP_NAME" \
        --region $REGION \
        --query "Parameters[?ParameterName=='ssl_svcename'].ParameterValue" \
        --output text)
    if [ "$SSL_PORT" = "None" ]; then
      SSL_PORT=""
      return 1
    fi
  fi
  export SSL_PORT=$SSL_PORT
  return 0
}
# Main entry point for the script.
#
# This function compiles a Java program, downloads the SSL certificate, retrieves
# the master user name, master password, database address, and SSL port from AWS
# RDS, and then runs the Java program with the retrieved parameters.
#
# Exports:
#     None
#
# Returns:
#     int: Returns 0 if the program runs successfully, otherwise 1.
main () {
  DB_INSTANCE_IDENTIFIER="viz-demo"
  CL_PATH=.:$HOME/sqllib/java/db2jcc4.jar
  REGION="us-east-1"
  PROG_NAME=Db2SSLTest
  JAVA_FILE=${PROG_NAME}.java
  DBNAME="TEST"
  if ! command -v javac &>/dev/null; then
    echo "javac is not installed. Please install Java Development Kit (JDK) to compile Java programs."
    exit 1
  fi
  echo "Compile Java program $JAVA_FILE"
  javac -cp $CL_PATH $JAVA_FILE
  echo "Downloading SSL certificate..."
  CERTCHAIN="/home/db2inst1/us-east-1-bundle.pem"
  if [ -f "$CERTCHAIN" ]; then
    echo "Certificate already exists. Skipping download."
  else
    echo "Certificate does not exist. Downloading..."
    if ! curl -sL "https://truststore.pki.rds.amazonaws.com/us-east-1/$REGION-bundle.pem" -o $REGION-bundle.pem; then
      echo "Failed to download SSL certificate. Please check your network connection or the URL."
      exit 1
    fi
  fi
  if get_master_user_name "$DB_INSTANCE_IDENTIFIER"; then
    echo "Master user name: $MASTER_USER_NAME"
    USER="$MASTER_USER_NAME"
  else
    echo "Failed to retrieve master user name. Exiting..."
    exit 1
  fi
  if get_master_password "$DB_INSTANCE_IDENTIFIER"; then
    PASSWORD=$MASTER_USER_PASSWORD
  else
    echo "Failed to retrieve master password. Exiting..."
    exit 1
  fi
  if get_db_address "$DB_INSTANCE_IDENTIFIER"; then
    echo "DB Address: $DB_ADDRESS"
    HOST="$DB_ADDRESS"
  else
    echo "Failed to retrieve DB address. Exiting..."
    exit 1
  fi
  if get_ssl_port "$DB_INSTANCE_IDENTIFIER"; then
    echo "SSL Port: $SSL_PORT"
    PORT="$SSL_PORT"
  else
    echo "Failed to retrieve SSL port. Exiting..."
    exit 1
  fi
  # Use -Djavax.net.debug=ssl:handshake:verbose to debug SSL issues
  echo "Running Java program..."
  java \
  -cp "$CL_PATH" $PROG_NAME $CERTCHAIN $HOST $PORT $DBNAME $USER $PASSWORD
}
main "$@"

If all goes well, you will get output similar to the following:

SSL Connection successful!
Current timestamp: 2025-07-21 18:30:14.675317

You can download the Java program and shell script from the Github Repository.

Considerations

If your certificate chain file has certificates for all AWS Regions such as global-bundle.pem, using JDBC connection property sslCertLocation to /path/to/global-bundle.pem will not work because of a limitation of the JDBC driver (at the time of writing). When you are working with an application that connects only to one Region, use a Region-specific certificate file such as us-east-1-bundle.pem. If you have an absolute need to use global-bundle.pem, use keytool and store the certificates in a keystore that the JDBC driver can extract that it needs.

Troubleshooting

The following are potential solutions to common issues:

  • Failing SSL connection – If your SSL connection is still failing even after enabling the SSL connection in RDS for Db2 instance, you must restart your RDS for Db2 instance, because SSL connection enablement happens only after the restart of the Db2 instance.
  • Unable to locate the db2jcc4.jar file – The db2jcc4.jar file is part of various IBM Db2 clients such as data server client, runtime client, or the server package. Refer to Connect to Amazon RDS for Db2 using AWS CloudShell for more information about how to install the Db2 client with an automatic catalog of the RDS for Db2 databases.
  • Unable to connect to the RDS database – If you can’t connect to the RDS database after cataloging a database using SSL through the use of db2cli commands, you might have an existing database connection that doesn’t have the information of the new recently cataloged database. Use command db2 terminate and test the connection again.

Conclusion

In this post, we discussed how you don’t always need the Java KeyStore or keytool to make SSL work. If you have a PEM certificate and a modern JDBC driver, you can connect securely with minimal setup. This approach is especially valuable for developers looking for rapid SSL testing, automated environments (such as CI/CD and containers), and simplifying secure Java DB2 connectivity.

Have you tried this approach in your setup? Let us know how it works for you or if you’d like to see a mutual TLS (client certificate) version next.


About the authors

Vikram S Khatri

Vikram S Khatri

Vikram is a Sr. DBE for Amazon RDS for Db2. Vikram has over 20 years of experience in Db2. He enjoys developing new products from the ground up. In his spare time, he practices meditation and enjoys listening to podcasts.

Sumit Kumar

Sumit Kumar

Sumit is a Senior Solutions Architect at AWS, and enjoys solving complex problems. He has been helping customers across various industries build and design their workloads on the AWS Cloud. He enjoys cooking, playing chess, and spending time with his family.

Ashish Saraswat

Ashish Saraswat

Ashish is Sr. Software Development Engineer for Amazon RDS for Db2. Ashish has over 10 years of software development experience.

Amine Yahsine

Amine Yahsine

Amine is a Software Developer Engineer with Amazon RDS for Db2. Amine has over 2 years of software development experience.