001package com.nimbusds.jose.crypto; 002 003 004import javax.crypto.SecretKey; 005 006import com.nimbusds.jose.DefaultJWEHeaderFilter; 007import com.nimbusds.jose.EncryptionMethod; 008import com.nimbusds.jose.JOSEException; 009import com.nimbusds.jose.JWEAlgorithm; 010import com.nimbusds.jose.JWEDecrypter; 011import com.nimbusds.jose.JWEHeaderFilter; 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#A256CBC_HS512} 032 * <li>{@link com.nimbusds.jose.EncryptionMethod#A128GCM} 033 * <li>{@link com.nimbusds.jose.EncryptionMethod#A256GCM} 034 * </ul> 035 * 036 * <p>Accepts all {@link com.nimbusds.jose.JWEHeader#getRegisteredParameterNames 037 * registered JWE header parameters}. Modify the {@link #getJWEHeaderFilter 038 * header filter} properties to restrict the acceptable JWE algorithms, 039 * encryption methods and header parameters, or to allow custom JWE header 040 * parameters. 041 * 042 * @author Vladimir Dzhuvinov 043 * @version $version$ (2013-10-07) 044 * 045 */ 046public class DirectDecrypter extends DirectCryptoProvider implements JWEDecrypter { 047 048 049 /** 050 * The JWE header filter. 051 */ 052 private final DefaultJWEHeaderFilter headerFilter; 053 054 055 /** 056 * Creates a new direct decrypter. 057 * 058 * @param key The shared symmetric key. Its algorithm must be "AES". 059 * Must be 128 bits (16 bytes), 256 bits (32 bytes) or 512 060 * bits (64 bytes) long. Must not be {@code null}. 061 * 062 * @throws JOSEException If the key length is unexpected. 063 */ 064 public DirectDecrypter(final SecretKey key) 065 throws JOSEException { 066 067 super(key); 068 069 headerFilter = new DefaultJWEHeaderFilter(supportedAlgorithms(), supportedEncryptionMethods()); 070 } 071 072 073 /** 074 * Creates a new direct decrypter. 075 * 076 * @param keyBytes The shared symmetric key, as a byte array. Must be 077 * 128 bits (16 bytes), 256 bits (32 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 byte[] keyBytes) 083 throws JOSEException { 084 085 super(keyBytes); 086 087 headerFilter = new DefaultJWEHeaderFilter(supportedAlgorithms(), supportedEncryptionMethods()); 088 } 089 090 091 @Override 092 public JWEHeaderFilter getJWEHeaderFilter() { 093 094 return headerFilter; 095 } 096 097 098 @Override 099 public byte[] decrypt(final ReadOnlyJWEHeader readOnlyJWEHeader, 100 final Base64URL encryptedKey, 101 final Base64URL iv, 102 final Base64URL cipherText, 103 final Base64URL authTag) 104 throws JOSEException { 105 106 // Validate required JWE parts 107 if (encryptedKey != null) { 108 109 throw new JOSEException("Unexpected encrypted key, must be omitted"); 110 } 111 112 if (iv == null) { 113 114 throw new JOSEException("The initialization vector (IV) must not be null"); 115 } 116 117 if (authTag == null) { 118 119 throw new JOSEException("The authentication tag must not be null"); 120 } 121 122 123 JWEAlgorithm alg = readOnlyJWEHeader.getAlgorithm(); 124 125 if (! alg.equals(JWEAlgorithm.DIR)) { 126 127 throw new JOSEException("Unsupported algorithm, must be \"dir\""); 128 } 129 130 // Compose the AAD 131 byte[] aad = StringUtils.toByteArray(readOnlyJWEHeader.toBase64URL().toString()); 132 133 // Decrypt the cipher text according to the JWE enc 134 EncryptionMethod enc = readOnlyJWEHeader.getEncryptionMethod(); 135 136 byte[] plainText; 137 138 if (enc.equals(EncryptionMethod.A128CBC_HS256) || enc.equals(EncryptionMethod.A256CBC_HS512)) { 139 140 plainText = AESCBC.decryptAuthenticated(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode()); 141 142 } else if (enc.equals(EncryptionMethod.A128GCM) || enc.equals(EncryptionMethod.A256GCM)) { 143 144 plainText = AESGCM.decrypt(getKey(), iv.decode(), cipherText.decode(), aad, authTag.decode()); 145 146 } else { 147 148 throw new JOSEException("Unsupported encryption method, must be A128CBC_HS256, A256CBC_HS512, A128GCM or A128GCM"); 149 } 150 151 152 // Apply decompression if requested 153 return DeflateHelper.applyDecompression(readOnlyJWEHeader, plainText); 154 } 155} 156