All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jsonwebtoken.security.Keys Maven / Gradle / Ivy

There is a newer version: 0.12.6
Show newest version
/*
 * Copyright (C) 2014 jsonwebtoken.io
 *
 * 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 io.jsonwebtoken.security;

import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Classes;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.KeyPair;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Utility class for securely generating {@link SecretKey}s and {@link KeyPair}s.
 *
 * @since 0.10.0
 */
public final class Keys {

    private static final String MAC = "io.jsonwebtoken.impl.crypto.MacProvider";
    private static final String RSA = "io.jsonwebtoken.impl.crypto.RsaProvider";
    private static final String EC = "io.jsonwebtoken.impl.crypto.EllipticCurveProvider";

    private static final Class[] SIG_ARG_TYPES = new Class[]{SignatureAlgorithm.class};

    //purposefully ordered higher to lower:
    private static final List PREFERRED_HMAC_ALGS = Collections.unmodifiableList(Arrays.asList(
        SignatureAlgorithm.HS512, SignatureAlgorithm.HS384, SignatureAlgorithm.HS256));

    //prevent instantiation
    private Keys() {
    }

    /*
    public static final int bitLength(Key key) throws IllegalArgumentException {
        Assert.notNull(key, "Key cannot be null.");
        if (key instanceof SecretKey) {
            byte[] encoded = key.getEncoded();
            return Arrays.length(encoded) * 8;
        } else if (key instanceof RSAKey) {
            return ((RSAKey)key).getModulus().bitLength();
        } else if (key instanceof ECKey) {
            return ((ECKey)key).getParams().getOrder().bitLength();
        }

        throw new IllegalArgumentException("Unsupported key type: " + key.getClass().getName());
    }
    */

    /**
     * Creates a new SecretKey instance for use with HMAC-SHA algorithms based on the specified key byte array.
     *
     * @param bytes the key byte array
     * @return a new SecretKey instance for use with HMAC-SHA algorithms based on the specified key byte array.
     * @throws WeakKeyException if the key byte array length is less than 256 bits (32 bytes) as mandated by the
     *                          JWT JWA Specification
     *                          (RFC 7518, Section 3.2)
     */
    public static SecretKey hmacShaKeyFor(byte[] bytes) throws WeakKeyException {

        if (bytes == null) {
            throw new InvalidKeyException("SecretKey byte array cannot be null.");
        }

        int bitLength = bytes.length * 8;

        for (SignatureAlgorithm alg : PREFERRED_HMAC_ALGS) {
            if (bitLength >= alg.getMinKeyLength()) {
                return new SecretKeySpec(bytes, alg.getJcaName());
            }
        }

        String msg = "The specified key byte array is " + bitLength + " bits which " +
            "is not secure enough for any JWT HMAC-SHA algorithm.  The JWT " +
            "JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a " +
            "size >= 256 bits (the key size must be greater than or equal to the hash " +
            "output size).  Consider using the " + Keys.class.getName() + "#secretKeyFor(SignatureAlgorithm) method " +
            "to create a key guaranteed to be secure enough for your preferred HMAC-SHA algorithm.  See " +
            "https://tools.ietf.org/html/rfc7518#section-3.2 for more information.";
        throw new WeakKeyException(msg);
    }

    /**
     * Returns a new {@link SecretKey} with a key length suitable for use with the specified {@link SignatureAlgorithm}.
     *
     * 

JWA Specification (RFC 7518), Section 3.2 * requires minimum key lengths to be used for each respective Signature Algorithm. This method returns a * secure-random generated SecretKey that adheres to the required minimum key length. The lengths are:

* * * * * * * * * * * * * * * * * * *
AlgorithmKey Length
HS256256 bits (32 bytes)
HS384384 bits (48 bytes)
HS512512 bits (64 bytes)
* * @param alg the {@code SignatureAlgorithm} to inspect to determine which key length to use. * @return a new {@link SecretKey} instance suitable for use with the specified {@link SignatureAlgorithm}. * @throws IllegalArgumentException for any input value other than {@link SignatureAlgorithm#HS256}, * {@link SignatureAlgorithm#HS384}, or {@link SignatureAlgorithm#HS512} */ public static SecretKey secretKeyFor(SignatureAlgorithm alg) throws IllegalArgumentException { Assert.notNull(alg, "SignatureAlgorithm cannot be null."); switch (alg) { case HS256: case HS384: case HS512: return Classes.invokeStatic(MAC, "generateKey", SIG_ARG_TYPES, alg); default: String msg = "The " + alg.name() + " algorithm does not support shared secret keys."; throw new IllegalArgumentException(msg); } } /** * Returns a new {@link KeyPair} suitable for use with the specified asymmetric algorithm. * *

If the {@code alg} argument is an RSA algorithm, a KeyPair is generated based on the following:

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
JWA AlgorithmKey Size
RS2562048 bits
PS2562048 bits
RS3843072 bits
PS3843072 bits
RS5124096 bits
PS5124096 bits
* *

If the {@code alg} argument is an Elliptic Curve algorithm, a KeyPair is generated based on the following:

* * * * * * * * * * * * * * * * * * * * * * * * * * *
JWA AlgorithmKey SizeJWA Curve NameASN1 OID Curve Name
EC256256 bits{@code P-256}{@code secp256r1}
EC384384 bits{@code P-384}{@code secp384r1}
EC512512 bits{@code P-521}{@code secp521r1}
* * @param alg the {@code SignatureAlgorithm} to inspect to determine which asymmetric algorithm to use. * @return a new {@link KeyPair} suitable for use with the specified asymmetric algorithm. * @throws IllegalArgumentException if {@code alg} is not an asymmetric algorithm */ public static KeyPair keyPairFor(SignatureAlgorithm alg) throws IllegalArgumentException { Assert.notNull(alg, "SignatureAlgorithm cannot be null."); switch (alg) { case RS256: case PS256: case RS384: case PS384: case RS512: case PS512: return Classes.invokeStatic(RSA, "generateKeyPair", SIG_ARG_TYPES, alg); case ES256: case ES384: case ES512: return Classes.invokeStatic(EC, "generateKeyPair", SIG_ARG_TYPES, alg); default: String msg = "The " + alg.name() + " algorithm does not support Key Pairs."; throw new IllegalArgumentException(msg); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy