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