簡體   English   中英

如何使用AES加密創建Java密鑰存儲(.jks)文件

[英]How to create Java Key Store (.jks) file with AES encryption

閱讀Oracle文檔,我發現默認情況下,JKS文件是使用PBEWithMD5AndTripleDES加密的。 雖然僅憑DES會讓我感到不安,但MD5會發出紅色的大光。 我想使用PBEWithSHA256And256BitAES-CBC-BCPBEWithSHA256And128bitAES-CBC-BC來加密私鑰。

我是否必須編寫實現整個KeyStore接口的新密碼學服務提供者,還是可以對KeyStore的創建進行參數化(使用純Java或BouncyCastle)?

編輯:一點點背景。

我知道3DES不會損壞,就像MD5用作KDF(或在PBE中)一樣。 問題是, 現在是這種情況。 就我們所知,MD5可能會被破壞到明天MD4被破壞的程度。 我的應用程序壽命至少為10年,而且很有可能還要更多。 不知怎的,我沒有看到人們在這10年后,深鑽研工作的加密代碼,只是因為它可能是不安全的。 只需查看密碼泄露的幾大“不幸事故”,以了解這種可能性有多大,這對於任何看過原始數據庫的人來說都是顯而易見的。

話雖這么說:NSA加密套件B僅允許AES進行任何形式的對稱加密。 NIST僅列出用於HMAC和KDF的SHA-1和SHA-2算法,但不建議使用SHA-1。 套件B 允許使用 SHA-2哈希函數。 這些算法是公開可用的,所以為什么我不應該使用它們呢?

最后,我處理了使用PBEWithSHA256And256BitAES-CBC-BC加密的PKCS#8文件

加密:

import java.io.FileOutputStream;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import org.bouncycastle.asn1.bc.BCObjectIdentifiers;

public class EncodePKCS8 {

/**
 * @param args
 * @throws NoSuchAlgorithmException 
 * @throws InvalidKeySpecException 
 * @throws NoSuchPaddingException 
 * @throws InvalidAlgorithmParameterException 
 * @throws InvalidKeyException 
 * @throws BadPaddingException 
 * @throws IllegalBlockSizeException 
 * @throws InvalidParameterSpecException 
 * @throws IOException 
 * @throws NoSuchProviderException 
 */
public static void main(String[] args) throws NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
InvalidParameterSpecException, IOException, NoSuchProviderException
{
    // before we can do anything with BouncyCastle we have to register its provider
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

    String password = "Very long and complex password";

    // generate RSA key pair
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    KeyPair keyPair = keyPairGenerator.genKeyPair();

    byte[] encryptedPkcs8 = encryptPrivateKey(password, keyPair);

    FileOutputStream fos = new FileOutputStream("privkey.p8");
    fos.write(encryptedPkcs8);
    fos.close();

    return;
}

private static byte[] encryptPrivateKey(String password, KeyPair keyPair)
    throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
    InvalidKeySpecException, NoSuchPaddingException,
    InvalidAlgorithmParameterException, IllegalBlockSizeException,
    BadPaddingException, InvalidParameterSpecException, IOException
{
    int count = 100000; // hash iteration count, best to leave at default or increase
    return encryptPrivateKey(password, keyPair, count);
}

/**
 * 
 * @param password
 * @param keyPair
 * @param count
 * @return PKCS#8 encoded, encrypted keyPair
 * @throws NoSuchAlgorithmException
 * @throws NoSuchProviderException
 * @throws InvalidKeySpecException
 * @throws NoSuchPaddingException
 * @throws InvalidKeyException
 * @throws InvalidAlgorithmParameterException
 * @throws IllegalBlockSizeException
 * @throws BadPaddingException
 * @throws InvalidParameterSpecException
 * @throws IOException
 */
private static byte[] encryptPrivateKey(String password, 
        KeyPair keyPair, int count) throws NoSuchAlgorithmException,
        NoSuchProviderException, InvalidKeySpecException,
        NoSuchPaddingException, InvalidKeyException,
        InvalidAlgorithmParameterException, IllegalBlockSizeException,
        BadPaddingException, InvalidParameterSpecException, IOException
{
    // extract the encoded private key, this is an unencrypted PKCS#8 private key
        byte[] encodedprivkey = keyPair.getPrivate().getEncoded();

        // Use a PasswordBasedEncryption (PBE) algorithm, OID of this algorithm will be saved
        // in the PKCS#8 file, so changing it (when more standard algorithm or safer
        // algorithm is available) doesn't break backwards compatibility.
        // In other words, decryptor doesn't need to know the algorithm before it will be
        // able to decrypt the PKCS#8 object.
        String encAlg = BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId();

        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[16];
        random.nextBytes(salt);

        // Create PBE parameter set
        PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
        PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
        SecretKeyFactory keyFac = SecretKeyFactory.getInstance(encAlg, "BC");
        SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

        Cipher pbeCipher = Cipher.getInstance(encAlg, "BC");

        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

        // Encrypt the encoded Private Key with the PBE key
        byte[] ciphertext = pbeCipher.doFinal(encodedprivkey);

        // Now construct  PKCS #8 EncryptedPrivateKeyInfo object
        AlgorithmParameters algparms = AlgorithmParameters.getInstance(encAlg, "BC");
        algparms.init(pbeParamSpec);
        EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, ciphertext);

