Шифрование парольной фразы Java - PullRequest
6 голосов
/ 16 декабря 2008

Я пытаюсь научиться делать шифрование на основе парольной фразы с помощью Java. Я нахожу несколько примеров в Интернете, но ни одного (пока) о переполнении стека. Примеры немного разъясняют мне, особенно в отношении выбора алгоритма. Кажется, что есть много передаваемых строк, чтобы сказать, какие алгоритмы использовать, но мало документации о том, откуда появились эти строки и что они значат. И также кажется, что разные алгоритмы могут требовать разных реализаций класса KeySpec, поэтому я не уверен, какие алгоритмы могут использовать класс PBEKeySpec, на который я смотрю. Кроме того, все примеры кажутся немного устаревшими, многие требуют, чтобы вы получили более старый пакет криптографии, который раньше не был частью JDK, или даже стороннюю реализацию.

Может ли кто-нибудь дать прямое введение в то, что мне нужно сделать, чтобы реализовать шифрование (строковые данные, строковую фразу-пароль) и дешифрование (байтовые [] данные, строковую фразу-пароль)?

Ответы [ 7 ]

11 голосов
/ 17 декабря 2008

Я буду осторожен с тем, чтобы давать или принимать советы по вопросам безопасности на форуме ... подробности довольно сложны и часто быстро устаревают.

Сказав это, я думаю, что справочное руководство Sun Java Cryptography Architecture (JCA) является хорошей отправной точкой. Посмотрите прилагаемый пример кода , иллюстрирующий шифрование на основе пароля (PBE).

Между прочим, стандартная JRE предоставляет только несколько готовых опций для PBE ("PBEWithMD5AndDES" - одна из них). Для большего выбора вам понадобится «пакет надежного шифрования» или какой-либо сторонний провайдер, например Bouncy Castle . Другой альтернативой может быть реализация собственного PBE с использованием алгоритмов хеширования и шифрования, представленных в JRE. Таким способом можно реализовать PBE с SHA-256 и AES-128 ( пример методов шифрования / дешифрования ).

Вкратце, метод шифрования для PBE может включать следующие шаги:

  1. Получите пароль и открытый текст от пользователя и преобразуйте их в байтовые массивы.
  2. Генерирует безопасную случайную соль .
  3. Добавьте соль к паролю и вычислите ее криптографический хэш . Повторите это много раз.
  4. Зашифруйте открытый текст, используя полученный хеш в качестве вектора инициализации и / или секретного ключа .
  5. Сохраните соль и полученный зашифрованный текст.
4 голосов
/ 09 августа 2009

Используйте RFC2898 для генерации ключей из паролей. Насколько я знаю, это не входит в JRE или JCE, но включено в J2EE-серверы, такие как JBoss , Oracle и WebSphere . Он также включен в библиотеку базовых классов .NET ( Rfc2898DeriveBytes ).

Существует несколько реализаций LGPL в Java, но на первый взгляд эта выглядит немного сложнее. Существует также хорошая версия JavaScript . (Я создал модифицированную версию этой и упаковал ее как компонент сценариев Windows)

Не имея хорошей реализации с соответствующей лицензией, я упаковал некоторый код из Mattias Gartner. Это код в полном объеме. Коротко, просто, легко понять. Он лицензирован под MS Public License .

// PBKDF2.java
// ------------------------------------------------------------------
//
// RFC2898 PBKDF2 in Java.  The RFC2898 defines a standard algorithm for
// deriving key bytes from a text password.  This is sometimes
// abbreviated "PBKDF2", for Password-based key derivation function #2.
//
// There's no RFC2898-compliant PBKDF2 function in the JRE, as far as I
// know, but it is available in many J2EE runtimes, including those from
// JBoss, IBM, and Oracle.
//
// It's fairly simple to implement, so here it is. 
// 
// Created Sun Aug 09 01:06:57 2009
//
// last saved: 
// Time-stamp: <2009-August-09 02:19:50>
// ------------------------------------------------------------------
//
// code thanks to Matthias Gartner
//
// ------------------------------------------------------------------

package cheeso.examples;


import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


