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 javax.crypto.SecretKey; 022 023import com.nimbusds.jose.JOSEException; 024import net.jcip.annotations.ThreadSafe; 025import org.bouncycastle.crypto.BlockCipher; 026import org.bouncycastle.crypto.CipherParameters; 027import org.bouncycastle.crypto.InvalidCipherTextException; 028import org.bouncycastle.crypto.engines.AESEngine; 029import org.bouncycastle.crypto.modes.GCMBlockCipher; 030import org.bouncycastle.crypto.params.AEADParameters; 031import org.bouncycastle.crypto.params.KeyParameter; 032 033 034/** 035 * Legacy AES/GSM/NoPadding encryption and decryption methods. Uses the 036 * BouncyCastle.org API. This class is thread-safe. 037 * 038 * @author Vladimir Dzhuvinov 039 * @author Axel Nennker 040 * @version 2015-11-15 041 */ 042@ThreadSafe 043public class LegacyAESGCM { 044 045 046 /** 047 * The standard authentication tag length (128 bits). 048 */ 049 public static final int AUTH_TAG_BIT_LENGTH = 128; 050 051 052 /** 053 * Creates a new AES cipher. 054 * 055 * @param secretKey The AES key. Must not be {@code null}. 056 * @param forEncryption If {@code true} creates an AES encryption 057 * cipher, else creates an AES decryption 058 * cipher. 059 * 060 * @return The AES cipher. 061 */ 062 public static AESEngine createAESCipher(final SecretKey secretKey, 063 final boolean forEncryption) { 064 065 AESEngine cipher = new AESEngine(); 066 067 CipherParameters cipherParams = new KeyParameter(secretKey.getEncoded()); 068 069 cipher.init(forEncryption, cipherParams); 070 071 return cipher; 072 } 073 074 075 /** 076 * Creates a new AES/GCM/NoPadding cipher. 077 * 078 * @param secretKey The AES key. Must not be {@code null}. 079 * @param forEncryption If {@code true} creates an encryption cipher, 080 * else creates a decryption cipher. 081 * @param iv The initialisation vector (IV). Must not be 082 * {@code null}. 083 * @param authData The authenticated data. Must not be 084 * {@code null}. 085 * 086 * @return The AES/GCM/NoPadding cipher. 087 */ 088 private static GCMBlockCipher createAESGCMCipher(final SecretKey secretKey, 089 final boolean forEncryption, 090 final byte[] iv, 091 final byte[] authData) { 092 093 // Initialise AES cipher 094 BlockCipher cipher = createAESCipher(secretKey, forEncryption); 095 096 // Create GCM cipher with AES 097 GCMBlockCipher gcm = new GCMBlockCipher(cipher); 098 099 AEADParameters aeadParams = new AEADParameters(new KeyParameter(secretKey.getEncoded()), 100 AUTH_TAG_BIT_LENGTH, 101 iv, 102 authData); 103 gcm.init(forEncryption, aeadParams); 104 105 return gcm; 106 } 107 108 109 /** 110 * Encrypts the specified plain text using AES/GCM/NoPadding. 111 * 112 * @param secretKey The AES key. Must not be {@code null}. 113 * @param plainText The plain text. Must not be {@code null}. 114 * @param iv The initialisation vector (IV). Must not be 115 * {@code null}. 116 * @param authData The authenticated data. Must not be {@code null}. 117 * 118 * @return The authenticated cipher text. 119 * 120 * @throws JOSEException If encryption failed. 121 */ 122 public static AuthenticatedCipherText encrypt(final SecretKey secretKey, 123 final byte[] iv, 124 final byte[] plainText, 125 final byte[] authData) 126 throws JOSEException { 127 128 // Initialise AES/GCM cipher for encryption 129 GCMBlockCipher cipher = createAESGCMCipher(secretKey, true, iv, authData); 130 131 132 // Prepare output buffer 133 int outputLength = cipher.getOutputSize(plainText.length); 134 byte[] output = new byte[outputLength]; 135 136 137 // Produce cipher text 138 int outputOffset = cipher.processBytes(plainText, 0, plainText.length, output, 0); 139 140 141 // Produce authentication tag 142 try { 143 outputOffset += cipher.doFinal(output, outputOffset); 144 145 } catch (InvalidCipherTextException e) { 146 147 throw new JOSEException("Couldn't generate GCM authentication tag: " + e.getMessage(), e); 148 } 149 150 // Split output into cipher text and authentication tag 151 int authTagLength = AUTH_TAG_BIT_LENGTH / 8; 152 153 byte[] cipherText = new byte[outputOffset - authTagLength]; 154 byte[] authTag = new byte[authTagLength]; 155 156 System.arraycopy(output, 0, cipherText, 0, cipherText.length); 157 System.arraycopy(output, outputOffset - authTagLength, authTag, 0, authTag.length); 158 159 return new AuthenticatedCipherText(cipherText, authTag); 160 } 161 162 163 /** 164 * Decrypts the specified cipher text using AES/GCM/NoPadding. 165 * 166 * @param secretKey The AES key. Must not be {@code null}. 167 * @param iv The initialisation vector (IV). Must not be 168 * {@code null}. 169 * @param cipherText The cipher text. Must not be {@code null}. 170 * @param authData The authenticated data. Must not be {@code null}. 171 * @param authTag The authentication tag. Must not be {@code null}. 172 * 173 * @return The decrypted plain text. 174 * 175 * @throws JOSEException If decryption failed. 176 */ 177 public static byte[] decrypt(final SecretKey secretKey, 178 final byte[] iv, 179 final byte[] cipherText, 180 final byte[] authData, 181 final byte[] authTag) 182 throws JOSEException { 183 184 // Initialise AES/GCM cipher for decryption 185 GCMBlockCipher cipher = createAESGCMCipher(secretKey, false, iv, authData); 186 187 188 // Join cipher text and authentication tag to produce cipher input 189 byte[] input = new byte[cipherText.length + authTag.length]; 190 191 System.arraycopy(cipherText, 0, input, 0, cipherText.length); 192 System.arraycopy(authTag, 0, input, cipherText.length, authTag.length); 193 194 int outputLength = cipher.getOutputSize(input.length); 195 196 byte[] output = new byte[outputLength]; 197 198 199 // Decrypt 200 int outputOffset = cipher.processBytes(input, 0, input.length, output, 0); 201 202 // Validate authentication tag 203 try { 204 outputOffset += cipher.doFinal(output, outputOffset); 205 206 } catch (InvalidCipherTextException e) { 207 208 throw new JOSEException("Couldn't validate GCM authentication tag: " + e.getMessage(), e); 209 } 210 211 return output; 212 } 213 214 215 /** 216 * Prevents public instantiation. 217 */ 218 private LegacyAESGCM() { } 219}