/*
* Copyright (C) 2017 Dominik Schadow, dominikschadow@gmail.com
*
* This file is part of the Java Security project.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.dominikschadow.javasecurity.symmetric;
import com.google.common.io.BaseEncoding;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.cert.CertificateException;
/**
* Symmetric encryption sample with plain Java. Loads the AES key from the sample keystore, encrypts and decrypts
* sample text with it.
* <p/>
* Note that the {@code INITIALIZATION_VECTOR} is not stored. One possibility to store it is to prepend it to the
* encrypted message with a delimiter (all in Base64 encoding): {@code Base64(IV) + DELIMITER + Base64(ENCRYPTED *
* MESSAGE)}
* <p/>
* Uses Google Guava to hex the encrypted message as readable format.
*
* @author Dominik Schadow
*/
public class AES {
private static final Logger log = LoggerFactory.getLogger(AES.class);
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String KEYSTORE_PATH = "/samples.ks";
private Cipher cipher;
public static void main(String[] args) {
AES aes = new AES();
aes.encrypt();
}
private void encrypt() {
final String initialText = "AES encryption sample text";
final char[] keystorePassword = "samples".toCharArray();
final String keyAlias = "symmetric-sample";
final char[] keyPassword = "symmetric-sample".toCharArray();
try {
cipher = Cipher.getInstance(ALGORITHM);
KeyStore ks = loadKeystore(keystorePassword);
Key key = loadKey(ks, keyAlias, keyPassword);
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getEncoded(), "AES");
byte[] ciphertext = encrypt(secretKeySpec, initialText);
byte[] plaintext = decrypt(secretKeySpec, ciphertext);
printReadableMessages(initialText, ciphertext, plaintext);
} catch (NoSuchPaddingException | NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException |
KeyStoreException | CertificateException | UnrecoverableKeyException |
InvalidAlgorithmParameterException |
InvalidKeyException | IOException ex) {
log.error(ex.getMessage(), ex);
}
}
private KeyStore loadKeystore(char[] keystorePassword) throws KeyStoreException,
CertificateException, NoSuchAlgorithmException, IOException {
try (InputStream keystoreStream = getClass().getResourceAsStream(KEYSTORE_PATH)) {
KeyStore ks = KeyStore.getInstance("JCEKS");
ks.load(keystoreStream, keystorePassword);
return ks;
}
}
private static Key loadKey(KeyStore ks, String keyAlias, char[] keyPassword) throws KeyStoreException,
UnrecoverableKeyException, NoSuchAlgorithmException {
if (!ks.containsAlias(keyAlias)) {
throw new UnrecoverableKeyException("Secret key " + keyAlias + " not found in keystore");
}
return ks.getKey(keyAlias, keyPassword);
}
private byte[] encrypt(SecretKeySpec secretKeySpec, String initialText) throws
UnsupportedEncodingException, BadPaddingException,
IllegalBlockSizeException, InvalidKeyException {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
return cipher.doFinal(initialText.getBytes("UTF-8"));
}
private byte[] decrypt(SecretKeySpec secretKeySpec, byte[] ciphertext) throws
BadPaddingException, IllegalBlockSizeException,
InvalidAlgorithmParameterException, InvalidKeyException {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(cipher.getIV()));
return cipher.doFinal(ciphertext);
}
private static void printReadableMessages(String initialText, byte[] ciphertext, byte[] plaintext) {
log.info("initial text: {}", initialText);
log.info("cipher text: {}", BaseEncoding.base16().encode(ciphertext));
log.info("plain text: {}", new String(plaintext));
}
}