2

Oracle のドキュメントを読んでみると、デフォルトで JKS ファイルは を使用して暗号化されていることがわかりますPBEWithMD5AndTripleDES。DESだけでも不安になる中、MD5は大きな赤信号。秘密鍵を使用PBEWithSHA256And256BitAES-CBC-BCまたは暗号化したいと考えています。PBEWithSHA256And128bitAES-CBC-BC

KeyStore インターフェイス全体を実装する新しい暗号化サービス プロバイダーを作成する必要がありますか、または KeyStore の作成をパラメーター化することは可能ですか (プレーン Java または BouncyCastle を使用)?

編集: 背景の少し。

MD5 が KDF (または PBE) として使用されているように、3DES が壊れていないことはわかっています。問題は、これがの状況だということだ。私たちが知っている限りでは、MD5 は明日 MD4 が壊れるレベルまで壊れる可能性があります。私のアプリケーションの寿命は少なくとも 10 年ですが、それよりもずっと長い可能性があります。どういうわけか、安全ではないかもしれないという理由だけで、その 10 年後に実際の暗号化コードを深く掘り下げている人を目にすることはありません。パスワード漏えいの大きな「事故」の最後のいくつかを見て、その可能性がどの程度であるかを確認する必要があります。これは、未加工のデータベースを見た人にとって明らかなことでした。

そうは言っても、NSA暗号スイートBは、あらゆる種類の対称暗号化にAESのみを許可します. NIST は、HMAC および KDF で使用する SHA-1 および SHA-2 アルゴリズムのみをリストしていますが、SHA-1 の使用は推奨されていません。Suite B では、 SHA-2 ハッシュ関数のみが許可されます。これらのアルゴリズムは公開されているのに、なぜそれらを使用しないのですか?

4

4 に答える 4

4

最後に、私は PKCS#8 ファイルを使用して暗号化しましたPBEWithSHA256And256BitAES-CBC-BC

暗号化:

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;
        }
    }
}
于 2012-08-09T14:46:57.487 に答える
1

トリプル DES は非常に強力で、Oracle は 168 ビットのエントロピーを持つキーを使用する可能性があります (執筆時点では 112 ビットの完全なセキュリティを提供します)。

さらに、MD5は署名などには安全ではないかもしれませんが、PBE などの鍵導出スキームでの使用には確かに有効です。

もちろん、Oracle がこれらのスキームからいずれ移行するのは良い考えですが、Triple DES と MD5 は不必要に心配する必要はありません。自分で書くのはおそらく悪い考えです。落とし穴が多すぎます。

適切なパスワードを選択してください。おそらく、これが最善の方法です。または、ハイエンドのセキュリティが必要な場合は、正しく構成された HSM またはスマートカードにキーストアを配置します。

于 2012-08-08T21:59:42.370 に答える