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

io.jsonwebtoken.impl.security.AbstractJwkBuilder Maven / Gradle / Ivy

/*
 * Copyright (C) 2021 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.impl.security;

import io.jsonwebtoken.impl.lang.DefaultNestedCollection;
import io.jsonwebtoken.impl.lang.DelegatingMapMutator;
import io.jsonwebtoken.impl.lang.IdRegistry;
import io.jsonwebtoken.impl.lang.Parameter;
import io.jsonwebtoken.impl.lang.Parameters;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.NestedCollection;
import io.jsonwebtoken.lang.Registry;
import io.jsonwebtoken.security.HashAlgorithm;
import io.jsonwebtoken.security.Jwk;
import io.jsonwebtoken.security.JwkBuilder;
import io.jsonwebtoken.security.Jwks;
import io.jsonwebtoken.security.KeyOperation;
import io.jsonwebtoken.security.KeyOperationPolicy;
import io.jsonwebtoken.security.MalformedKeyException;
import io.jsonwebtoken.security.SecretJwk;
import io.jsonwebtoken.security.SecretJwkBuilder;

import javax.crypto.SecretKey;
import java.security.Key;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Set;

abstract class AbstractJwkBuilder, T extends JwkBuilder>
        extends DelegatingMapMutator, T>
        implements JwkBuilder {

    protected final JwkFactory jwkFactory;

    static final KeyOperationPolicy DEFAULT_OPERATION_POLICY = Jwks.OP.policy().build();

    protected KeyOperationPolicy opsPolicy = DEFAULT_OPERATION_POLICY; // default

    @SuppressWarnings("unchecked")
    protected AbstractJwkBuilder(JwkContext jwkContext) {
        this(jwkContext, (JwkFactory) DispatchingJwkFactory.DEFAULT_INSTANCE);
    }

    // visible for testing
    protected AbstractJwkBuilder(JwkContext context, JwkFactory factory) {
        super(context);
        this.jwkFactory = Assert.notNull(factory, "JwkFactory cannot be null.");
    }

    @SuppressWarnings("unchecked")
    protected  JwkContext newContext(A key) {
        return (JwkContext) this.jwkFactory.newContext(this.DELEGATE, (K) key);
    }

    @Override
    public T provider(Provider provider) {
        this.DELEGATE.setProvider(provider);
        return self();
    }

    @Override
    public T random(SecureRandom random) {
        this.DELEGATE.setRandom(random);
        return self();
    }

    @Override
    public T algorithm(String alg) {
        Assert.hasText(alg, "Algorithm cannot be null or empty.");
        this.DELEGATE.setAlgorithm(alg);
        return self();
    }

    @Override
    public T id(String id) {
        Assert.hasText(id, "Id cannot be null or empty.");
        this.DELEGATE.setIdThumbprintAlgorithm(null); //clear out any previously set value
        this.DELEGATE.setId(id);
        return self();
    }

    @Override
    public T idFromThumbprint() {
        return idFromThumbprint(Jwks.HASH.SHA256);
    }

    @Override
    public T idFromThumbprint(HashAlgorithm alg) {
        Assert.notNull(alg, "Thumbprint HashAlgorithm cannot be null.");
        Assert.notNull(alg.getId(), "Thumbprint HashAlgorithm ID cannot be null.");
        this.DELEGATE.setId(null); // clear out any previous value
        this.DELEGATE.setIdThumbprintAlgorithm(alg);
        return self();
    }

    @Override
    public NestedCollection operations() {
        return new DefaultNestedCollection(self(), this.DELEGATE.getOperations()) {
            @Override
            protected void changed() {
                Collection c = getCollection();
                opsPolicy.validate(c);
                DELEGATE.setOperations(c);
            }
        };
    }

    @Override
    public T operationPolicy(KeyOperationPolicy policy) throws IllegalArgumentException {
        Assert.notNull(policy, "Policy cannot be null.");
        Collection ops = policy.getOperations();
        Assert.notEmpty(ops, "Policy operations cannot be null or empty.");
        this.opsPolicy = policy;

        // update the JWK internal param to enable the policy's values:
        Registry registry = new IdRegistry<>("JSON Web Key Operation", ops);
        Parameter> param = Parameters.builder(KeyOperation.class)
                .setConverter(new KeyOperationConverter(registry)).set()
                .setId(AbstractJwk.KEY_OPS.getId())
                .setName(AbstractJwk.KEY_OPS.getName())
                .build();
        setDelegate(this.DELEGATE.parameter(param));
        return self();
    }

    @Override
    public J build() {

        //should always exist as there isn't a way to set it outside the constructor:
        Assert.stateNotNull(this.DELEGATE, "JwkContext should always be non-null");

        K key = this.DELEGATE.getKey();
        if (key == null && isEmpty()) {
            String msg = "A " + Key.class.getName() + " or one or more name/value pairs must be provided to create a JWK.";
            throw new IllegalStateException(msg);
        }

        try {
            this.opsPolicy.validate(this.DELEGATE.get(AbstractJwk.KEY_OPS));
            return jwkFactory.createJwk(this.DELEGATE);
        } catch (IllegalArgumentException iae) {
            //if we get an IAE, it means the builder state wasn't configured enough in order to create
            String msg = "Unable to create JWK: " + iae.getMessage();
            throw new MalformedKeyException(msg, iae);
        }
    }

    static class DefaultSecretJwkBuilder extends AbstractJwkBuilder
            implements SecretJwkBuilder {
        public DefaultSecretJwkBuilder(JwkContext ctx) {
            super(ctx);
            // assign a standard algorithm if possible:
            Key key = Assert.notNull(ctx.getKey(), "SecretKey cannot be null.");
            DefaultMacAlgorithm mac = DefaultMacAlgorithm.findByKey(key);
            if (mac != null) {
                algorithm(mac.getId());
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy