org.panbox.core.crypto.CryptCore.java Source code

Java tutorial

Introduction

Here is the source code for org.panbox.core.crypto.CryptCore.java

Source

/*
 * 
 *               Panbox - encryption for cloud storage 
 *      Copyright (C) 2014-2015 by Fraunhofer SIT and Sirrix AG 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Additonally, third party code may be provided with notices and open source
 * licenses from communities and third parties that govern the use of those
 * portions, and any licenses granted hereunder do not alter any rights and
 * obligations you may have under such open source licenses, however, the
 * disclaimer of warranty and limitation of liability provisions of the GPLv3 
 * will apply to all the product.
 * 
 */
package org.panbox.core.crypto;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Calendar;
import java.util.Date;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.panbox.PanboxConstants;
import org.panbox.core.exception.SymmetricKeyDecryptionException;
import org.panbox.core.exception.SymmetricKeyEncryptionException;
import org.panbox.core.identitymgmt.IPerson;
import org.panbox.core.keymgmt.ShareKeyDBEntry;

public class CryptCore {

    private final static Cipher ASYMM_CIPHER;

    private static final Logger logger = Logger.getLogger("org.panbox.core");

    static {
        // add bouncy castle
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        try {
            ASYMM_CIPHER = Cipher.getInstance(KeyConstants.ASYMMETRIC_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            logger.fatal("Alogrithm " + KeyConstants.ASYMMETRIC_ALGORITHM + " not found!", e);
            throw new RuntimeException(e);
        } catch (NoSuchPaddingException e) {
            logger.fatal("No Padding for " + KeyConstants.ASYMMETRIC_ALGORITHM + " found!", e);
            throw new RuntimeException(e);
        }
    }

    private CryptCore() {
        super();
    }

    /**
     * Method calculates a fingerprint for the given public key instance
     * 
     * @param pubKey
     * @return secure hash {@link KeyConstants#PUBKEY_FINGERPRINT_DIGEST} of the
     *         encoded public key, or null if there was an error
     */
    public static byte[] getPublicKeyfingerprint(PublicKey pubKey) {
        try {
            MessageDigest mdFingerprint = MessageDigest.getInstance(KeyConstants.PUBKEY_FINGERPRINT_DIGEST,
                    KeyConstants.PROV_BC);
            byte[] enc = pubKey.getEncoded();
            if (enc != null && enc.length != 0) {
                return mdFingerprint.digest(enc);
            } else {
                logger.error(CryptCore.class.getName() + "::getPublicKeyFingerprint: Invalid public key!");
            }
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            logger.error(CryptCore.class.getName()
                    + "::getPublicKeyFingerprint: Could not init digest for fingerprinting!", e);
        }
        return null;
    }

    public static KeyStore createUnprotectedKeyStore() {
        KeyStore store = null;
        try {
            store = KeyStore.getInstance(KeyConstants.KEYSTORE_TYPE);
            store.load(null, KeyConstants.OPEN_KEYSTORE_PASSWORD);
        } catch (NoSuchAlgorithmException | CertificateException | IOException | KeyStoreException e) {
            logger.error("Could not create a keystore with out default password", e);
        }
        return store;
    }

    public static boolean verifySignature(Signable s, byte[] signature, PublicKey key) throws SignatureException {
        try {
            return SignatureHelper.verify(s, signature, key);
        } catch (Exception e) {
            throw new SignatureException("Could not verify signature", e);
        }
    }

    public static SecretKey generateSymmetricKey() {
        KeyGenerator generator;
        try {
            generator = KeyGenerator.getInstance(KeyConstants.SYMMETRIC_ALGORITHM, KeyConstants.PROV_BC);
            generator.init(KeyConstants.SYMMETRIC_KEY_SIZE);
            return generator.generateKey();
        } catch (NoSuchAlgorithmException e) {
            logger.error("Error during symmetric key generation: " + e);
        } catch (NoSuchProviderException e) {
            logger.error("Error during symmetric key generation: " + e);
        }
        return null;
    }

    public static byte[] encryptSymmetricKey(byte[] symKey, PublicKey pKey) throws SymmetricKeyEncryptionException {
        try {
            ASYMM_CIPHER.init(Cipher.ENCRYPT_MODE, pKey);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            CipherOutputStream cos = new CipherOutputStream(bos, ASYMM_CIPHER);
            cos.write(symKey);
            cos.flush();
            cos.close();
            byte[] byteArray = bos.toByteArray();
            return byteArray;
        } catch (Exception e) {
            throw new SymmetricKeyEncryptionException(e);
        }
    }

    public static SecretKey decryptSymmertricKey(byte[] symKey, PrivateKey pKey)
            throws SymmetricKeyDecryptionException {
        try {
            byte[] byteArray = _asymmetricDecrypt(symKey, pKey);

            SecretKey k = new SecretKeySpec(byteArray, KeyConstants.SYMMETRIC_ALGORITHM);

            return k;
        } catch (Exception e) {
            throw new SymmetricKeyDecryptionException(e);
        }
    }

    public static byte[] _asymmetricDecrypt(byte[] symKey, PrivateKey pKey)
            throws InvalidKeyException, IOException {
        ASYMM_CIPHER.init(Cipher.DECRYPT_MODE, pKey);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        CipherOutputStream cos = new CipherOutputStream(bos, ASYMM_CIPHER);
        cos.write(symKey);
        cos.flush();
        cos.close();
        byte[] byteArray = bos.toByteArray();
        return byteArray;
    }

    public static SecretKey decryptShareKey(ShareKeyDBEntry entry, PublicKey pubKey, PrivateKey privKey) {
        SecretKey result = null;
        if (entry != null) {
            byte[] encSK = entry.getEncryptedKey(pubKey);
            byte[] sk = new byte[KeyConstants.SYMMETRIC_BLOCK_SIZE];
            try {
                ASYMM_CIPHER.init(Cipher.DECRYPT_MODE, privKey);
                ByteArrayInputStream bis = new ByteArrayInputStream(encSK);
                CipherInputStream cis = new CipherInputStream(bis, ASYMM_CIPHER);
                cis.read(sk);
                cis.close();
                bis.close();
                result = new SecretKeySpec(sk, entry.getAlgorithm());
            } catch (InvalidKeyException e) {
                logger.warn("Exception caught in CryptCore.decryptShareKey", e);
            } catch (IOException e) {
                logger.warn("Exception caught in CryptCore.decryptShareKey", e);
            }
        }
        return result;
    }

    public static KeyPair generateKeypair() {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyConstants.KEY_FACTORY, KeyConstants.PROV_BC);
            kpg.initialize(KeyConstants.ASYMMETRIC_KEYSIZE);
            KeyPair kp = kpg.generateKeyPair();
            return kp;
        } catch (NoSuchAlgorithmException e) {
            logger.error("Error during asymmetric key pair generation: " + e);
        } catch (NoSuchProviderException e) {
            logger.error("Error during asymmetric key pair generation: " + e);
        }
        return null;
    }

    public static byte[] sign(Signable s, PrivateKey key) throws SignatureException {
        try {
            return SignatureHelper.sign(s, key);
        } catch (Exception e) {
            throw new SignatureException("Could not sign...", e);
        }
    }

    public static X509Certificate createSelfSignedX509Certificate(PrivateKey privKey, PublicKey pubKey,
            IPerson person) {
        return createSelfSignedX509Certificate(privKey, pubKey, person.getEmail(),
                person.getFirstName() + " " + person.getName());
    }

    /**
     * Creates a self signed certificate valid for 10 years (necessary to store
     * public keys in keystore)
     * 
     * @param privKey
     * @param pubKey
     * @param eMail
     * @param name
     * @return the certificate or NULL if there is an error
     */
    private static X509Certificate createSelfSignedX509Certificate(PrivateKey privKey, PublicKey pubKey,
            String eMail, String name) {
        // Generate self-signed certificate
        X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
        builder.addRDN(BCStyle.OU, "Panbox");
        builder.addRDN(BCStyle.O, "Panbox");
        builder.addRDN(BCStyle.CN, "localhost");

        if (eMail != null) {
            builder.addRDN(BCStyle.EmailAddress, eMail);
        }

        if (name != null) {
            builder.addRDN(BCStyle.NAME, name);
        }

        Calendar cal = Calendar.getInstance();
        Date notBefore = cal.getTime();

        cal.add(Calendar.YEAR, PanboxConstants.CERTIFICATE_LIFETIME_YEARS);
        Date notAfter = cal.getTime();

        BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());

        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), serial, notBefore,
                notAfter, builder.build(), pubKey);

        X509Certificate cert = null;
        try {
            ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
                    .setProvider(KeyConstants.PROV_BC).build(privKey);

            cert = new JcaX509CertificateConverter().setProvider(KeyConstants.PROV_BC)
                    .getCertificate(certGen.build(sigGen));

            cert.checkValidity(new Date());

            cert.verify(cert.getPublicKey());

        } catch (NoSuchAlgorithmException | InvalidKeyException | OperatorCreationException | CertificateException
                | NoSuchProviderException | SignatureException e) {
            logger.warn("Exception caught in CryptCore.createSelfSignedX509Certificate, returning null", e);
        }

        return cert;
    }

    /**
     * Creates a public key from a byte[]
     * 
     * @param keyType
     *            type for example "RSA"
     * @param keyBytes
     *            the byte[] with the key
     * @return the public key or null on error
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     */
    public static PublicKey createPublicKeyFromBytes(byte[] keyBytes) {
        PublicKey pk = null;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KeyConstants.KEY_FACTORY, new BouncyCastleProvider());
            pk = keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes));
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            logger.warn("Exception caught in CryptCore." + "createPublicKeyFromBytes, returning null", e);
        }
        return pk;
    }

    /**
     * Creates an X509 Certificate for a given byte array
     * 
     * @param certBytes
     * @return
     */
    public static X509Certificate createCertificateFromBytes(byte[] certBytes) {
        X509Certificate cert = null;
        CertificateFactory certFactory;
        try {
            certFactory = CertificateFactory.getInstance("X.509");

            InputStream in = new ByteArrayInputStream(certBytes);
            cert = (X509Certificate) certFactory.generateCertificate(in);

        } catch (CertificateException e) {
            logger.warn("Excpetion caught in CryptCore." + "createCertificateFromBytes, returning null", e);
        }

        return cert;
    }

    /**
     * Converts a given RSA PrivateKey instance to a KeyPair instance. The
     * PublicKey will be extracted from PrivateKey instance.
     * 
     * @param pKey
     *            The RSA PrivateKey used to generate the KeyPair
     * @return KeyPair instance including the private and public key.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static KeyPair privateKeyToKeyPair(PrivateKey pKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory keyFactory = KeyFactory.getInstance(KeyConstants.KEY_FACTORY, new BouncyCastleProvider());
        RSAPrivateCrtKey rsaPKey = (RSAPrivateCrtKey) pKey;
        RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(rsaPKey.getModulus(),
                rsaPKey.getPublicExponent());
        return new KeyPair(keyFactory.generatePublic(publicKeySpec), pKey);
    }

    public static byte[] genChecksum(File f)
            throws NoSuchAlgorithmException, NoSuchProviderException, FileNotFoundException, IOException {
        MessageDigest md = MessageDigest.getInstance(KeyConstants.DEFAULT_HASH, KeyConstants.PROV_BC);
        byte[] data = IOUtils.toByteArray(new FileInputStream(f));
        return md.digest(data);
    }

}