Как преобразовать String в SecretKey

Я хочу преобразовать String в secretKey

public void generateCode(String keyStr){ 
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
secretKey skey=keyStr;  //How can I make the casting here
//SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
}

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

EDIT: Я хочу вызвать эту функцию из другого места

 static public String encrypt(String message , String key , int keyLength) throws Exception {
     // Get the KeyGenerator
   KeyGenerator kgen = KeyGenerator.getInstance("AES");
    kgen.init(keyLength); // 192 and 256 bits may not be available
    // Generate the secret key specs.
     SecretKey skey = key; //here is the error
   byte[] raw = skey.getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
    // Instantiate the cipher
    Cipher cipher = Cipher.getInstance("AES");

    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    System.out.println("msg is" + message + "\n raw is" + raw);
    byte[] encrypted = cipher.doFinal(message.getBytes());
    String cryptedValue = new String(encrypted);
    System.out.println("encrypted string: " + cryptedValue);
    return cryptedValue;
}

Если бы кто-нибудь мог помочь, я был бы очень благодарен.

Ответ 1

Нет проверки целостности по этим причинам.

  • Потребность не очевидна из прецедента.
  • "AES/GCM/NoPadding" режим доступен только с Java 7 вперед
  • Это зависит от пользователя, если они хотят развернуть, например. HMAC и/или AESCMAC (рекомендуется).
  • Для этого потребуется дополнительный ключ как минимум, и два полных прохода.

Если у вас есть реализация режима GCM с обеих сторон - например, используя Bouncy Castle на Java 6 - пожалуйста, пойдите для этого, поскольку это намного безопаснее (пока "IV" действительно уникален). Это должно быть очень легко изменить реализацию.

Замечания по внедрению шифрования

  • Эта реализация небезопасна при использовании в неограниченной роли клиент/сервер из-за дополнительных атак (требуется 128 попыток на байт или ниже, в среднем, независимо от алгоритма или размера ключа). Вам нужно будет использовать MAC, HMAC или Signature над зашифрованными данными и проверить его перед расшифровкой, чтобы развернуть его в режиме клиент/сервер.
  • Decrypt вернет значение null, если дешифрование завершится с ошибкой. Это может указывать только на исключение заполнения, которое должно быть надлежащим образом обработано (предупреждаю ли я об отложенных атаках оракула?)
  • Неверные ключи будут возвращены как InvalidArgumentException.
  • Все остальные исключения, связанные с безопасностью, "подметаются под таблицу", так как это означает, что среда выполнения Java недействительна. Например, поддержка поддержки "UTF-8" и "AES/CBC/PKCS5Padding" требуется для каждой реализации Java SE.

Некоторые другие примечания

  • Пожалуйста, не пробуйте обратное и вставьте байты непосредственно во входную строку метода шифрования (например, с помощью new String(byte[])). Метод может терпеть неудачу!
  • Оптимизирован для удобства чтения. Перейдите к реализациям Base64 и CipherStream, если вы предпочитаете скорость и лучший объем памяти.
  • Вам нужен минимум Java 6 SE или совместимый для запуска этого кода.
  • Шифрование/дешифрование может завершиться с ошибкой для размеров ключей AES более 128 бит, так как вам могут понадобиться файлы политики для неограниченного шифрования (доступные от Oracle)
  • Остерегайтесь правительственных правил при экспорте шифрования.
  • Эта реализация использует шестнадцатеричные ключи вместо ключей base64, поскольку они достаточно малы, а шестнадцатеричный текст просто упрощается для редактирования/проверки вручную.
  • Используемые кодировки/декодирования hex и base64, извлеченные из JDK, не нужны никакие внешние библиотеки.
  • Uber прост в использовании, но, конечно, не очень объектно-ориентированный, не кэширование экземпляров объектов, используемых в шифровании/расшифровке. Рефакторинг по желанию.

