com.adeptj.modules.security.jwt.internal.JwtKeys.java Source code

Java tutorial

Introduction

Here is the source code for com.adeptj.modules.security.jwt.internal.JwtKeys.java

Source

/*
###############################################################################
#                                                                             #
#    Copyright 2016, AdeptJ (http://www.adeptj.com)                           #
#                                                                             #
#    Licensed under the Apache License, Version 2.0 (the "License");          #
#    you may not use this file except in compliance with the License.         #
#    You may obtain a copy of the License at                                  #
#                                                                             #
#        http://www.apache.org/licenses/LICENSE-2.0                           #
#                                                                             #
#    Unless required by applicable law or agreed to in writing, software      #
#    distributed under the License is distributed on an "AS IS" BASIS,        #
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
#    See the License for the specific language governing permissions and      #
#    limitations under the License.                                           #
#                                                                             #
###############################################################################
*/

package com.adeptj.modules.security.jwt.internal;

import com.adeptj.modules.security.jwt.JwtConfig;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.lang.Assert;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import static java.nio.charset.StandardCharsets.UTF_8;
import static javax.crypto.Cipher.DECRYPT_MODE;
import static org.apache.commons.lang3.StringUtils.EMPTY;

/**
 * Utility for creating JWT signing and verification key.
 *
 * @author Rakesh.Kumar, AdeptJ
 */
final class JwtKeys {

    private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    private static final String PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----";

    private static final String PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----";

    private static final String PRIVATE_ENCRYPTED_KEY_HEADER = "-----BEGIN ENCRYPTED PRIVATE KEY-----";

    private static final String PRIVATE_ENCRYPTED_KEY_FOOTER = "-----END ENCRYPTED PRIVATE KEY-----";

    private static final String PUB_KEY_HEADER = "-----BEGIN PUBLIC KEY-----";

    private static final String PUB_KEY_FOOTER = "-----END PUBLIC KEY-----";

    private static final String REGEX_SPACE = "\\s";

    private static final String KEYPASS_NULL_MSG = "privateKeyPassword can't be blank!!";

    private static final String INVALID_PUBLIC_KEY_MSG = "Invalid PublicKey, must start with -----BEGIN PUBLIC KEY-----";

    private static final String INVALID_PRIVATE_KEY_MSG = "Invalid PrivateKey, must start either with -----BEGIN PRIVATE KEY-----"
            + " or -----BEGIN ENCRYPTED PRIVATE KEY-----";

    private JwtKeys() {
    }

    static PrivateKey createSigningKey(JwtConfig jwtConfig, SignatureAlgorithm signatureAlgo) {
        LOGGER.info("Creating RSA signing key!!");
        String keyData = jwtConfig.privateKey();
        Assert.isTrue(StringUtils.startsWithAny(keyData, PRIVATE_ENCRYPTED_KEY_HEADER, PRIVATE_KEY_HEADER),
                INVALID_PRIVATE_KEY_MSG);
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(signatureAlgo.getFamilyName());
            if (StringUtils.startsWith(keyData, PRIVATE_ENCRYPTED_KEY_HEADER)) {
                LOGGER.info("Creating PKCS8EncodedKeySpec from private [encrypted] key !!");
                Assert.hasText(jwtConfig.privateKeyPassword(), KEYPASS_NULL_MSG);
                byte[] privateKeyData = decodePrivateKeyData(keyData, true);
                EncryptedPrivateKeyInfo privateKeyInfo = new EncryptedPrivateKeyInfo(privateKeyData);
                SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(privateKeyInfo.getAlgName());
                PBEKeySpec keySpec = new PBEKeySpec(jwtConfig.privateKeyPassword().toCharArray());
                SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
                Cipher cipher = Cipher.getInstance(privateKeyInfo.getAlgName());
                cipher.init(DECRYPT_MODE, secretKey, privateKeyInfo.getAlgParameters());
                return keyFactory.generatePrivate(privateKeyInfo.getKeySpec(cipher));
            }
            LOGGER.info("Creating PKCS8EncodedKeySpec from private key !!");
            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(decodePrivateKeyData(keyData, false)));
        } catch (GeneralSecurityException | IOException ex) {
            LOGGER.error(ex.getMessage(), ex);
            throw new KeyInitializationException(ex.getMessage(), ex);
        }
    }

    static PublicKey createVerificationKey(SignatureAlgorithm signatureAlgo, String publicKey) {
        LOGGER.info("Creating RSA verification key!!");
        Assert.isTrue(StringUtils.startsWith(publicKey, PUB_KEY_HEADER), INVALID_PUBLIC_KEY_MSG);
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(signatureAlgo.getFamilyName());
            byte[] publicKeyData = Base64.getDecoder().decode(publicKey.replace(PUB_KEY_HEADER, EMPTY)
                    .replace(PUB_KEY_FOOTER, EMPTY).replaceAll(REGEX_SPACE, EMPTY).trim().getBytes(UTF_8));
            LOGGER.info("Creating X509EncodedKeySpec from public key !!");
            return keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyData));
        } catch (GeneralSecurityException ex) {
            LOGGER.error(ex.getMessage(), ex);
            throw new KeyInitializationException(ex.getMessage(), ex);
        }
    }

    private static byte[] decodePrivateKeyData(String keyData, boolean encryptedKey) {
        return Base64.getDecoder()
                .decode(keyData.replace(encryptedKey ? PRIVATE_ENCRYPTED_KEY_HEADER : PRIVATE_KEY_HEADER, EMPTY)
                        .replace(encryptedKey ? PRIVATE_ENCRYPTED_KEY_FOOTER : PRIVATE_KEY_FOOTER, EMPTY)
                        .replaceAll(REGEX_SPACE, EMPTY).trim().getBytes(UTF_8));
    }
}