Как зашифровать строку в Java - PullRequest
127 голосов
/ 30 июля 2009

Мне нужно зашифровать строку, которая будет отображаться в 2D-штрих-коде (PDF-417), чтобы, когда у кого-то появилась идея отсканировать, ничего не читалось.

Другие требования:

  • не должно быть сложным
  • он не должен состоять из RSA, инфраструктуры PKI, пар ключей и т. Д.

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

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

Что вы предлагаете? Есть ли какой-нибудь класс Java, выполняющий encrypt() & decrypt() без особых сложностей в достижении высоких стандартов безопасности?

Ответы [ 16 ]

1 голос
/ 28 февраля 2018

Здесь простое решение с только java.* и javax.crypto.* зависимостями для шифрования байтов, обеспечивающее конфиденциальность и целостность . Он должен быть неотличимым при выбранной атаке открытым текстом для коротких сообщений порядка килобайт.

Он использует AES в режиме GCM без заполнения, 128-битный ключ получается из PBKDF2 с большим количеством итераций и статической солью из предоставленного пароля. Это гарантирует, что перебор паролей будет трудным и распределяет энтропию по всему ключу.

Генерируется случайный вектор инициализации (IV), который будет добавлен к зашифрованному тексту. Кроме того, статический байт 0x01 добавляется как первый байт как «версия».

Все сообщение отправляется в код аутентификации сообщения (MAC), сгенерированный AES/GCM.

Вот так, класс шифрования с нулевыми внешними зависимостями обеспечивает конфиденциальность и целостность :

<code>package ch.n1b.tcrypt.utils;

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * This class implements AES-GCM symmetric key encryption with a PBKDF2 derived password.
 * It provides confidentiality and integrity of the plaintext.
 *
 * @author Thomas Richner
 * @created 2018-12-07
 */
public class AesGcmCryptor {

    // https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode
    private static final byte VERSION_BYTE = 0x01;
    private static final int VERSION_BYTE_LENGTH = 1;
    private static final int AES_KEY_BITS_LENGTH = 128;


    // fixed AES-GCM constants
    private static final String GCM_CRYPTO_NAME = "AES/GCM/NoPadding";
    private static final int GCM_IV_BYTES_LENGTH = 12;
    private static final int GCM_TAG_BYTES_LENGTH = 16;

    // can be tweaked, more iterations = more compute intensive to brute-force password
    private static final int PBKDF2_ITERATIONS = 1024;

    // protects against rainbow tables
    private static final byte[] PBKDF2_SALT = hexStringToByteArray("4d3fe0d71d2abd2828e7a3196ea450d4");

    public String encryptString(char[] password, String plaintext) throws CryptoException {

        byte[] encrypted = null;
        try {
            encrypted = encrypt(password, plaintext.getBytes(StandardCharsets.UTF_8));
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException //
                | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException //
                | InvalidKeySpecException e) {
            throw new CryptoException(e);
        }
        return byteArrayToHexString(encrypted);
    }

    public String decryptString(char[] password, String ciphertext)
            throws CryptoException {

        byte[] ct = hexStringToByteArray(ciphertext);
        byte[] plaintext = null;
        try {
            plaintext = decrypt(password, ct);
        } catch (AEADBadTagException e) {
            throw new CryptoException(e);
        } catch ( //
                NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeySpecException //
                        | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException //
                        | BadPaddingException e) {
            throw new CryptoException(e);
        }
        return new String(plaintext, StandardCharsets.UTF_8);
    }

    /**
     * Decrypts an AES-GCM encrypted ciphertext and is
     * the reverse operation of {@link AesGcmCryptor#encrypt(char[], byte[])}
     *
     * @param password   passphrase for decryption
     * @param ciphertext encrypted bytes
     * @return plaintext bytes
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws InvalidKeySpecException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws IllegalArgumentException           if the length or format of the ciphertext is bad
     * @throws CryptoException
     */
    public byte[] decrypt(char[] password, byte[] ciphertext)
            throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {

        // input validation
        if (ciphertext == null) {
            throw new IllegalArgumentException("ciphertext cannot be null");
        }

        if (ciphertext.length <= VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH + GCM_TAG_BYTES_LENGTH) {
            throw new IllegalArgumentException("ciphertext too short");
        }

        // the version must match, we don't decrypt other versions
        if (ciphertext[0] != VERSION_BYTE) {
            throw new IllegalArgumentException("wrong version: " + ciphertext[0]);
        }

        // input seems legit, lets decrypt and check integrity

        // derive key from password
        SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);