        // DER encoded PKCS#8 encrypted key
        byte[] encryptedPkcs8 = encinfo.getEncoded();

        return encryptedPkcs8;
    }
}

解密:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;
import javax.crypto.EncryptedPrivateKeyInfo;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public class DecodePKCS8 {

    /**
     * @param args
     * @throws IOException 
     * @throws NoSuchPaddingException When file is corrupted
     * @throws NoSuchAlgorithmException When no BC provider has been loaded 
     * @throws InvalidKeySpecException When decryption of file failed
     * @throws InvalidAlgorithmParameterException When file is corrupted
     * @throws InvalidKeyException When Unlimited cryptography extensions are not installed
     */
    public static void main(String[] args) throws
    IOException, NoSuchAlgorithmException, NoSuchPaddingException,
    InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException
    {
        // before we can do anything with BouncyCastle we have to register its provider
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        String password = "Very long and complex password";

        // read DER encoded key from files
        byte[] encodedprivkey = getFileBytes("privkey.p8");

        // this is a encoded PKCS#8 encrypted private key
        EncryptedPrivateKeyInfo ePKInfo = new EncryptedPrivateKeyInfo(encodedprivkey);

        // first we have to read algorithm name and parameters (salt, iterations) used
        // to encrypt the file
        Cipher cipher = Cipher.getInstance(ePKInfo.getAlgName());
        PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());

        SecretKeyFactory skFac = SecretKeyFactory.getInstance(ePKInfo
                .getAlgName());
        Key pbeKey = skFac.generateSecret(pbeKeySpec);

        // Extract the iteration count and the salt
        AlgorithmParameters algParams = ePKInfo.getAlgParameters();
        cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);

        // Decrypt the encryped private key into a PKCS8EncodedKeySpec
        KeySpec pkcs8KeySpec = ePKInfo.getKeySpec(cipher);

        // Now retrieve the RSA Public and private keys by using an
        // RSA key factory.
        KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
        // First get the private key
        PrivateKey rsaPriv = rsaKeyFac.generatePrivate(pkcs8KeySpec);
        // Now derive the RSA public key from the private key
        RSAPublicKeySpec rsaPubKeySpec = new RSAPublicKeySpec(((RSAKey) rsaPriv).getModulus(),
                ((RSAPrivateCrtKey) rsaPriv).getPublicExponent());
        PublicKey rsaPubKey = (RSAPublicKey) rsaKeyFac.generatePublic(rsaPubKeySpec);

    System.out.println("Key extracted, public part: " + rsaPubKey);
    }

    private static byte[] getFileBytes(String path)
    {
        File f = new File(path);
        int sizecontent = ((int) f.length()); // no key file will ever be bigger than 4GiB...
        byte[] data = new byte[sizecontent];
        try 
            {
            FileInputStream freader = new FileInputStream(f);
            freader.read(data, 0, sizecontent) ;
            freader.close();
            return data;
            }
        catch(IOException ioe)
        {
            System.out.println(ioe.toString());
            return null;
        }
    }
}

從Java 8開始,您可以創建PKCS#12密鑰庫,並在存儲密鑰以指定要使用的加密算法時傳遞顯式的PasswordProtection參數:

