/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you 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 org.openengsb.core.util; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.openengsb.core.api.security.DecryptionException; import org.openengsb.core.api.security.EncryptionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class CipherUtils { private static final Logger LOGGER = LoggerFactory.getLogger(CipherUtils.class); public static final String DEFAULT_SIGN_ALGORITHM = "SHA1withRSA"; public static final String DEFAULT_SYMMETRIC_ALGORITHM = "AES"; public static final int DEFAULT_SYMMETRIC_KEYSIZE = 128; public static final String DEFAULT_ASYMMETRIC_ALGORITHM = "RSA"; public static final int DEFAULT_ASYMMETRIC_KEYSIZE = 2048; /** * Decrypts the given data using the given key. The key holds the algorithm used for decryption. If you are * decrypting data that is supposed to be a string, consider that it might be Base64-encoded. * * @throws DecryptionException if the string cannot be decrypted with the given key */ public static byte[] decrypt(byte[] text, Key key) throws DecryptionException { return decrypt(text, key, key.getAlgorithm()); } /** * Decrypts the given data using the given key using the given algorithm. If you are decrypting data that is * supposed to be a string, consider that it might be Base64-encoded. * * @throws DecryptionException if the string cannot be decrypted with the given key * @throws IllegalArgumentException if the algorithm is not supported. */ public static byte[] decrypt(byte[] text, Key key, String algorithm) throws DecryptionException { Cipher cipher; try { LOGGER.trace("start decrypting text using {} cipher", algorithm); cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.DECRYPT_MODE, key); LOGGER.trace("initialized decryption with key of type {}", key.getClass()); } catch (GeneralSecurityException e) { throw new IllegalArgumentException("unable to initialize cipher for algorithm " + algorithm, e); } try { return cipher.doFinal(text); } catch (GeneralSecurityException e) { throw new DecryptionException("unable to decrypt data using algorithm " + algorithm, e); } } /** * Encrypts the given data using the given key. The key holds the algorithm used for encryption. If you are * encrypting data that is supposed to be a string, consider that it might be Base64-encoded. * * @throws EncryptionException if the string cannot be encrypted with the given key */ public static byte[] encrypt(byte[] text, Key key) throws EncryptionException { return encrypt(text, key, key.getAlgorithm()); } /** * Encrypts the given data using the given key using the given algorithm. If you are encrypting data that is * supposed to be a string, consider encoding it in Base64. * * @throws EncryptionException if the string cannot be encrypted with the given key * @throws IllegalArgumentException if the algorithm is not supported. */ public static byte[] encrypt(byte[] text, Key key, String algorithm) throws EncryptionException { Cipher cipher; try { LOGGER.trace("start encrypting text using {} cipher", algorithm); cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, key); LOGGER.trace("initialized encryption with key of type {}", key.getClass()); } catch (GeneralSecurityException e) { throw new IllegalArgumentException("unable to initialize cipher for algorithm " + algorithm, e); } try { return cipher.doFinal(text); } catch (GeneralSecurityException e) { throw new EncryptionException("unable to encrypt data using algorithm " + algorithm, e); } } /** * converts a byte[] that originally was created using {@link PublicKey#getEncoded()} back to the corresponding * instance. * * Example: CipherUtils.deserializePublicKey(data, "RSA") */ public static PublicKey deserializePublicKey(byte[] keyData, String algorithm) { LOGGER.trace("deserialize public key from data using algorithm \"{}\"", algorithm); X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(keyData); try { KeyFactory keyFactory = KeyFactory.getInstance(algorithm); return keyFactory.generatePublic(pubSpec); } catch (GeneralSecurityException e) { throw new IllegalArgumentException("provided data could not be converted to a PublicKey for algorithm " + algorithm, e); } } /** * converts a byte[] that originally was created using {@link PrivateKey#getEncoded()} back to the corresponding * instance. * * Example: CipherUtils.deserializePrivateKey(data, "RSA") */ public static PrivateKey deserializePrivateKey(byte[] keyData, String algorithm) { LOGGER.trace("deserialize private key from data using algorithm \"{}\"", algorithm); PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(keyData); try { KeyFactory keyFactory = KeyFactory.getInstance(algorithm); return keyFactory.generatePrivate(privSpec); } catch (GeneralSecurityException e) { throw new IllegalArgumentException("provided data could not be converted to a PrivateKey for algorithm " + algorithm, e); } } /** * converts a byte[] that originally was created using {@link SecretKey#getEncoded()} back to the corresponding * instance. * * Example: CipherUtils.deserializeSecretKey(data, "AES") */ public static SecretKey deserializeSecretKey(byte[] keyData, String algorithm) { LOGGER.trace("deserialize secret key from data using algorithm \"{}\"", algorithm); return new SecretKeySpec(keyData, algorithm); } /** * Generate a {@link KeyPair} for the given assymmetric algorithm and keysize * * Example: CipherUtils.generateKeyPair("RSA", 2048) */ public static KeyPair generateKeyPair(String algorithm, int keySize) { KeyPairGenerator keyPairGenerator; try { keyPairGenerator = KeyPairGenerator.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); } keyPairGenerator.initialize(keySize); return keyPairGenerator.generateKeyPair(); } /** * Generate a {@link SecretKey} for the given symmetric algorithm and keysize * * Example: CipherUtils.generateKeyPair("AES", 128) */ public static SecretKey generateKey(String algorithm, int keySize) { KeyGenerator secretKeyGenerator; try { secretKeyGenerator = KeyGenerator.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); } secretKeyGenerator.init(keySize); return secretKeyGenerator.generateKey(); } /** * create a signature for the given text using the PrivateKey and algorithm * * Example: CipherUtils.sign(data, key, CipherUtils.DEFAULT_SIGN_ALGORITHM) * * @throws SignatureException if the data cannot not be signed */ public static byte[] sign(byte[] text, PrivateKey key, String algorithm) throws SignatureException { Signature signature; try { signature = Signature.getInstance(algorithm); signature.initSign(key); } catch (GeneralSecurityException e) { throw new IllegalArgumentException("cannot initialize signature for algorithm " + algorithm, e); } signature.update(text); return signature.sign(); } /** * verifies if the given data is valid for the given signature and public key. * * @throws SignatureException if the algorithm cannot be initialized */ public static boolean verify(byte[] text, byte[] signatureValue, PublicKey key, String algorithm) throws SignatureException { Signature signature; try { signature = Signature.getInstance(algorithm); signature.initVerify(key); } catch (GeneralSecurityException e) { throw new IllegalArgumentException("cannot initialize signature for algorithm " + algorithm, e); } signature.update(text); return signature.verify(signatureValue); } private CipherUtils() { } }