public class PBKDF2
{
    public static byte[] deriveKey( byte[] password, byte[] salt, int iterationCount, int dkLen )
        throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException
    {
        SecretKeySpec keyspec = new SecretKeySpec( password, "HmacSHA1" );
        Mac prf = Mac.getInstance( "HmacSHA1" );
        prf.init( keyspec );

        // Note: hLen, dkLen, l, r, T, F, etc. are horrible names for
        //       variables and functions in this day and age, but they
        //       reflect the terse symbols used in RFC 2898 to describe
        //       the PBKDF2 algorithm, which improves validation of the
        //       code vs. the RFC.
        //
        // dklen is expressed in bytes. (16 for a 128-bit key)

        int hLen = prf.getMacLength();   // 20 for SHA1
        int l = Math.max( dkLen, hLen); //  1 for 128bit (16-byte) keys
        int r = dkLen - (l-1)*hLen;      // 16 for 128bit (16-byte) keys
        byte T[] = new byte[l * hLen];
        int ti_offset = 0;
        for (int i = 1; i <= l; i++) {
            F( T, ti_offset, prf, salt, iterationCount, i );
            ti_offset += hLen;
        }

        if (r < hLen) {
            // Incomplete last block
            byte DK[] = new byte[dkLen];
            System.arraycopy(T, 0, DK, 0, dkLen);
            return DK;
        }
        return T;
    } 


    private static void F( byte[] dest, int offset, Mac prf, byte[] S, int c, int blockIndex ) {
        final int hLen = prf.getMacLength();
        byte U_r[] = new byte[ hLen ];
        // U0 = S || INT (i);
        byte U_i[] = new byte[S.length + 4];
        System.arraycopy( S, 0, U_i, 0, S.length );
        INT( U_i, S.length, blockIndex );
        for( int i = 0; i < c; i++ ) {
            U_i = prf.doFinal( U_i );
            xor( U_r, U_i );
        }

        System.arraycopy( U_r, 0, dest, offset, hLen );
    }

    private static void xor( byte[] dest, byte[] src ) {
        for( int i = 0; i < dest.length; i++ ) {
            dest[i] ^= src[i];
        }
    }

    private static void INT( byte[] dest, int offset, int i ) {
        dest[offset + 0] = (byte) (i / (256 * 256 * 256));
        dest[offset + 1] = (byte) (i / (256 * 256));
        dest[offset + 2] = (byte) (i / (256));
        dest[offset + 3] = (byte) (i);
    } 

    // ctor
    private PBKDF2 () {}

}
3 голосов
/ 22 февраля 2012

В очень полезном ответе Cheeso, приведенном выше, есть плохая ошибка производительности.

Линия

int l = Math.max( dkLen, hLen)

должен рассчитывать не максимум, а потолок деления, поэтому

int l = ((dkLen - 1) / hLen) + 1; // >= ceil(dkLen / hLen), == for dkLen =>1

Это ускорит вычисления в 20 раз для 16-байтовых ключей.

2 голосов
/ 17 декабря 2008

Вам нужна библиотека шифрования, которая расскажет вам, как ее настроить.
Мне нравится материал от bouncycastle.org. Вы можете найти их как здесь DES, ссылка на который приведена в примере 5.1, является одним из предлагаемых ими шифрований. Что означает настоящая строка, будет зависеть от поставщика. По сути вы загружаете библиотеку.

Security.addProvider(new BouncyCastleProvider());

А затем только используйте интерфейсы JCE, чтобы делать все, что вы хотите:

 keyGen = KeyGenerator.getInstance("DES", "BC");

Java обрабатывает привязку библиотеки и интерфейсов для вас, вам не нужно этого делать. Я был бы более рад объяснить больше, если у вас есть какие-либо вопросы. К сожалению, в настоящее время я страдаю от болезни «я не помню, как я это узнал», поэтому, пожалуйста, не стесняйтесь спрашивать.

1 голос
/ 17 декабря 2008

Вы можете использовать алгоритм хеширования (при необходимости, несколько раз), чтобы перейти от ключевой фразы к некоторым необработанным данным, которые вы можете использовать в качестве ключа (+ вектор инициализации, если алгоритм требует один).

