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
020import java.util.Collections;
021import java.util.LinkedHashSet;
022import java.util.Set;
023
024import com.nimbusds.jose.JOSEException;
025import com.nimbusds.jose.jwk.JWKException;
026import com.nimbusds.jose.JWSAlgorithm;
027import com.nimbusds.jose.JWSSigner;
028import com.nimbusds.jose.crypto.ECDSASigner;
029import com.nimbusds.jose.crypto.Ed25519Signer;
030import com.nimbusds.jose.crypto.MACSigner;
031import com.nimbusds.jose.crypto.RSASSASigner;
032import com.nimbusds.jose.jca.JCAContext;
033import com.nimbusds.jose.jwk.*;
034import com.nimbusds.jose.produce.JWSSignerFactory;
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 2020-03-29
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 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) {
094                        signer = new ECDSASigner((ECKey)key);
095                } else if (key instanceof OctetKeyPair) {
096                        signer = new Ed25519Signer((OctetKeyPair)key);
097                } else {
098                        throw new JOSEException("Unsupported JWK type: " + key);
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 (MACSigner.SUPPORTED_ALGORITHMS.contains(alg)) {
123
124                        if (!(key instanceof OctetSequenceKey)) {
125                                throw JWKException.expectedClass(OctetSequenceKey.class);
126                        }
127
128                        signer = new MACSigner((OctetSequenceKey)key);
129                } else if (RSASSASigner.SUPPORTED_ALGORITHMS.contains(alg)) {
130
131                        if (!(key instanceof RSAKey)) {
132                                throw JWKException.expectedClass(RSAKey.class);
133                        }
134
135                        signer = new RSASSASigner((RSAKey)key);
136                } else if (ECDSASigner.SUPPORTED_ALGORITHMS.contains(alg)) {
137
138                        if (!(key instanceof ECKey)) {
139                                throw JWKException.expectedClass(ECKey.class);
140                        }
141
142                        signer = new ECDSASigner((ECKey)key);
143                } else if (Ed25519Signer.SUPPORTED_ALGORITHMS.contains(alg)) {
144
145                        if (!(key instanceof OctetKeyPair)) {
146                                throw JWKException.expectedClass(OctetKeyPair.class);
147                        }
148
149                        signer = new Ed25519Signer((OctetKeyPair)key);
150
151                } else {
152                        throw new JOSEException("Unsupported JWS algorithm: " + alg);
153                }
154
155                // Apply JCA context
156                signer.getJCAContext().setSecureRandom(jcaContext.getSecureRandom());
157                signer.getJCAContext().setProvider(jcaContext.getProvider());
158
159                return signer;
160        }
161}