Extremely Serious

Category: Security (Page 1 of 4)

Generating and Validating JWT Tokens in Java using PEM

JSON Web Tokens (JWT) are a popular way to secure communication between parties using digitally signed tokens. In this article, we will explore how to generate and validate JWT tokens in Java using PEM (Privacy Enhanced Mail) files. PEM files are commonly used to store cryptographic keys and certificates.

Generate JWT Token

In this section, we'll walk through how to generate a JWT token in Java using a private key stored in a PEM file.

Setting up the Environment

Before we proceed, make sure you have the following prerequisites:

  1. Java Development Kit (JDK) installed on your system.
  2. A PEM file containing a private key (referred to as <PRIVATE_KEY_PEM>).

Code Implementation

We'll use Java to create a JWT token. Here's the code for generating a JWT token:

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.UUID;

public class GenerateJWTSignedByPEM {

    public static void main(final String ... args) throws Exception {
        // Load the private key and certificate
        final var privateKeyPEM = """
                <PRIVATE_KEY_PEM>              
                """;

        final var privateKey = getPrivateKeyFromPEM(privateKeyPEM);

        // Create JWT claims
        final var subject = "user123";
        final var issuer = "yourapp.com";
        final var expirationTimeMillis = System.currentTimeMillis() + 3600 * 1000; // 1 hour
        final var jwtID = UUID.randomUUID().toString();

        // Build JWT claims
        final var jwtHeader = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
        final var jwtClaims = "{\"sub\":\"" + subject + "\",\"iss\":\"" + issuer + "\",\"exp\":" + expirationTimeMillis + ",\"jti\":\"" + jwtID + "\"}";

        // Base64Url encode the JWT header and claims
        final var base64UrlHeader = base64UrlEncode(jwtHeader.getBytes());
        final var base64UrlClaims = base64UrlEncode(jwtClaims.getBytes());

        // Combine header and claims with a period separator
        final var headerClaims = base64UrlHeader + "." + base64UrlClaims;

        // Sign the JWT
        final var signature = signWithRSA(headerClaims, privateKey);

        // Combine the JWT components
        final var jwtToken = headerClaims + "." + signature;

        System.out.println("JWT Token: " + jwtToken);
    }

    // Helper function to load a PrivateKey from PEM format
    private static PrivateKey getPrivateKeyFromPEM(String privateKeyPEM) throws Exception {
        privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");

        final var privateKeyBytes = Base64.getDecoder().decode(privateKeyPEM);

        final var keyFactory = KeyFactory.getInstance("RSA");
        final var keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        return keyFactory.generatePrivate(keySpec);
    }

    // Base64 URL encoding
    private static String base64UrlEncode(final byte[] data) {
        return Base64.getUrlEncoder().withoutPadding().encodeToString(data);
    }

    // Sign the JWT using RSA
    private static String signWithRSA(final String data, final PrivateKey privateKey) throws Exception {
        final var signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data.getBytes());
        final var signatureBytes = signature.sign();
        return base64UrlEncode(signatureBytes);
    }
}

Here's a breakdown of the code:

  1. We load the private key from the PEM file.
  2. Create JWT claims, including subject, issuer, expiration time, and a unique JWT ID.
  3. Base64Url encode the JWT header and claims.
  4. Combine the header and claims with a period separator.
  5. Sign the JWT using the private key.
  6. Combine all the JWT components to get the final JWT token.

Make sure to replace <PRIVATE_KEY_PEM> with the actual content of your private key PEM file.

Validate JWT Token

In this section, we'll learn how to validate a JWT token using a public certificate stored in a PEM file.

Setting up the Environment

Ensure you have the following prerequisites:

  1. Java Development Kit (JDK) installed on your system.
  2. A PEM file containing a public certificate (referred to as <PUBLIC_CERT_PEM>).
  3. A JWT token you want to validate (referred to as <JWT_TOKEN>).

Code Implementation

We'll use Java to validate a JWT token. Here's the code:

import java.io.ByteArrayInputStream;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Base64;

public class ValidateJWTSignedByPEM {
    public static void main(final String ... args) throws Exception {

        // The JWT token to validate.
        final var jwtToken = "<JWT_TOKEN>";

        // Load the X.509 certificate
        final var certificatePEM = """
                <PUBLIC_CERT_PEM>              
                """;

        final var certificate = getCertificateFromPEM(certificatePEM);

        // Parse JWT components
        final var jwtParts = jwtToken.split("\\.");
        if (jwtParts.length != 3) {
            System.out.println("Invalid JWT format");
            return;
        }

        // Decode and verify the JWT signature
        final var base64UrlHeader = jwtParts[0];
        final var base64UrlClaims = jwtParts[1];
        final var signature = jwtParts[2];

        // Verify the signature
        if (verifySignature(base64UrlHeader, base64UrlClaims, signature, certificate.getPublicKey())) {
            System.out.println("JWT signature is valid");
        } else {
            System.out.println("JWT signature is invalid");
        }
    }

    private static X509Certificate getCertificateFromPEM(String certificatePEM) throws Exception {
        certificatePEM = certificatePEM.replace("-----BEGIN CERTIFICATE-----", "")
                .replace("-----END CERTIFICATE-----", "")
                .replaceAll("\\s+", "");

        final var certificateBytes = Base64.getDecoder().decode(certificatePEM);

        final var certificateFactory = java.security.cert.CertificateFactory.getInstance("X.509");
        return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificateBytes));
    }

    private static boolean verifySignature(final String base64UrlHeader, final String base64UrlClaims, final String signature, final PublicKey publicKey) throws Exception {
        final var signedData = base64UrlHeader + "." + base64UrlClaims;
        final var signatureBytes = Base64.getUrlDecoder().decode(signature);

        final var verifier = Signature.getInstance("SHA256withRSA");
        verifier.initVerify(publicKey);
        verifier.update(signedData.getBytes());

        return verifier.verify(signatureBytes);
    }
}

Here's how the code works:

  1. Load the JWT token and public certificate from their respective PEM files.
  2. Parse the JWT token into its components: header, claims, and signature.
  3. Verify the signature by re-signing the header and claims and comparing it with the provided signature.

Replace <PUBLIC_CERT_PEM> and <JWT_TOKEN> with the actual content of your public certificate PEM file and the JWT token you want to validate.

Summary

In this article, we've explored how to generate and validate JWT tokens in Java using PEM files. This approach allows you to secure your applications by creating and verifying digitally signed tokens. Make sure to keep your private keys and certificates secure, as they are crucial for the security of your JWT-based authentication system.

Related Topic

Generating a Self-signed CA Certificate for JSON Web Token (JWT) in Java

Generating and Validating JWT Tokens in Java using Keystore

JWT (JSON Web Tokens) is a compact, URL-safe means of representing claims to be transferred between two parties. In this article, we will walk through how to generate and validate JWT tokens in Java, using a private certificate stored in a keystore. We will provide example Java code for both processes.

Generate JWT Token

Generating a JWT token involves several steps:

  1. Loading the Keystore: You need to load the keystore that contains the private key and certificate. You'll also specify the keystore password and the alias of the certificate in the keystore.
  2. Creating JWT Claims: Define the claims you want to include in the JWT. These can include the subject, issuer, expiration time, and a unique JWT ID (jti).
  3. Base64URL Encoding: Encode the JWT header and claims in base64 URL-safe format. This is a requirement for JWT.
  4. Combining Header and Claims: Combine the base64-encoded header and claims with a period separator.
  5. Signing the JWT: Sign the JWT using RSA with the private key from the keystore.
  6. Combining JWT Components: Combine the header, claims, and signature to create the final JWT token.

Here's the Java code for generating a JWT token:

import java.io.FileInputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.util.Base64;
import java.util.UUID;

public class GenerateJWTSignedByKSCert {

    public static void main(String... args) throws Exception {
        // Load the keystore and retrieve the private key and certificate
        final var keystorePath = "<CERTIFICATE_KEYSTORE>";
        final var keystorePassword = "<KEYSTORE_PASSWORD>";
        final var alias = "<CERTIFICATE_ALIAS>";
        final var keystore = KeyStore.getInstance("JKS");
        keystore.load(new FileInputStream(keystorePath), keystorePassword.toCharArray());
        final var privateKey = (RSAPrivateKey) keystore.getKey(alias, keystorePassword.toCharArray());

        // Sample JWT claims
        final var subject = "user123";
        final var issuer = "yourapp.com";
        final var expirationTimeMillis = System.currentTimeMillis() + 3600 * 1000; // 1 hour
        final var jwtID = UUID.randomUUID().toString();

        // Build JWT claims
        final var jwtHeader = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";
        final var jwtClaims = "{\"sub\":\"" + subject + "\",\"iss\":\"" + issuer + "\",\"exp\":" + expirationTimeMillis + ",\"jti\":\"" + jwtID + "\"}";

        // Base64Url encode the JWT header and claims
        final var base64UrlHeader = base64UrlEncode(jwtHeader.getBytes());
        final var base64UrlClaims = base64UrlEncode(jwtClaims.getBytes());

        // Combine header and claims with a period separator
        final var headerClaims = base64UrlHeader + "." + base64UrlClaims;

        // Sign the JWT
        final var signature = signWithRSA(headerClaims, privateKey);

        // Combine the JWT components
        final var jwtToken = headerClaims + "." + signature;

        System.out.println("JWT Token: " + jwtToken);
    }

    // Base64 URL encoding
    private static String base64UrlEncode(byte[] data) {
        return Base64.getUrlEncoder().withoutPadding().encodeToString(data);
    }

    // Sign the JWT using RSA
    private static String signWithRSA(String data, RSAPrivateKey privateKey) throws Exception {
        // Perform the RSA signing (e.g., with Signature.getInstance("SHA256withRSA"))
        // and return the base64Url-encoded signature
        final var signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data.getBytes());
        final var signatureBytes = signature.sign();
        return base64UrlEncode(signatureBytes);
    }
}

Validate JWT Token

Once you have generated a JWT token, you may need to validate it to ensure its integrity. Validation typically involves verifying the token's signature using the corresponding public key from the keystore. Here's the Java code for validating a JWT token:

import java.io.FileInputStream;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Base64;

public class ValidateJWTSignedByKSCert {

    public static void main(final String ... args) throws Exception {
        // The JWT token to validate.
        final var jwtToken = "<JWT_TOKEN>";

        // Load the keystore and retrieve the public key
        final var keystorePath = "<CERTIFICATE_KEYSTORE>";
        final var keystorePassword = "<KEYSTORE_PASSWORD>";
        final var alias = "<CERTIFICATE_ALIAS>";
        KeyStore keystore = KeyStore.getInstance("JKS");
        keystore.load(new FileInputStream(keystorePath), keystorePassword.toCharArray());
        final var certificate = (X509Certificate) keystore.getCertificate(alias);

        // Parse JWT components
        final var jwtParts = jwtToken.split("\\.");
        if (jwtParts.length != 3) {
            System.out.println("Invalid JWT format");
            return;
        }

        // Decode and verify the JWT signature
        final var base64UrlHeader = jwtParts[0];
        final var base64UrlClaims = jwtParts[1];
        final var signature = jwtParts[2];

        // Verify the signature
        if (verifySignature(base64UrlHeader, base64UrlClaims, signature, certificate.getPublicKey())) {
            System.out.println("JWT signature is valid");
        } else {
            System.out.println("JWT signature is invalid");
        }
    }

    private static boolean verifySignature(final String base64UrlHeader, final String base64UrlClaims, final String signature, final PublicKey publicKey) throws Exception {
        final var signedData = base64UrlHeader + "." + base64UrlClaims;
        final var signatureBytes = Base64.getUrlDecoder().decode(signature);

        final var verifier = Signature.getInstance("SHA256withRSA");
        verifier.initVerify(publicKey);
        verifier.update(signedData.getBytes());

        return verifier.verify(signatureBytes);
    }
}

Tokens

In both code examples, there are tokens that need to be replaced with specific values:

  • <CERTIFICATE_KEYSTORE>: Replace this with the absolute path of the keystore. It's possible to have separate keystores for the private and public certificates.
  • <KEYSTORE_PASSWORD>: Replace this with the password that corresponds to the keystore.
  • <CERTIFICATE_ALIAS>: Replace this with the alias of the certificate in the keystore.
  • <JWT_TOKEN>: Replace this with the JWT token you want to validate.

By using the provided code and replacing these tokens with the appropriate values, you can generate and validate JWT tokens in Java with ease, using a private certificate stored in a keystore.

Related Topic

Generating a Self-signed CA Certificate for JSON Web Token (JWT) in Java

The Resource Owner Password Credential (ROPC) Grant Type

The resource owner password credential grant type is designed as a stop-gap for legacy applications. Should only be used temporarily until the migration of the application to OAUTH is complete. This grant type should never be used anymore. This type can request for offline_access scope (i.e. to request for refresh token).

  1. Use the token end point to do post request for the access token with the following headers:

    Content-Type = application/x-www-form-urlencoded

    And with the following form data:

    grant_type = password
    client_id = the one used from step 1.
    client_secret = 
    username = 
    password = 
    scope = (Optional) what permision wanted. If not specified, default permission will be given.
    state = (Optional) value to echo to us.

    Expected Response

    {
    "access_token" : <ACCESS_TOKEN>,
    "token_type" : "Bearer",
    "expires_in" : 3600,
    "scope" : <The scope allowed by the server>
    }
  2. Call the API with the authorization header like the following syntax:

    Bearer <ACCESS_TOKEN>

Related Post
KEYCLOAK – JWT GENERATION – PASSWORD GRANT TYPE

Keycloak – Realm – OpenID Configuration

The open id configuration exposes some information like the following:

  • Authorization endpoint
  • Token endpoint
  • Supported grant types
  • Supported response types
  • Supported response modes
  • Supported claims
  • Supported scopes

Use the following address syntax to find-out the OpenID configuration:

<KEYCLOAK_ADDRESS>/realms/<TARGET_REALM>/.well-known/openid-configuration

Example

Given

Token Value
KEYCLOAK_ADDRESS http://localhost:8080
TARGET_REALM test

The OpenID configuration would be:

http://localhost:8080/realms/testrealm/.well-known/openid-configuration

Keycloak – JWT Generation – Password Grant Type

Pre-requisite

  • Keycloak 19^1

Creating a New Client and User

  1. Sign in to keycloak admin console using the following address:

    Must know a valid credential.

    http://localhost:8080/admin/

  2. Switch or create a realm that is NOT a master realm (i.e. leave the master realms for keycloak usage only), like the following (i.e. jwtrealm):
    jwt-realm

  3. Create a new client as follows:

    1. Ensure that OpenID Connect is the Client type.

    2. Provide a Client ID (e.g. jwtclient).

    3. Click the Next button.

      client-general-settings

    4. Enable the Client authentication.

    5. In the Authentication flow, unselect the standard flow.

    6. Click the Save button.

      client-capability-config

  4. Create a new user as follows:

    1. Fill-in the username field (e.g. testuser).

    2. Click the Create button.

      user-create

    3. Click the Credentials tab.

    4. Click the Set password button.

    5. Fill-in the Password field.

    6. Fill-in the Password confirmation field.

    7. Turn-off temporary.

    8. Click the Save button.

      user-password

    9. Click the Save password button.

Using Postman for Testing

  1. Create a post request to the following address format:

    http://localhost:8080/realms/<TARGET_REALM>/protocol/openid-connect/token

    Example

    Using the jwtrealm as the TARGET_REALM (i.e. configured in the previous section).

    http://localhost:8080/realms/jwtrealm/protocol/openid-connect/token

  2. Click the Body tab.

  3. Select x-www-form-url-encoded.

  4. Add the following entries:

    Key Value Comment
    client_id jwtclient This is the client configured earlier.
    grant_type password This is for direct access grant type.
    client_secret <Client secret> This can be found in the jwtclient (i.e. configured earlier) client credentials tab.

    client-secret

    scope openid The openid scope is required; to indicate that the application intends to use OIDC to verify the user's identity.
    username testuser This is the user configured earlier.
    password <password> This is the password for the user that is configured earlier.
  5. Click the Send button.

    postman-request

Success Output

The success output is in the following format.

{
    "access_token": "The access token.",
    "expires_in": "Access token expiration.",
    "refresh_expires_in": "Refresh token expiration",
    "refresh_token": "The refresh token.",
    "token_type": "Bearer",
    "id_token": "The ID token.",
    "not-before-policy": 0,
    "session_state": "The session state.",
    "scope": "openid profile email"
}

You paste the encoded token to the following website to decode its content:

https://jwt.io/

Invalid Credential Output

{
    "error": "invalid_grant",
    "error_description": "Invalid user credentials"
}

Related Post
THE RESOURCE OWNER PASSWORD CREDENTIAL (ROPC) GRANT TYPE

Authorization Code Grant Type

