001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2020, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.crypto.factories;
019
020
021import com.nimbusds.jose.JOSEException;
022import com.nimbusds.jose.JWSAlgorithm;
023import com.nimbusds.jose.JWSSigner;
024import com.nimbusds.jose.crypto.ECDSASigner;
025import com.nimbusds.jose.crypto.Ed25519Signer;
026import com.nimbusds.jose.crypto.MACSigner;
027import com.nimbusds.jose.crypto.RSASSASigner;
028import com.nimbusds.jose.jca.JCAContext;
029import com.nimbusds.jose.jwk.*;
030import com.nimbusds.jose.produce.JWSSignerFactory;
031
032import java.util.Collections;
033import java.util.LinkedHashSet;
034import java.util.Set;
035
036/**
037 * A factory to create JWS signers from a JWK instance based on the
038 * key type.
039 *
040 * @author Justin Richer
041 * @since 2024-05-07
042 */
043public class DefaultJWSSignerFactory implements JWSSignerFactory {
044
045        /**
046         * The JCA context.
047         */
048        private final JCAContext jcaContext = new JCAContext();
049
050        /**
051         * The supported JWS algorithms.
052         */
053        public static final Set<JWSAlgorithm> SUPPORTED_ALGORITHMS;
054
055
056        static {
057                Set<JWSAlgorithm> algs = new LinkedHashSet<>();
058                algs.addAll(MACSigner.SUPPORTED_ALGORITHMS);
059                algs.addAll(RSASSASigner.SUPPORTED_ALGORITHMS);
060                algs.addAll(ECDSASigner.SUPPORTED_ALGORITHMS);
061                algs.addAll(Ed25519Signer.SUPPORTED_ALGORITHMS);
062                SUPPORTED_ALGORITHMS = Collections.unmodifiableSet(algs);
063        }
064
065        @Override
066        public Set<JWSAlgorithm> supportedJWSAlgorithms() {
067                return SUPPORTED_ALGORITHMS;
068        }
069
070        @Override
071        public JCAContext getJCAContext() {
072                return jcaContext;
073        }
074
075        @Override
076        public JWSSigner createJWSSigner(final JWK key) throws JOSEException {
077
078                if (!key.isPrivate()) { // can't create a signer without the private key
079                        throw JWKException.expectedPrivate();
080                }
081                
082                if (key.getKeyUse() != null && ! KeyUse.SIGNATURE.equals(key.getKeyUse())) {
083                        throw new JWKException("The JWK use must be sig (signature) or unspecified");
084                }
085
086                JWSSigner signer;
087
088                // base this just on the key type (+ curve) alone without the algorithm check
089                if (key instanceof OctetSequenceKey) {
090                        signer = new MACSigner((OctetSequenceKey)key);
091                } else if (key instanceof RSAKey) {
092                        signer = new RSASSASigner((RSAKey)key);
093                } else if (key instanceof ECKey && ECDSASigner.SUPPORTED_CURVES.contains(((ECKey) key).getCurve())) {
094                        signer = new ECDSASigner((ECKey)key);
095                } else if (key instanceof OctetKeyPair && Ed25519Signer.SUPPORTED_CURVES.contains(((OctetKeyPair) key).getCurve())) {
096                        signer = new Ed25519Signer((OctetKeyPair)key);
097                } else {
098                        throw new JOSEException("Unsupported JWK type and / or curve");
099                }
100
101                // Apply JCA context
102                signer.getJCAContext().setSecureRandom(jcaContext.getSecureRandom());
103                signer.getJCAContext().setProvider(jcaContext.getProvider());
104
105                return signer;
106        }
107
108        @Override
109        public JWSSigner createJWSSigner(final JWK key, final JWSAlgorithm alg) throws JOSEException {
110
111                if (!key.isPrivate()) { // can't create a signer without the private key
112                        throw JWKException.expectedPrivate();
113                }
114                
115                if (key.getKeyUse() != null && ! KeyUse.SIGNATURE.equals(key.getKeyUse())) {
116                        throw new JWKException("The JWK use must be sig (signature) or unspecified");
117                }
118
119                JWSSigner signer;
120
121
122                if (
123                        MACSigner.SUPPORTED_ALGORITHMS.contains(alg) &&
124                        key instanceof OctetSequenceKey) {
125
126                        signer = new MACSigner((OctetSequenceKey)key);
127
128                } else if (
129                        RSASSASigner.SUPPORTED_ALGORITHMS.contains(alg) &&
130                        key instanceof RSAKey) {
131
132                        signer = new RSASSASigner((RSAKey)key);
133
134                } else if (
135                        ECDSASigner.SUPPORTED_ALGORITHMS.contains(alg) &&
136                        key instanceof ECKey &&
137                        ECDSASigner.SUPPORTED_CURVES.contains(((ECKey) key).getCurve())) {
138
139                        signer = new ECDSASigner((ECKey)key);
140
141                } else if (
142                        Ed25519Signer.SUPPORTED_ALGORITHMS.contains(alg) &&
143                        key instanceof OctetKeyPair &&
144                        Ed25519Signer.SUPPORTED_CURVES.contains(((OctetKeyPair) key).getCurve())) {
145
146                        signer = new Ed25519Signer((OctetKeyPair)key);
147
148                } else {
149                        throw new JOSEException("Unsupported JWK type, JWK curve and / or JWS algorithm");
150                }
151
152                // Apply JCA context
153                signer.getJCAContext().setSecureRandom(jcaContext.getSecureRandom());
154                signer.getJCAContext().setProvider(jcaContext.getProvider());
155
156                return signer;
157        }
158}