001package com.nimbusds.jose.crypto; 002 003 004import java.util.HashSet; 005import java.util.Set; 006import javax.crypto.SecretKey; 007 008import com.nimbusds.jose.EncryptionMethod; 009import com.nimbusds.jose.JOSEException; 010import com.nimbusds.jose.JWEAlgorithm; 011import com.nimbusds.jose.JWEDecrypter; 012import com.nimbusds.jose.JWEHeader; 013import com.nimbusds.jose.util.Base64URL; 014import com.nimbusds.jose.util.StringUtils; 015import net.jcip.annotations.ThreadSafe; 016 017 018/** 019 * Direct decrypter of {@link com.nimbusds.jose.JWEObject JWE objects} with a 020 * shared symmetric key. This class is thread-safe. 021 * 022 * <p>Supports the following JWE algorithms: 023 * 024 * <ul> 025 * <li>{@link com.nimbusds.jose.JWEAlgorithm#DIR} 026 * </ul> 027 * 028 * <p>Supports the following encryption methods: 029 * 030 * <ul> 031 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128CBC_HS256} 032 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192CBC_HS384} 033 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256CBC_HS512} 034 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 035 * <li>{@link com.nimbusds.jose.EncryptionMethod#A192GCM} 036 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 037 * </ul> 038 * 039 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames 040 * registered JWE header parameters}. Use {@link #setAcceptedAlgorithms} and 041 * {@link #setAcceptedEncryptionMethods} to restrict the acceptable JWE 042 * algorithms and encryption methods. 043 * 044 * @author Vladimir Dzhuvinov 045 * @version $version$ (2014-07-08) 046 */ 047@ThreadSafe 048public class DirectDecrypter extends DirectCryptoProvider implements JWEDecrypter { 049 050 051 /** 052 * The accepted JWE algorithms. 053 */ 054 private Set<JWEAlgorithm> acceptedAlgs = 055 new HashSet<>(supportedAlgorithms()); 056 057 058 /** 059 * The accepted encryption methods. 060 */ 061 private Set<EncryptionMethod> acceptedEncs = 062 new HashSet<>(supportedEncryptionMethods()); 063 064 065 /** 066 * The critical header parameter checker. 067 */ 068 private final CriticalHeaderParameterChecker critParamChecker = 069 new CriticalHeaderParameterChecker(); 070 071 072 /** 073 * Creates a new direct decrypter. 074 * 075 * @param key The shared symmetric key. Its algorithm must be "AES". 076 * Must be 128 bits (16 bytes), 192 bits (24 bytes), 256 077 * bits (32 bytes), 384 bits (48 bytes) or 512 bits 078 * (64 bytes) long. Must not be {@code null}. 079 * 080 * @throws JOSEException If the key length is unexpected. 081 */ 082 public DirectDecrypter(final SecretKey key) 083 throws JOSEException { 084 085 super(key); 086 } 087 088 089 /** 090 * Creates a new direct decrypter. 091 * 092 * @param keyBytes The shared symmetric key, as a byte array. Must be 093 * 128 bits (16 bytes), 192 bits (24 bytes), 256 bits 094 * (32 bytes), 384 bits (48 bytes) or 512 bits (64 095 * bytes) long. Must not be {@code null}. 096 * 097 * @throws JOSEException If the key length is unexpected. 098 */ 099 public DirectDecrypter(final byte[] keyBytes) 100 throws JOSEException { 101 102 super(keyBytes); 103 } 104 105 106 @Override 107 public Set<JWEAlgorithm> getAcceptedAlgorithms() { 108 109 return acceptedAlgs; 110 } 111 112 113 @Override 114 public void setAcceptedAlgorithms(final Set<JWEAlgorithm> acceptedAlgs) { 115 116 if (acceptedAlgs == null) { 117 throw new IllegalArgumentException("The accepted JWE algorithms must not be null"); 118 } 119 120 if (! supportedAlgorithms().containsAll(acceptedAlgs)) { 121 throw new IllegalArgumentException("Unsupported JWE algorithm(s)"); 122 } 123 124 this.acceptedAlgs = acceptedAlgs; 125 } 126 127 128 @Override 129 public Set<EncryptionMethod> getAcceptedEncryptionMethods() { 130 131 return acceptedEncs; 132 } 133 134 135 @Override 136 public void setAcceptedEncryptionMethods(final Set<EncryptionMethod> acceptedEncs) { 137 138 if (acceptedEncs == null) 139 throw new IllegalArgumentException("The accepted encryption methods must not be null"); 140 141 if (!supportedEncryptionMethods().containsAll(acceptedEncs)) { 142 throw new IllegalArgumentException("Unsupported encryption method(s)"); 143 } 144 145 this.acceptedEncs = acceptedEncs; 146 } 147 148 149 @Override 150 public Set<String> getIgnoredCriticalHeaderParameters() { 151 152 return critParamChecker.getIgnoredCriticalHeaders(); 153 } 154 155 156 @Override 157 public void setIgnoredCriticalHeaderParameters(final Set<String> headers) { 158 159 critParamChecker.setIgnoredCriticalHeaders(headers); 160 } 161 162 163 @Override 164 public byte[] decrypt(final JWEHeader header, 165 final Base64URL encryptedKey, 166 final Base64URL iv, 167 final Base64URL cipherText, 168 final Base64URL authTag) 169 throws JOSEException { 170 171 // Validate required JWE parts 172 if (encryptedKey != null) { 173 174 throw new JOSEException("Unexpected encrypted key, must be omitted"); 175 } 176 177 if (iv == null) { 178 179 throw new JOSEException("The initialization vector (IV) must not be null"); 180 } 181 182 if (authTag == null) { 183 184 throw new JOSEException("The authentication tag must not be null"); 185 } 186 187 188 JWEAlgorithm alg = header.getAlgorithm(); 189 190 if (! alg.equals(JWEAlgorithm.DIR)) { 191 192 throw new JOSEException("Unsupported algorithm, must be \"dir\""); 193 } 194 195 if (! critParamChecker.headerPasses(header)) { 196 197 throw new JOSEException("Unsupported critical header parameter"); 198 } 199 200 // Compose the AAD 201 byte[] aad = StringUtils.toByteArray(header.toBase64URL().toString()); 202 203 // Decrypt the cipher text according to the JWE enc 204 EncryptionMethod enc = header.getEncryptionMethod(); 205 206 byte[] plainText; 207 208 if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A192CBC_HS384) || enc.equals(EncryptionMethod.A256CBC_HS512)) { 209 210 plainText = AESCBC.decryptAuthenticated(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode(), contentEncryptionProvider, macProvider); 211 212 } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A192GCM) || enc.equals(EncryptionMethod.A256GCM)) { 213 214 plainText = AESGCM.decrypt(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode(), contentEncryptionProvider); 215 216 } else { 217 218 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM or A128GCM"); 219 } 220 221 222 // Apply decompression if requested 223 return DeflateHelper.applyDecompression(header, plainText); 224 } 225} 226