Затем вы можете использовать этот ключ с любым симметричным алгоритмом - таким как 3DES-CBC или AES-CBC (DES считается устаревшим в наши дни).

В зависимости от имеющейся у вас JCE у вас могут быть разные алгоритмы, но AES, вероятно, то, что вам нужно. Однако выбор алгоритма и то, как именно его использовать, является в некотором роде религиозным вопросом, и вам бы не советовали пробовать свой собственный или даже пытаться создать собственную схему шифрования с использованием стандартных алгоритмов. Вы почти наверняка ошибетесь, если не изучали его, и, возможно, даже если у вас есть.

Если безопасность так важна для вас, что вы рассматриваете шифрование, то вам следует рассмотреть также книгу по технике безопасности, такую ​​как «Прикладная криптография» Брюса Шнайера или «Безопасность инженерии» Росса Андерсона, - существует много ошибок при реализации. Например, использование парольной фразы в качестве ключа - не самая лучшая идея, поскольку она существенно уменьшает размер вашего ключа.

Вы также можете посмотреть на проекты, которые делали другие люди, на IETF есть много, например: http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha1-00

0 голосов
/ 23 декабря 2014

Преобразование вашей строки в байтовый массив во время шифрования. Преобразовать обратно в строку после расшифровки.

/**
 * Creates a cipher for encryption or decryption.
 * 
 * @param algorithm  PBE algorithm like "PBEWithMD5AndDES" or "PBEWithMD5AndTripleDES".
 * @param mode Encyrption or decyrption.
 * @param password Password
 * @param salt Salt usable with algorithm.
 * @param count Iterations.
 * @return Ready initialized cipher.
 * @throws GeneralSecurityException Error creating the cipher.
 */
private static Cipher createCipher(final String algorithm, final int mode, final char[] password, final byte[] salt, final int count) throws GeneralSecurityException {
    final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
    final PBEKeySpec keySpec = new PBEKeySpec(password);
    final SecretKey key = keyFactory.generateSecret(keySpec);
    final Cipher cipher = Cipher.getInstance(algorithm);
    final PBEParameterSpec params = new PBEParameterSpec(salt, count);
    cipher.init(mode, key, params);
    return cipher;
}

/**
 * Encrypts some data based on a password.
 * @param algorithm PBE algorithm like "PBEWithMD5AndDES" or "PBEWithMD5AndTripleDES"
 * @param data Data to encrypt
 * @param password Password
 * @param salt Salt usable with algorithm
 * @param count Iterations.
 * @return Encrypted data.
 */
public static byte[] encryptPasswordBased(final String algorithm, final byte[] data, final char[] password, final byte[] salt, final int count) {
    Validate.notNull(algorithm);
    Validate.notNull(data);
    Validate.notNull(password);
    Validate.notNull(salt);
    try {
        final Cipher cipher = createCipher(algorithm, Cipher.ENCRYPT_MODE, password, salt, count);
        return cipher.doFinal(data);
    } catch (final Exception ex) {
        throw new RuntimeException("Error encrypting the password!", ex);
    }
}

/**
 * Decrypts some data based on a password.
 * @param algorithm PBE algorithm like "PBEWithMD5AndDES" or "PBEWithMD5AndTripleDES"
 * @param encryptedData Data to decrypt
 * @param password Password
 * @param salt Salt usable with algorithm
 * @param count Iterations.
 * @return Encrypted data.
 */
public static byte[] decryptPasswordBased(final String algorithm, final byte[] encryptedData, final char[] password, final byte[] salt, final int count) {
    Validate.notNull(algorithm);
    Validate.notNull(encryptedData);
    Validate.notNull(password);
    Validate.notNull(salt);
    try {
        final Cipher cipher = createCipher(algorithm, Cipher.DECRYPT_MODE, password, salt, count);
        return cipher.doFinal(encryptedData);
    } catch (final Exception ex) {
        throw new RuntimeException("Error decrypting the password!", ex);
    }
}
0 голосов
/ 23 декабря 2008

Если вам не нужно дешифровать парольную фразу, а просто сгенерировать ключ шифрования на основе пароля / парольной фразы, вы можете реализовать стандарт PKCS # 5 , используя классы JCE Cipher и MessageDigest.

...