ОК, вот какой код...

    public static String encrypt(final String plainMessage,
            final String symKeyHex) {
        final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);

        final byte[] encodedMessage = plainMessage.getBytes(Charset
                .forName("UTF-8"));
        try {
            final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final int blockSize = cipher.getBlockSize();

            // create the key
            final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");

            // generate random IV using block size (possibly create a method for
            // this)
            final byte[] ivData = new byte[blockSize];
            final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
            rnd.nextBytes(ivData);
            final IvParameterSpec iv = new IvParameterSpec(ivData);

            cipher.init(Cipher.ENCRYPT_MODE, symKey, iv);

            final byte[] encryptedMessage = cipher.doFinal(encodedMessage);

            // concatenate IV and encrypted message
            final byte[] ivAndEncryptedMessage = new byte[ivData.length
                    + encryptedMessage.length];
            System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
            System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage,
                    blockSize, encryptedMessage.length);

            final String ivAndEncryptedMessageBase64 = DatatypeConverter
                    .printBase64Binary(ivAndEncryptedMessage);

            return ivAndEncryptedMessageBase64;
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException(
                    "key argument does not contain a valid AES key");
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(
                    "Unexpected exception during encryption", e);
        }
    }

    public static String decrypt(final String ivAndEncryptedMessageBase64,
            final String symKeyHex) {
        final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);

        final byte[] ivAndEncryptedMessage = DatatypeConverter
                .parseBase64Binary(ivAndEncryptedMessageBase64);
        try {
            final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final int blockSize = cipher.getBlockSize();

            // create the key
            final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");

            // retrieve random IV from start of the received message
            final byte[] ivData = new byte[blockSize];
            System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
            final IvParameterSpec iv = new IvParameterSpec(ivData);

            // retrieve the encrypted message itself
            final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length
                    - blockSize];
            System.arraycopy(ivAndEncryptedMessage, blockSize,
                    encryptedMessage, 0, encryptedMessage.length);

            cipher.init(Cipher.DECRYPT_MODE, symKey, iv);

            final byte[] encodedMessage = cipher.doFinal(encryptedMessage);

            // concatenate IV and encrypted message
            final String message = new String(encodedMessage,
                    Charset.forName("UTF-8"));

            return message;
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException(
                    "key argument does not contain a valid AES key");
        } catch (BadPaddingException e) {
            // you'd better know about padding oracle attacks
            return null;
        } catch (GeneralSecurityException e) {
            throw new IllegalStateException(
                    "Unexpected exception during decryption", e);
        }
    }

Использование:

    String plain = "Zaphod just zis guy, ya knöw?";
    String encrypted = encrypt(plain, "000102030405060708090A0B0C0D0E0F");
    System.out.println(encrypted);
    String decrypted = decrypt(encrypted, "000102030405060708090A0B0C0D0E0F");
    if (decrypted != null && decrypted.equals(plain)) {
        System.out.println("Hey! " + decrypted);
    } else {
        System.out.println("Bummer!");
    }

Ответ 2

Здесь версия, использующая класс Base64 Util вместо DatatypeConverter

public static String encrypt(final String plainMessage,
                             final String symKeyHex) {
    final byte[] symKeyData = Base64.decode(symKeyHex,Base64.DEFAULT);

    final byte[] encodedMessage = plainMessage.getBytes(Charset
            .forName("UTF-8"));
    try {
        final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        final int blockSize = cipher.getBlockSize();

        // create the key
        final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");

        // generate random IV using block size (possibly create a method for
        // this)
        final byte[] ivData = new byte[blockSize];
        final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
        rnd.nextBytes(ivData);
        final IvParameterSpec iv = new IvParameterSpec(ivData);

        cipher.init(Cipher.ENCRYPT_MODE, symKey, iv);

        final byte[] encryptedMessage = cipher.doFinal(encodedMessage);

        // concatenate IV and encrypted message
        final byte[] ivAndEncryptedMessage = new byte[ivData.length
                + encryptedMessage.length];
        System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
        System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage,
                blockSize, encryptedMessage.length);

        final String ivAndEncryptedMessageBase64 = Base64.encodeToString(ivAndEncryptedMessage,Base64.DEFAULT);

        return ivAndEncryptedMessageBase64;
    } catch (InvalidKeyException e) {
        throw new IllegalArgumentException(
                "key argument does not contain a valid AES key");
    } catch (GeneralSecurityException e) {
        throw new IllegalStateException(
                "Unexpected exception during encryption", e);
    }
}

public static String decrypt(final String ivAndEncryptedMessageBase64,
                             final String symKeyHex) {
    final byte[] symKeyData = Base64.decode((symKeyHex),Base64.DEFAULT);

    final byte[] ivAndEncryptedMessage = Base64.decode(ivAndEncryptedMessageBase64,Base64.DEFAULT);
    try {
        final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        final int blockSize = cipher.getBlockSize();

        // create the key
        final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");

        // retrieve random IV from start of the received message
        final byte[] ivData = new byte[blockSize];
        System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
        final IvParameterSpec iv = new IvParameterSpec(ivData);

        // retrieve the encrypted message itself
        final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length
                - blockSize];
        System.arraycopy(ivAndEncryptedMessage, blockSize,
                encryptedMessage, 0, encryptedMessage.length);

        cipher.init(Cipher.DECRYPT_MODE, symKey, iv);

        final byte[] encodedMessage = cipher.doFinal(encryptedMessage);

        // concatenate IV and encrypted message
        final String message = new String(encodedMessage,
                Charset.forName("UTF-8"));

        return message;
    } catch (InvalidKeyException e) {
        throw new IllegalArgumentException(
                "key argument does not contain a valid AES key");
    } catch (BadPaddingException e) {
        // you'd better know about padding oracle attacks
        return null;
    } catch (GeneralSecurityException e) {
        throw new IllegalStateException(
                "Unexpected exception during decryption", e);
    }
}

Просто напоминание для тех, кто получает исключение для заполнения. Убедитесь, что вы используете правильную длину ключа. Подсказка: посмотрите на сообщение Маартен: его шестнадцатеричный ровно 32;) Это не совпадение:)