import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;

import javax.crypto.spec.PBEParameterSpec;

public class scratch {
    public static void main(String... args) throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(null, null); // Initialize a blank keystore

        // Your key to store
        PrivateKey key = ...;
        Certificate[] chain = new Certificate[] { ... };

        char[] password = "changeit".toCharArray();
        byte[] salt = new byte[20];
        new SecureRandom().nextBytes(salt);
        keyStore.setEntry("test", new PrivateKeyEntry(key, chain),
                          new PasswordProtection(password,
                                                 "PBEWithHmacSHA512AndAES_256",
                                                 new PBEParameterSpec(salt, 100_000)));

        keyStore.store(new FileOutputStream("/tmp/keystore.p12"), password);
    }
}

您可以閱讀本文的詳細信息(免責聲明:我寫過該文章)。

三重DES非常強大,Oracle可能使用具有168位熵的密鑰(在編寫本文時提供了完整的112位安全性)。

此外,盡管MD5對於例如簽名可能不是安全的,但是它對於在諸如PBE這樣的密鑰派生方案中使用肯定是有效的。

對於Oracle來說,及時從這些方案中遷移出來當然是個好主意,但是Triple DES和MD5不必讓您擔心。 自己編寫可能是一個更糟糕的主意,有太多陷阱。

選擇一個好的密碼,這可能是您最好的選擇。 如果需要高端安全性,也可以將密鑰庫放入正確配置的HSM或智能卡中。

主類

`public class a {

/**
 * @param args the command line arguments
 */
public static void main(String[] args) throws Exception {
    // TODO code application logic here
    String b = "MSISDN=559915129&productID=5859";

    AEScryptography enj = new AEScryptography();
    String[] argts = {b, "EN"};
    System.out.println("ENCY -> "+enj.encryptionDecryption(argts));
    System.out.println(checksum.encode(b));
} }`

AEScryptography類

`public class AEScryptography {
public String encryptionDecryption(String[] args) throws UnsupportedEncodingException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
    String returnVariable = "";
    if (args.length == 2) {
        try {
            String keystorePass = "201774";  // KeyStroe Password
            String keyPass = "mc7129";       // KeyPassword 
            String alias = "raVi";           // Alias

        InputStream keystoreStream = new FileInputStream("D:/keyFile.jks");
            KeyStore keystore = KeyStore.getInstance("JCEKS");
            keystore.load(keystoreStream, keystorePass.toCharArray());
            Key key = keystore.getKey(alias, keyPass.toCharArray());
            byte[] bt = key.getEncoded();
            String s = new String(bt);
            String originalString = args[0];
            switch (args[1]) {
                case "EN":
                    String encryptedString = AES.encrypt(originalString, s, bt);
                    returnVariable = encryptedString;
                    break;
                case "DE":
                    String decryptedString = AES.decrypt(originalString, s, bt);
                    returnVariable = decryptedString;
                    break;
                default:
                    System.out.println("java -jar /home/jocg/AES-256Encryption.jar StringToEncrypt/Decrypt EN/DE");
                    break;
            }

        } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException ex) {
            System.out.println(ex);

        }
    } else {
        System.out.println("java -jar /home/jocg/AES-256Encryption.jar StringToEncrypt/Decrypt EN/DE");
    }
    return returnVariable;
} }`

AES等級

 public class AES {
private static SecretKeySpec secretKey;
private static byte[] key;
public static void setKey(String myKey, byte[] ActualKey) throws 
NoSuchAlgorithmException {
    try {
        key = myKey.getBytes("UTF-8");
        key = ActualKey;
        secretKey = new SecretKeySpec(key, "AES");
    } catch (UnsupportedEncodingException e) {
    }
}

public static String encrypt(String strToEncrypt, String secret, byte[] bt) throws UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
    try {
        setKey(secret, bt);
        byte[] iv = new byte[16];
        IvParameterSpec ivspec = new IvParameterSpec(iv);
        SecretKey secKey = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secKey, ivspec);
        byte[] newData = cipher.doFinal(strToEncrypt.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(newData);
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
        System.out.println("Error while encrypting: " + e.toString());
    }
    return null;
}
 }`

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM