001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
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.impl;
019
020
021import com.nimbusds.jose.EncryptionMethod;
022import com.nimbusds.jose.JOSEException;
023import com.nimbusds.jose.JWEAlgorithm;
024import com.nimbusds.jose.JWEProvider;
025import com.nimbusds.jose.jca.JWEJCAContext;
026
027import javax.crypto.SecretKey;
028import java.util.Arrays;
029import java.util.Collections;
030import java.util.HashSet;
031import java.util.Set;
032
033
034/**
035 * The base abstract class for JSON Web Encryption (JWE) encrypters and
036 * decrypters.
037 *
038 * @author Vladimir Dzhuvinov
039 * @version 2023-09-18
040 */
041public abstract class BaseJWEProvider implements JWEProvider {
042
043
044        /**
045         * The acceptable CEK algorithms.
046         */
047        private static final Set<String> ACCEPTABLE_CEK_ALGS = Collections.unmodifiableSet(
048                new HashSet<>(Arrays.asList("AES", "ChaCha20"))
049        );
050
051
052        /**
053         * The supported algorithms by the JWE provider instance.
054         */
055        private final Set<JWEAlgorithm> algs;
056
057
058        /**
059         * The supported encryption methods by the JWE provider instance.
060         */
061        private final Set<EncryptionMethod> encs;
062
063
064        /**
065         * The JWE JCA context.
066         */
067        private final JWEJCAContext jcaContext = new JWEJCAContext();
068
069
070        /**
071         * The externally supplied AES content encryption key (CEK) to use,
072         * {@code null} to generate a CEK for each JWE.
073         */
074        private final SecretKey cek;
075
076
077        /**
078         * Creates a new base JWE provider.
079         *
080         * @param algs The supported algorithms by the JWE provider instance.
081         *             Must not be {@code null}.
082         * @param encs The supported encryption methods by the JWE provider
083         *             instance. Must not be {@code null}.
084         */
085        public BaseJWEProvider(final Set<JWEAlgorithm> algs,
086                               final Set<EncryptionMethod> encs) {
087
088                this(algs, encs, null);
089        }
090
091
092        /**
093         * Creates a new base JWE provider.
094         *
095         * @param algs The supported algorithms by the JWE provider instance.
096         *             Must not be {@code null}.
097         * @param encs The supported encryption methods by the JWE provider
098         *             instance. Must not be {@code null}.
099         * @param cek  The content encryption key (CEK) to use. If specified
100         *             its algorithm must be "AES" or "ChaCha20" and its length
101         *             must match the expected for the JWE encryption method
102         *             ("enc"). If {@code null} a CEK will be generated for
103         *             each JWE.
104         */
105        public BaseJWEProvider(final Set<JWEAlgorithm> algs,
106                               final Set<EncryptionMethod> encs,
107                               final SecretKey cek) {
108
109                if (algs == null) {
110                        throw new IllegalArgumentException("The supported JWE algorithm set must not be null");
111                }
112
113                this.algs = Collections.unmodifiableSet(algs);
114
115
116                if (encs == null) {
117                        throw new IllegalArgumentException("The supported encryption methods must not be null");
118                }
119
120                this.encs = encs;
121
122                if (cek != null && algs.size() > 1 && (cek.getAlgorithm() == null || ! ACCEPTABLE_CEK_ALGS.contains(cek.getAlgorithm()))) {
123                        throw new IllegalArgumentException("The algorithm of the content encryption key (CEK) must be AES or ChaCha20");
124                }
125
126                this.cek = cek;
127        }
128
129
130        @Override
131        public Set<JWEAlgorithm> supportedJWEAlgorithms() {
132
133                return algs;
134        }
135
136
137        @Override
138        public Set<EncryptionMethod> supportedEncryptionMethods() {
139
140                return encs;
141        }
142
143
144        @Override
145        public JWEJCAContext getJCAContext() {
146
147                return jcaContext;
148        }
149
150
151        /**
152         * Returns {@code true} if a content encryption key (CEK) was
153         * provided at construction time.
154         *
155         * @return {@code true} if a CEK was provided at construction time,
156         *         {@code false} if CEKs will be internally generated.
157         */
158        protected boolean isCEKProvided() {
159                return cek != null;
160        }
161
162
163        /**
164         * Returns the content encryption key (CEK) to use. Unless a CEK was
165         * provided at construction time this will be a new internally
166         * generated CEK.
167         *
168         * @param enc The encryption method. Must not be {@code null}.
169         *
170         * @return The content encryption key (CEK).
171         *
172         * @throws JOSEException If an internal exception is encountered.
173         */
174        protected SecretKey getCEK(final EncryptionMethod enc)
175                throws JOSEException {
176
177                return (isCEKProvided() || enc == null) ? cek : ContentCryptoProvider.generateCEK(enc, jcaContext.getSecureRandom());
178        }
179}
180