The authorization code grant type is designed for confidential clients (e.g. websites with a server back end) that can keep a secret. This type can request for offline_access scope (i.e. to request for refresh token).

  1. Use the authorization end point to request the authorization code with the following query parameters:

    response_type = code 
    client_id = the client unique code
    redirect_uri = redirection URL.
    state = (Optional) value to echo to us.
    scope = (Optional) what permision wanted. If not specified, default permission will be given.
    response_mode = (Optional) query

    A login form will be displayed if not yet filled-up before.

    Expected Response

    The redirect_uri with the following query parameters:

    code = The authorization code
    state = state value if given.
  2. Use the token end point to do post request for the access token with the following headers:

    Content-Type = application/x-www-form-urlencoded
    Authorization = Basic <CREDENTIAL>

    And with the following parameters:

    grant_type = authorization_code.
    code = The authorization code from step 1.
    redirect_uri = The used from step 1.

    Expected Response

    Header

    Content-Type: application/json
    
    {
    "access_token" : <ACCESS_TOKEN>,
    "token_type" : "Bearer",
    "expires_in" : 3600,
    "scope" : <The scope allowed by the server>
    }
  3. Call the API with the authorization header like the following syntax:

    Bearer <ACCESS_TOKEN>

Sample implementation can be found here.

Private Signing a CSR

Signing the CSR

  1. Download OpenSSL binaries from the following link if you are using windows:

    https://slproweb.com/products/Win32OpenSSL.html

  2. Create a v3.cnf file using the following template:

    authorityKeyIdentifier=keyid,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName          = @alternate_names
    nsComment           = "Self-signed Certificate"
    
    [ alternate_names ]
    
    DNS.1       = <DNS_1>
    #DNS.2       = <DNS_2>
    #DNS.3       = <DNS_3>
    #DNS.4       = <DNS_4>
    
    # Add these if you need them. But usually you don't want them or
    #   need them in production. You may need them for development.
    # DNS.5       = localhost
    # DNS.6       = localhost.localdomain
    # DNS.7       = 127.0.0.1
    
    # IPv6 localhost
    # DNS.8     = ::1

    Replace the following fields on the template:

    Field Name Description
    DNS_<INDEX> Identify the DNS names from the CSR.

    Example:

    authorityKeyIdentifier=keyid,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName          = @alternate_names
    nsComment           = "Self-signed Certificate"
    
    [ alternate_names ]
    
    DNS.1       = www.ronella.xyz
    #DNS.2       = <DNS_2> 
    #DNS.3       = <DNS_3>
    #DNS.4       = <DNS_4>
    
    # Add these if you need them. But usually you don't want them or
    #   need them in production. You may need them for development.
    # DNS.5       = localhost
    # DNS.6       = localhost.localdomain
    # DNS.7       = 127.0.0.1
    
    # IPv6 localhost
    # DNS.8     = ::1
  3. Generate a CA private key and certificate pair. The following link can help:
    PRIVATE CERTIFICATION AUTHORITY (CA)

  4. Once you have the pair (i.e. key is ca.key.pem and the certificate is ca.cert.crt), sign the CSR using the following command:

    openssl x509 -req -days 365 -sha256 -in domain.csr -extfile v3.cnf -CA ca.cert.crt -CAkey ca.key.pem -CAcreateserial -out domain.crt

Viewing the generated certificate from CSR

  1. View the signed certificate using the following the command:

    openssl x509 -in domain.crt -text

Certificate Signing Request (CSR)

Generating a CSR

  1. Download OpenSSL binaries from the following link if you are using windows:

    https://slproweb.com/products/Win32OpenSSL.html

  2. Create a domain.cnf file using the following template:

    [ req ]
    default_bits        = 2048
    default_keyfile     = private.pem
    distinguished_name  = subject
    req_extensions      = req_ext
    x509_extensions     = x509_ext
    string_mask         = utf8only
    
    [ subject ]
    countryName         = Country Name (2 letter code)
    countryName_default     = <2_LETTER_COUNTRY_CODE>
    
    stateOrProvinceName     = State or Province Name (full name)
    stateOrProvinceName_default = <STATE_NAME>
    
    localityName            = Locality Name (eg, city)
    localityName_default        = <CITY_NAME>
    
    organizationName         = Organization Name (eg, company)
    organizationName_default    = <ORGANIZATION_NAME>
    
    organizationalUnitName         = Organizational Unit (eg, section)
    organizationalUnitName_default = <ORGANIZATIONAL_UNIT>
    
    commonName          = Common Name (e.g. server FQDN or YOUR name)
    commonName_default      = <YOUR_NAME>
    
    emailAddress            = Email Address
    emailAddress_default        = <YOUR_EMAIL_ADDR>
    
    # Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
    [ x509_ext ]
    
    subjectKeyIdentifier        = hash
    authorityKeyIdentifier    = keyid,issuer
    
    basicConstraints        = CA:false
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName          = @alternate_names
    nsComment           = "Self-signed Certificate"
    
    # Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
    [ req_ext ]
    
    subjectKeyIdentifier        = hash
    
    basicConstraints        = CA:false
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName          = @alternate_names
    nsComment           = "Private Certificate"
    
    [ alternate_names ]
    
    DNS.1        = <DNS_1>
    
    # Add more DNS by incrementing the DNS.<SUFFIX> like the following.
    # DNS.2       = <DNS_2>
    # DNS.3       = <DNS_3>
    # DNS.4       = <DNS_4>
    
    # Add these if you need them. But usually you don't want them or
    #   need them in production. You may need them for development.
    # DNS.5       = localhost
    # DNS.6       = localhost.localdomain
    # DNS.7       = 127.0.0.1
    
    # IPv6 localhost
    # DNS.8     = ::1

    Replace the following fields on the template:

    Field Name Description
    2_LETTER_COUNTRY_CODE The two letter code of your country.
    STATE_NAME The name of your state.
    CITY_NAME The name of your city.
    ORGANIZATION_NAME The name of your organization.
    ORGANIZATIONAL_UNIT The name of your section in the organization.
    YOUR_NAME Your full name.
    YOUR_EMAIL_ADDR Your email address.
    DNS_<INDEX> Your DNS name.

    Example:

    [ req ]
    default_bits        = 2048
    default_keyfile     = private.pem
    distinguished_name  = subject
    req_extensions      = req_ext
    x509_extensions     = x509_ext
    string_mask         = utf8only
    
    [ subject ]
    countryName         = Country Name (2 letter code)
    countryName_default     = NZ
    
    stateOrProvinceName     = State or Province Name (full name)
    stateOrProvinceName_default = Wellington
    
    localityName            = Locality Name (eg, city)
    localityName_default        = Wellington
    
    organizationName         = Organization Name (eg, company)
    organizationName_default    = My Organization
    
    organizationalUnitName         = Organizational Unit (eg, section)
    organizationalUnitName_default = IT Department
    
    commonName          = Common Name (e.g. server FQDN or YOUR name)
    commonName_default      = www.ronella.xyz
    
    emailAddress            = Email Address
    emailAddress_default        = ron@ronella.xyz
    
    # Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
    [ x509_ext ]
    
    subjectKeyIdentifier        = hash
    authorityKeyIdentifier    = keyid,issuer
    
    basicConstraints        = CA:false
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName          = @alternate_names
    nsComment           = "Self-signed Certificate"
    
    # Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
    [ req_ext ]
    
    subjectKeyIdentifier        = hash
    
    basicConstraints        = CA:false
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName          = @alternate_names
    nsComment           = "Private Certificate"
    
    [ alternate_names ]
    
    DNS.1        = www.ronella.xyz
    
    # Add more DNS by incrementing the DNS.<SUFFIX> like the following.
    # DNS.2       = <DNS_2>
    # DNS.3       = <DNS_3>
    # DNS.4       = <DNS_4>
    
    # Add these if you need them. But usually you don't want them or
    #   need them in production. You may need them for development.
    # DNS.5       = localhost
    # DNS.6       = localhost.localdomain
    # DNS.7       = 127.0.0.1
    
    # IPv6 localhost
    # DNS.8     = ::1
  3. Generate a private key using the following command:

    openssl genrsa -out domain.key.pem 2048
  4. Generate the CSR using the private key with the following command:

    openssl req -new -key domain.key.pem -nodes -out domain.csr -config domain.cnf

Viewing the Generated CSR

  1. View the generated CSR using the following command:

    openssl req -text -noout -verify -in domain.csr

Private Certification Authority (CA)

Create the private key and certificate pair.

  1. Download OpenSSL binaries from the following link if you are using windows:

    https://slproweb.com/products/Win32OpenSSL.html

  2. Create a ca.cnf file using the following template:

    [ req ]
    default_bits        = 2048
    default_keyfile     = private.pem
    distinguished_name  = subject
    req_extensions      = req_ext
    x509_extensions     = x509_ext
    string_mask         = utf8only
    
    # The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description).
    #   Its sort of a mashup. For example, RFC 4514 does not provide emailAddress.
    [ subject ]
    countryName         = Country Name (2 letter code)
    countryName_default     = <2_LETTER_COUNTRY_CODE>
    
    stateOrProvinceName     = State or Province Name (full name)
    stateOrProvinceName_default = <STATE_NAME>
    
    localityName            = Locality Name (eg, city)
    localityName_default        = <CITY_NAME>
    
    organizationName         = Organization Name (eg, company)
    organizationName_default    = <ORGANIZATION_NAME>
    
    organizationalUnitName         = Organizational Unit (eg, section)
    organizationalUnitName_default = <ORGANIZATIONAL_UNIT>
    
    # Use a friendly name here because it's presented to the user. The server's DNS
    #   names are placed in Subject Alternate Names. Plus, DNS names here is deprecated
    #   by both IETF and CA/Browser Forums. If you place a DNS name here, then you
    #   must include the DNS name in the SAN too (otherwise, Chrome and others that
    #   strictly follow the CA/Browser Baseline Requirements will fail).
    commonName          = Common Name (e.g. server FQDN or YOUR name)
    commonName_default      = <YOUR_NAME>
    
    emailAddress            = Email Address
    emailAddress_default        = <YOUR_EMAIL_ADDR>
    
    # Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
    [ x509_ext ]
    
    subjectKeyIdentifier        = hash
    authorityKeyIdentifier    = keyid,issuer
    
    basicConstraints        = CA:TRUE
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
    nsComment           = "Private CA"
    
    # Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
    [ req_ext ]
    
    subjectKeyIdentifier        = hash
    
    basicConstraints        = CA:true
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
    nsComment           = "Private CA"

    Replace the following fields on the template:

    Field Name Description
    2_LETTER_COUNTRY_CODE The two letter code of your country.
    STATE_NAME The name of your state.
    CITY_NAME The name of your city.
    ORGANIZATION_NAME The name of your organization.
    ORGANIZATIONAL_UNIT The name of your section in the organization.
    YOUR_NAME Your full name or anything that represents you as a CA.
    YOUR_EMAIL_ADDR Your email address.

    Example:

    [ req ]
    default_bits        = 2048
    default_keyfile     = private.pem
    distinguished_name  = subject
    req_extensions      = req_ext
    x509_extensions     = x509_ext
    string_mask         = utf8only
    
    # The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description).
    #   Its sort of a mashup. For example, RFC 4514 does not provide emailAddress.
    [ subject ]
    countryName         = Country Name (2 letter code)
    countryName_default     = NZ
    
    stateOrProvinceName     = State or Province Name (full name)
    stateOrProvinceName_default = Wellington
    
    localityName            = Locality Name (eg, city)
    localityName_default        = Wellington
    
    organizationName         = Organization Name (eg, company)
    organizationName_default    = My Company
    
    organizationalUnitName         = Organizational Unit (eg, section)
    organizationalUnitName_default = IT Department
    
    # Use a friendly name here because it's presented to the user. The server's DNS
    #   names are placed in Subject Alternate Names. Plus, DNS names here is deprecated
    #   by both IETF and CA/Browser Forums. If you place a DNS name here, then you
    #   must include the DNS name in the SAN too (otherwise, Chrome and others that
    #   strictly follow the CA/Browser Baseline Requirements will fail).
    commonName          = Common Name (e.g. server FQDN or YOUR name)
    commonName_default      = Ronaldo Webb CA APR 2021
    
    emailAddress            = Email Address
    emailAddress_default        = ron@ronella.xyz
    
    # Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
    [ x509_ext ]
    
    subjectKeyIdentifier        = hash
    authorityKeyIdentifier    = keyid,issuer
    
    basicConstraints        = CA:TRUE
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
    nsComment           = "Private CA"
    
    # Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
    [ req_ext ]
    
    subjectKeyIdentifier        = hash
    
    basicConstraints        = CA:true
    keyUsage            = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyCertSign, cRLSign
    nsComment           = "Private CA"
  3. Generate a private key using the following command:

    openssl genrsa -out ca.key.pem 2048
  4. Generate a certificate with a validity of 10 years from the private key using the following command:

    openssl req -x509 -sha256 -new -nodes -key ca.key.pem -days 3650 -out ca.cert.crt -config ca.cnf

Viewing the generated certificate

  1. View the generated certificate using the following command:

    openssl x509 -in ca.cert.crt -text
« Older posts