如何在java中实现基于密码的混合加密?

时间:2014-08-17 17:25:44

标签: java android encryption

我目前正在开发一个用于开发混合文件加密的Android项目。从这个site开始,我已经创建了一个混合加密,只能加密文本/字符串。虽然所有密钥都是在代码中自动生成的。有关如何使密钥成为用户输入密码并能够加密文件的任何想法吗?

以下是代码:

    // Some codes here

    // Create key pair (public and private key) for RSA encryption and decryption
    try {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024);
        kp = kpg.genKeyPair();
        publicKey = kp.getPublic();
        privateKey = kp.getPrivate();
    } catch (Exception e) {
        Log.e(TAG, "RSA key pair error");
    }
}

private void encHybrid() throws GeneralSecurityException {

    // Create random secret key with AES algorithm
    KeyGenerator kg = KeyGenerator.getInstance("AES");

    kg.init(128, sr) ;
    SecretKey cipherKey = kg.generateKey() ;

    // Encrypt secret key asymmetry algorithm (RSA)
    encryptedSecretKey = encrypt(cipherKey.getEncoded(), kp.getPublic());

    textHybrid = etHybrid.getText().toString();

    // Encrypt inputted text/string with symmetry algorithm using encrypted secret key
    encryptedData = encrypt(textHybrid.getBytes(), cipherKey);

}

// Method to encrypt the text/string
public static byte[] encrypt(byte[] toEncrypt, SecretKey key)
        throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance("AES") ;
    cipher.init(Cipher.ENCRYPT_MODE, key) ;
    return cipher.doFinal(toEncrypt);
}

// Method to encrypt the secret key
public static byte[] encrypt(byte[] toEncrypt, PublicKey key)
        throws GeneralSecurityException {
    Cipher cipher = Cipher.getInstance("RSA") ;
    cipher.init(Cipher.ENCRYPT_MODE, key) ;
    return cipher.doFinal(toEncrypt);
}

private void decryptHybrid() throws GeneralSecurityException {

    // Decrypt secret key with private key
    byte[] decryptedSecretKey = decrypt(encryptedSecretKey, kp.getPrivate()) ;

    // Decrypted secret key will be stored in sKey
    SecretKey sKey = new SecretKeySpec(decryptedSecretKey, "AES") ;
    textHybrid = etHybrid.getText().toString();

    // Decrypt encrypted text/string with decrypted secret key
    byte[] decryptedData = decrypt(encryptedData, sKey) ;

}

// Method to decrypt the text/string
public static byte[] decrypt(byte[] toDecrypt, SecretKey key)
        throws GeneralSecurityException {
    Cipher deCipher = Cipher.getInstance("AES") ;
    deCipher.init(Cipher.DECRYPT_MODE, key) ;
    return deCipher.doFinal(toDecrypt);
}

// Method to decrypt the secret key
public static byte[] decrypt(byte[] toDecrypt, PrivateKey key)
        throws GeneralSecurityException {
    Cipher deCipher = Cipher.getInstance("RSA") ;
    deCipher.init(Cipher.DECRYPT_MODE, key) ;
    return deCipher.doFinal(toDecrypt);
}

1 个答案:

答案 0 :(得分:3)

您真的不想从密码生成密钥对。该方案的问题在于无法信任公钥。通常做的是使用从密码生成的秘密(对称)密钥加密私钥

所以,除了混合加密之外,你会有一个完全相反的方案。这是一些代码,但它应该足够可读。或者您可以使用PGP,它基本上执行相同类型的操作。

import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class PassphraseWrapRSA {
    private static KeyPair generateRSAKeyPair(final int size) {
        KeyPairGenerator kpgen;
        try {
            kpgen = KeyPairGenerator.getInstance("RSA");
        } catch (final NoSuchAlgorithmException e) {
            throw new IllegalStateException();
        }
        kpgen.initialize(size);
        return kpgen.generateKeyPair();
    }

    public static byte[] generateSalt() {
        final SecureRandom rng = new SecureRandom();
        final byte[] salt = new byte[16];
        rng.nextBytes(salt);
        return salt;
    }

    private static SecretKey deriveAESKey(final byte[] salt,
            final char[] password) {
        try {
            final SecretKeyFactory factory = SecretKeyFactory
                    .getInstance("PBKDF2WithHmacSHA1");
            final KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
            final SecretKey keyWrapKey = factory.generateSecret(spec);
            final SecretKey secret = new SecretKeySpec(keyWrapKey.getEncoded(),
                    "AES");
            return secret;

        } catch (final Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private static byte[] encryptRSAPrivateKey(final RSAPrivateKey rsaPrivateKey,
            final SecretKey aesKey) {
        try {
            final Cipher c = Cipher.getInstance("AES/GCM/NoPadding");

            final SecureRandom ivGen = new SecureRandom();
            final byte[] iv = new byte[c.getBlockSize()];
            ivGen.nextBytes(iv);
            c.init(Cipher.WRAP_MODE, aesKey, new IvParameterSpec(iv));
            final byte[] wrappedKey = c.wrap(rsaPrivateKey);
            return concat(iv, wrappedKey);
        } catch (final GeneralSecurityException e) {
            throw new IllegalStateException(e);
        }
    }

    public static byte[] wrapRSAPrivateKey(final String passphrase,
            final RSAPrivateKey rsaPrivateKey) {
        // --- generate salt
        final byte[] newSalt = generateSalt();

        // --- derive symmetric key from salt and password
        final SecretKey aesKey = deriveAESKey(newSalt,
                passphrase.toCharArray());


        final byte[] encryptedPrivate = encryptRSAPrivateKey(rsaPrivateKey, aesKey);
        final byte[] saltedAndEncryptedPrivate = concat(newSalt,
                encryptedPrivate);
        return saltedAndEncryptedPrivate;
    }

    private static RSAPrivateKey decryptRSAPrivateKey(final byte[] encryptedRSAPrivateKey,
            final SecretKey aesKey) throws InvalidKeyException {
        try {
            final Cipher c = Cipher.getInstance("AES/GCM/NoPadding");

            int offset = 0;
            final byte[] iv = Arrays.copyOfRange(encryptedRSAPrivateKey, 0,
                    c.getBlockSize());
            offset += c.getBlockSize();

            c.init(Cipher.UNWRAP_MODE, aesKey, new IvParameterSpec(iv));
            final Key key = c.unwrap(Arrays.copyOfRange(encryptedRSAPrivateKey, offset,
                    encryptedRSAPrivateKey.length), "RSA", Cipher.PRIVATE_KEY);
            return (RSAPrivateKey) key;
        } catch (final InvalidKeyException e) {
            throw e;
        } catch (final GeneralSecurityException e) {
            throw new IllegalStateException(e);
        }
    }

    public static RSAPrivateKey unwrapRSAPrivateKey(final String passphrase,
            final byte[] saltedAndEncryptedPrivate) throws InvalidKeyException {
        int offset = 0;
        final byte[] backSalt = Arrays.copyOfRange(saltedAndEncryptedPrivate,
                offset, 16);
        offset += 16;
        final SecretKey backAESKey = deriveAESKey(backSalt,
                passphrase.toCharArray());
        final byte[] backEncryptedPrivateKey = Arrays.copyOfRange(
                saltedAndEncryptedPrivate, offset,
                saltedAndEncryptedPrivate.length);
        final RSAPrivateKey decryptedPrivate = decryptRSAPrivateKey(
                backEncryptedPrivateKey, backAESKey);
        return decryptedPrivate;
    }

    public static RSAPublicKey decodeRSAPublicKey(
            final byte[] x509EncodedPUblicKey) throws InvalidKeySpecException {
        try {
            final KeyFactory rsaPublicKeyFactory = KeyFactory.getInstance("RSA");
            final PublicKey pubKey = rsaPublicKeyFactory
                    .generatePublic(new X509EncodedKeySpec(x509EncodedPUblicKey));
            return (RSAPublicKey) pubKey;
        } catch (final InvalidKeySpecException e) {
            throw e;
        } catch (final GeneralSecurityException e) {
            throw new IllegalStateException(e);
        }
    }

    public static byte[] encodeRSAPublicKey(final RSAPublicKey rsaPublicKey) {
        return rsaPublicKey.getEncoded();
    }

    private static byte[] concat(final byte[] a, final byte[] a2) {
        final byte[] result = new byte[a.length + a2.length];
        System.arraycopy(a, 0, result, 0, a.length);
        System.arraycopy(a2, 0, result, a.length, a2.length);
        return result;
    }

    public static void main(final String[] args) throws Exception {
        // --- not required for Java 8
        Security.addProvider(new BouncyCastleProvider());

        // --- setup key pair (generated in advance)
        final String passphrase = "owlstead";
        final KeyPair kp = generateRSAKeyPair(1024);
        final RSAPublicKey rsaPublicKey = (RSAPublicKey) kp.getPublic();
        final RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) kp.getPrivate();

        // --- encode and wrap
        byte[] x509EncodedRSAPublicKey = encodeRSAPublicKey(rsaPublicKey);
        final byte[] saltedAndEncryptedPrivate = wrapRSAPrivateKey(
                passphrase, rsaPrivateKey);

        // --- decode and unwrap
        final RSAPublicKey retrievedRSAPublicKey = decodeRSAPublicKey(x509EncodedRSAPublicKey);
        final RSAPrivateKey retrievedRSAPrivateKey = unwrapRSAPrivateKey(passphrase,
                saltedAndEncryptedPrivate);

        // --- check result
        System.out.println(retrievedRSAPublicKey);
        System.out.println(retrievedRSAPrivateKey);
    }
}

警告:仅出于演示目的,请使用类和更灵活的方法来处理协议(包括例如版本号)等。