001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.pageStore.crypt; 018 019import java.security.AlgorithmParameters; 020import java.security.GeneralSecurityException; 021import java.security.SecureRandom; 022import java.util.Arrays; 023 024import javax.crypto.Cipher; 025import javax.crypto.KeyGenerator; 026import javax.crypto.SecretKey; 027 028import org.apache.wicket.WicketRuntimeException; 029import org.bouncycastle.jcajce.spec.AEADParameterSpec; 030 031/** 032 * Encryption and decryption implementation using AES-256-GCM-SIV authenticated encryption. 033 * 034 * This implementation requires Bouncy Castle. It is more secure than the {@link DefaultCrypter}, 035 * but also more expensive. Simple measurements have shown {@link DefaultCrypter} to be about 10 to 036 * 15 times faster than this implementation. This is likely caused by not-so-optimal implementation 037 * of the algorithm in Java by BC. When the JDK gets support for GCM-SIV 038 * (https://bugs.openjdk.org/browse/JDK-8256530), this implementation will likely be faster than or 039 * about as fast as CBC. 040 */ 041public class GCMSIVCrypter implements ICrypter 042{ 043 protected Cipher getCipher() throws GeneralSecurityException 044 { 045 return Cipher.getInstance("AES/GCM-SIV/NoPadding"); 046 } 047 048 @Override 049 public SecretKey generateKey(SecureRandom random) 050 { 051 try 052 { 053 KeyGenerator generator = KeyGenerator.getInstance("AES"); 054 generator.init(256, random); 055 return generator.generateKey(); 056 } 057 catch (GeneralSecurityException ex) 058 { 059 throw new WicketRuntimeException(ex); 060 } 061 } 062 063 @Override 064 public byte[] encrypt(byte[] decrypted, SecretKey key, SecureRandom random) 065 { 066 try 067 { 068 Cipher cipher = getCipher(); 069 cipher.init(Cipher.ENCRYPT_MODE, key, random); 070 071 AlgorithmParameters params = cipher.getParameters(); 072 byte[] nonce = params.getParameterSpec(AEADParameterSpec.class).getNonce(); 073 byte[] ciphertext = cipher.doFinal(decrypted); 074 075 byte[] encrypted = Arrays.copyOf(nonce, nonce.length + ciphertext.length); 076 System.arraycopy(ciphertext, 0, encrypted, nonce.length, ciphertext.length); 077 078 return encrypted; 079 } 080 catch (GeneralSecurityException ex) 081 { 082 throw new WicketRuntimeException(ex); 083 } 084 } 085 086 @Override 087 public byte[] decrypt(byte[] encrypted, SecretKey key) 088 { 089 try 090 { 091 byte[] nonce = new byte[12]; 092 byte[] ciphertext = new byte[encrypted.length - nonce.length]; 093 System.arraycopy(encrypted, 0, nonce, 0, nonce.length); 094 System.arraycopy(encrypted, nonce.length, ciphertext, 0, ciphertext.length); 095 096 Cipher cipher = getCipher(); 097 cipher.init(Cipher.DECRYPT_MODE, key, new AEADParameterSpec(nonce, 128)); 098 byte[] decrypted = cipher.doFinal(ciphertext); 099 100 return decrypted; 101 } 102 catch (GeneralSecurityException ex) 103 { 104 throw new WicketRuntimeException(ex); 105 } 106 } 107}