        // init cipher
        Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
        GCMParameterSpec params = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8,
                ciphertext,
                VERSION_BYTE_LENGTH,
                GCM_IV_BYTES_LENGTH
        );
        cipher.init(Cipher.DECRYPT_MODE, key, params);

        final int ciphertextOffset = VERSION_BYTE_LENGTH + GCM_IV_BYTES_LENGTH;

        // add version and IV to MAC
        cipher.updateAAD(ciphertext, 0, ciphertextOffset);

        // decipher and check MAC
        return cipher.doFinal(ciphertext, ciphertextOffset, ciphertext.length - ciphertextOffset);
    }

    /**
     * Encrypts a plaintext with a password.
     * <p>
     * The encryption provides the following security properties:
     * Confidentiality + Integrity
     * <p>
     * This is achieved my using the AES-GCM AEAD blockmode with a randomized IV.
     * <p>
     * The tag is calculated over the version byte, the IV as well as the ciphertext.
     * <p>
     * Finally the encrypted bytes have the following structure:
     * <pre>
     *          +-------------------------------------------------------------------+
     *          |         |               |                             |           |
     *          | version | IV bytes      | ciphertext bytes            |    tag    |
     *          |         |               |                             |           |
     *          +-------------------------------------------------------------------+
     * Length:     1B        12B            len(plaintext) bytes            16B
     * 
* Примечание: для AES-GCM не требуется заполнение, но это также подразумевает, что * указана точная длина открытого текста. * * @param пароль пароль, чтобы использовать для шифрования * @param открытый текст для шифрования * @throws NoSuchAlgorithmException * @throws NoSuchProviderException * @throws NoSuchPaddingException * @throws InvalidAlgorithmParameterException * @throws InvalidKeyException * @throws BadPaddingException * @throws IllegalBlockSizeException * @throws InvalidKeySpecException * / публичный байт [] шифрование (char [] пароль, байт [] открытый текст) выдает NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidKeySpecException { // инициализируем случайное число и генерируем IV (вектор инициализации) Ключ SecretKey = производнаяAesKey (пароль, PBKDF2_SALT, AES_KEY_BITS_LENGTH); последний байт [] iv = новый байт [GCM_IV_BYTES_LENGTH]; SecureRandom random = SecureRandom.getInstanceStrong (); random.nextBytes (IV); // зашифровать Cipher cipher = Cipher.getInstance (GCM_CRYPTO_NAME); GCMParameterSpec spec = новый GCMParameterSpec (GCM_TAG_BYTES_LENGTH * 8, iv); cipher.init (Cipher.ENCRYPT_MODE, ключ, спецификация); // добавляем IV в MAC последний байт [] versionBytes = новый байт [] {VERSION_BYTE}; cipher.updateAAD (versionBytes); cipher.updateAAD (IV); // шифрование и открытый текст MAC byte [] ciphertext = cipher.doFinal (открытый текст); // добавляем VERION и IV к шифрованному тексту byte [] encrypted = новый байт [1 + GCM_IV_BYTES_LENGTH + ciphertext.length]; int pos = 0; System.arraycopy (versionBytes, 0, зашифрованный, 0, VERSION_BYTE_LENGTH); pos + = VERSION_BYTE_LENGTH; System.arraycopy (iv, 0, зашифрованный, pos, iv.length); pos + = длина волны; System.arraycopy (зашифрованный текст, 0, зашифрованный, pos, ciphertext.length); возврат зашифрован; } / ** * Мы получаем ключ AES фиксированной длины с равномерной энтропией из * пароль. Это делается с PBKDF2 / HMAC256 с фиксированным счетом * итераций и предоставленной соли. * * @param пароль пароль для получения ключа из * соль соли @param для PBKDF2, если возможно, используйте соль для каждого ключа, или * случайная постоянная соль лучше, чем без соли. * @param keyLen количество битов ключа для вывода * @ возврат секретного ключа для AES, полученного из парольной фразы * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException * / закрытое SecretKey производноеAesKey (пароль char [], соль байта [], int keyLen) выдает NoSuchAlgorithmException, InvalidKeySpecException { if (пароль == ноль || salt == ноль || keyLen <= 0) { бросить новый IllegalArgumentException (); }SecretKeyFactory factory = SecretKeyFactory.getInstance ("PBKDF2WithHmacSHA256"); KeySpec spec = новый PBEKeySpec (пароль, соль, PBKDF2_ITERATIONS, keyLen); SecretKey pbeKey = factory.generateSecret (spec); вернуть новый SecretKeySpec (pbeKey.getEncoded (), "AES"); } / ** * Помощник для преобразования шестнадцатеричных строк в байты. * <p> * Может использоваться для чтения байтов из констант. * / закрытый статический байт [] hexStringToByteArray (String s) { if (s == null) { throw new IllegalArgumentException ("Предоставленная строка` null`. "); } int len ​​= s.length (); if (len% 2! = 0) { бросить новое IllegalArgumentException ("Недопустимая длина:" + len); } byte [] data = new byte [len / 2]; для (int i = 0; i

Вот весь проект с симпатичным CLI: https://github.com/trichner/tcrypt

Редактировать: теперь с соответствующими encryptString и decryptString

0 голосов
/ 09 марта 2019

Вот решение для копирования / вставки. Я также рекомендую прочитать и проголосовать за @ ответ Константино , хотя он не предоставляет никакого кода. Вектор инициализации (IV) подобен соли - его не нужно хранить в секрете. Я новичок в GCM и, очевидно, AAD не является обязательным и используется только в определенных обстоятельствах. Задайте ключ в переменной окружения SECRET_KEY_BASE. Используйте что-то вроде KeePass для генерации 32-символьного пароля. Это решение смоделировано после моего решения на Ruby.

    public static String encrypt(String s) {
        try {
            byte[] input = s.getBytes("UTF-8");
            String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
            if (keyString == null || keyString.length() == 0) {
                Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
                return null;
            }
            byte[] keyBytes = keyString.getBytes("UTF-8");
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            // generate IV
            SecureRandom secureRandom = SecureRandom.getInstanceStrong();
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            secureRandom.nextBytes(ivBytes);
            GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes); // 96 bit tag length
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
            // generate AAD
//          byte[] aadBytes = new byte[cipher.getBlockSize()];
//          secureRandom.nextBytes(aadBytes);
//          cipher.updateAAD(aadBytes);
            // encrypt
            byte[] encrypted = cipher.doFinal(input);
            byte[] returnBytes = new byte[ivBytes.length + encrypted.length];
//          byte[] returnBytes = new byte[ivBytes.length + aadBytes.length + encrypted.length];
            System.arraycopy(ivBytes, 0, returnBytes, 0, ivBytes.length);
//          System.arraycopy(aadBytes, 0, returnBytes, ivBytes.length, aadBytes.length);
            System.arraycopy(encrypted, 0, returnBytes, ivBytes.length, encrypted.length);
//          System.arraycopy(encrypted, 0, returnBytes, ivBytes.length+aadBytes.length, encrypted.length);
            String encryptedString = Base64.getEncoder().encodeToString(returnBytes);
            return encryptedString;
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | 
                InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            Logger.error(Utils.class, "encrypt()", "Could not encrypt string: " + e.getMessage());
            return null;
        }
    }

    public static String decrypt(String s) {
        if (s == null || s.length() == 0) return "";
        try {
            byte[] encrypted = Base64.getDecoder().decode(s);
            String keyString = System.getProperty("SECRET_KEY_BASE", System.getenv("SECRET_KEY_BASE"));
            if (keyString == null || keyString.length() == 0) {
                Logger.error(Utils.class, "encrypt()", "$SECRET_KEY_BASE is not set.");
                return null;
            }
            byte[] keyBytes = keyString.getBytes("UTF-8");
            SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            byte[] ivBytes = new byte[cipher.getBlockSize()];
            System.arraycopy(encrypted, 0, ivBytes, 0, ivBytes.length);
            GCMParameterSpec gcmSpec = new GCMParameterSpec(96, ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
//          cipher.updateAAD(encrypted, ivBytes.length, cipher.getBlockSize());
            byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize(), encrypted.length - cipher.getBlockSize());
//          byte[] decrypted = cipher.doFinal(encrypted, cipher.getBlockSize()*2, encrypted.length - cipher.getBlockSize()*2);
            String decryptedString = new String(decrypted, "UTF-8");
            return decryptedString;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedEncodingException | InvalidKeyException | 
                InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            Logger.error(Utils.class, "decrypt()", "Could not decrypt string: " + e.getMessage());
            return null;
        }
    }

Вот пример:

    String s = "This is a test.";
    String enc = Utils.encrypt(s);
    System.out.println(enc);
    // fQHfYjbD+xAuN5XzH2ojk/EWNeKXUrKRSfx8LU+5dpuKkM/pueCMBjKCZw==
    String dec = Utils.decrypt(enc);
    System.out.println(dec);
    // This is a test.
0 голосов
/ 06 сентября 2018

Как многие из парней уже сказали, вы должны использовать стандартный шифр, который используется слишком часто, как DES или AES.

Простой пример того, как вы можете зашифровать и расшифровать строку в Java, используя AES .

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class EncryptorDemo {

    public static String encrypt(String key, String randomVector, String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(value.getBytes());
            System.out.println("encrypted text: "  + Base64.encodeBase64String(encrypted));
            return Base64.encodeBase64String(encrypted);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String key, String randomVector, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(randomVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] originalText = cipher.doFinal(Base64.decodeBase64(encrypted));
            System.out.println("decrypted text: "  + new String(originalText));
            return new String(originalText);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        String key = "JavasEncryptDemo"; // 128 bit key
        String randomVector = "RandomJavaVector"; // 16 bytes IV
        decrypt(key, randomVector, encrypt(key, randomVector, "Anything you want to encrypt!"));

    }
}
0 голосов
/ 16 августа 2017
String s1="arshad"; 
char[] s2=s1.toCharArray(); 
int s3= s2.length; 

  System.out.println(s3);
 int i=0; 

// for(int j=0;j<s3;j++) 
// System.out.println(s2[j]); 

for(i=0;i<((s3)/2);i++) { 

char z,f=10; 
z=(char) (s2[i] * f); 
s2[i]=s2[(s3-1)-i]; 
s2[(s3-1)-i]=z; 

String b=new String(s2);

 print(b);  }
0 голосов
/ 12 августа 2015

Возможно, вы захотите использовать какой-нибудь автоматизированный инструмент для генерации кода шифрования / дешифрования, например. https://www.stringencrypt.com/java-encryption/

Он может генерировать разные коды шифрования и дешифрования каждый раз для шифрования строки или файла.

Это очень удобно, когда дело доходит до быстрого строкового шифрования без использования RSA, AES и т. Д.

Пример результатов:

// encrypted with https://www.stringencrypt.com (v1.1.0) [Java]
// szTest = "Encryption in Java!"
String szTest = "\u9E3F\uA60F\uAE07\uB61B\uBE1F\uC62B\uCE2D\uD611" +
                "\uDE03\uE5FF\uEEED\uF699\uFE3D\u071C\u0ED2\u1692" +
                "\u1E06\u26AE\u2EDC";

for (int iatwS = 0, qUJQG = 0; iatwS < 19; iatwS++)
{
        qUJQG = szTest.charAt(iatwS);
        qUJQG ++;
        qUJQG = ((qUJQG << 5) | ( (qUJQG & 0xFFFF) >> 11)) & 0xFFFF;
        qUJQG -= iatwS;
        qUJQG = (((qUJQG & 0xFFFF) >> 6) | (qUJQG << 10)) & 0xFFFF;
        qUJQG ^= iatwS;
        qUJQG -= iatwS;
        qUJQG = (((qUJQG & 0xFFFF) >> 3) | (qUJQG << 13)) & 0xFFFF;
        qUJQG ^= 0xFFFF;
        qUJQG ^= 0xB6EC;
        qUJQG = ((qUJQG << 8) | ( (qUJQG & 0xFFFF) >> 8)) & 0xFFFF;
        qUJQG --;
        qUJQG = (((qUJQG & 0xFFFF) >> 5) | (qUJQG << 11)) & 0xFFFF;
        qUJQG ++;
        qUJQG ^= 0xFFFF;
        qUJQG += iatwS;
        szTest = szTest.substring(0, iatwS) + (char)(qUJQG & 0xFFFF) + szTest.substring(iatwS + 1);
}

System.out.println(szTest);

Мы постоянно используем его в нашей компании.

0 голосов
/ 27 февраля 2014
public static String encryptParams(String myTextInput) {

        String myKey = "40674244454045cb9a70040a30e1c007";
        String myVector = "@1B2c3D4e5F6g7H8";

        String encData = "";

        try{
            JavaEncryprtionUtil encUtil = new JavaEncryprtionUtil();
            encData = Base64.encodeToString(encUtil.encrypt(myTextInput.getBytes("UTF-8"), myKey.getBytes("UTF-8"), myVector.getBytes("UTF-8")),Base64.DEFAULT);
            System.out.println(encData);
        }catch(NoSuchAlgorithmException ex){
            ex.printStackTrace();
        }catch(NoSuchPaddingException ex){
            ex.printStackTrace();
        }catch(InvalidKeyException ex){
            ex.printStackTrace();
        }catch(InvalidAlgorithmParameterException ex){
            ex.printStackTrace();
        }catch(IllegalBlockSizeException ex){
            ex.printStackTrace();
        }catch(BadPaddingException ex){
            ex.printStackTrace();
        }catch(UnsupportedEncodingException ex){
            ex.printStackTrace();
        }

        return encData;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...