web-dev-qa-db-fra.com

Comment chiffrer String en Java

Ce dont j'ai besoin, c'est de chiffrer une chaîne qui apparaîtra dans un code à barres 2D (PDF-417). Ainsi, si quelqu'un a une idée à numériser, rien ne sera lisible. 

Autres exigences: 

  • ne devrait pas être compliqué
  • il ne devrait pas comprendre RSA, infrastructure PKI, paires de clés, etc.

Il doit être assez simple pour se débarrasser des personnes qui fouinent dans la rue et facile à décrypter pour les autres entreprises intéressées à obtenir ces données. Ils nous appellent, nous leur disons la norme ou leur donnons une clé simple qui peut ensuite être utilisée pour le décryptage. 

Ces entreprises pourraient probablement utiliser différentes technologies. Il serait donc bon de s'en tenir à une norme qui ne soit pas liée à une plate-forme ou une technologie particulière. 

Que suggérez-vous? Existe-t-il une classe Java effectuant encrypt () decrypt () sans trop de complications pour atteindre des normes de sécurité élevées? 

119
ante.sabo

Attention

Ne pas utiliser ceci comme une sorte de mesure de sécurité.

Le mécanisme de cryptage de cette publication est un pad à usage unique, ce qui signifie que la clé secrète peut être facilement récupérée par un attaquant utilisant 2 messages cryptés. XOR 2 messages chiffrés et vous obtenez la clé. C'est simple! 

Signalé par Moussa


J'utilise le Base64Encoder/Decoder de Sun, qui se trouve dans le JRE de Sun, pour éviter encore un autre JAR dans lib. C'est dangereux d'utiliser OpenJDK ou le JRE d'un autre. Outre cela, y a-t-il une autre raison pour laquelle je devrais envisager d'utiliser Apache commons lib avec Encoder/Decoder? 

public class EncryptUtils {
    public static final String DEFAULT_ENCODING = "UTF-8"; 
    static BASE64Encoder enc = new BASE64Encoder();
    static BASE64Decoder dec = new BASE64Decoder();

    public static String base64encode(String text) {
        try {
            return enc.encode(text.getBytes(DEFAULT_ENCODING));
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }//base64encode

    public static String base64decode(String text) {
        try {
            return new String(dec.decodeBuffer(text), DEFAULT_ENCODING);
        } catch (IOException e) {
            return null;
        }
    }//base64decode

    public static void main(String[] args) {
        String txt = "some text to be encrypted";
        String key = "key phrase used for XOR-ing";
        System.out.println(txt + " XOR-ed to: " + (txt = xorMessage(txt, key)));

        String encoded = base64encode(txt);       
        System.out.println(" is encoded to: " + encoded + " and that is decoding to: " + (txt = base64decode(encoded)));
        System.out.print("XOR-ing back to original: " + xorMessage(txt, key));
    }

    public static String xorMessage(String message, String key) {
        try {
            if (message == null || key == null) return null;

            char[] keys = key.toCharArray();
            char[] mesg = message.toCharArray();

            int ml = mesg.length;
            int kl = keys.length;
            char[] newmsg = new char[ml];

            for (int i = 0; i < ml; i++) {
                newmsg[i] = (char)(mesg[i] ^ keys[i % kl]);
            }//for i

            return new String(newmsg);
        } catch (Exception e) {
            return null;
        }
    }//xorMessage
}//class
20
ante.sabo

Je recommanderais d'utiliser un chiffrement symétrique standard largement disponible, tel que DES , 3DES ou AES . Bien que cet algorithme ne soit pas le plus sûr, il existe de nombreuses implémentations et il vous suffirait de donner la clé à toute personne supposée déchiffrer les informations contenues dans le code à barres. javax.crypto.Cipher est ce que vous voulez travailler avec ici.

Supposons que les octets à chiffrer sont en

byte[] input;

Ensuite, vous aurez besoin de la clé et du vecteur d'initialisation bytes

byte[] keyBytes;
byte[] ivBytes;

Vous pouvez maintenant initialiser le chiffre pour l'algorithme que vous avez sélectionné:

// wrap key data in Key/IV specs to pass to cipher
SecretKeySpec key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
// create the cipher with the algorithm you choose
// see javadoc for Cipher class for more info, e.g.
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

Le chiffrement serait comme ceci:

cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] encrypted= new byte[cipher.getOutputSize(input.length)];
int enc_len = cipher.update(input, 0, input.length, encrypted, 0);
enc_len += cipher.doFinal(encrypted, enc_len);

Et le décryptage comme ceci:

cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decrypted = new byte[cipher.getOutputSize(enc_len)];
int dec_len = cipher.update(encrypted, 0, enc_len, decrypted, 0);
dec_len += cipher.doFinal(decrypted, dec_len);
110
VoidPointer

merci ive fait cette classe en utilisant votre code peut-être que quelqu'un le trouve userfull

cryptage d'objet 

import Java.io.ByteArrayInputStream;
import Java.io.ByteArrayOutputStream;
import Java.io.IOException;
import Java.io.ObjectInputStream;
import Java.io.ObjectOutputStream;
import Java.security.InvalidAlgorithmParameterException;
import Java.security.InvalidKeyException;
import Java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class ObjectCrypter {

private Cipher deCipher;
private Cipher enCipher;
private SecretKeySpec key;
private IvParameterSpec ivSpec;


public ObjectCrypter(byte[] keyBytes,   byte[] ivBytes) {
    // wrap key data in Key/IV specs to pass to cipher


     ivSpec = new IvParameterSpec(ivBytes);
    // create the cipher with the algorithm you choose
    // see javadoc for Cipher class for more info, e.g.
    try {
         DESKeySpec dkey = new  DESKeySpec(keyBytes);
          key = new SecretKeySpec(dkey.getKey(), "DES");
         deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
         enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
    } catch (NoSuchAlgorithmException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
public byte[] encrypt(Object obj) throws InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, ShortBufferException, BadPaddingException {
    byte[] input = convertToByteArray(obj);
    enCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

    return enCipher.doFinal(input);




//  cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
//  byte[] encypted = new byte[cipher.getOutputSize(input.length)];
//  int enc_len = cipher.update(input, 0, input.length, encypted, 0);
//  enc_len += cipher.doFinal(encypted, enc_len);
//  return encypted;


}
public Object decrypt( byte[]  encrypted) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, IOException, ClassNotFoundException {
    deCipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

    return convertFromByteArray(deCipher.doFinal(encrypted));

}



private Object convertFromByteArray(byte[] byteObject) throws IOException,
        ClassNotFoundException {
    ByteArrayInputStream bais;

    ObjectInputStream in;
    bais = new ByteArrayInputStream(byteObject);
    in = new ObjectInputStream(bais);
    Object o = in.readObject();
    in.close();
    return o;

}



private byte[] convertToByteArray(Object complexObject) throws IOException {
    ByteArrayOutputStream baos;

    ObjectOutputStream out;

    baos = new ByteArrayOutputStream();

    out = new ObjectOutputStream(baos);

    out.writeObject(complexObject);

    out.close();

    return baos.toByteArray();

}


}
11
sherif

Que dis-tu de ça:

private static byte[] xor(final byte[] input, final byte[] secret) {
    final byte[] output = new byte[input.length];
    if (secret.length == 0) {
        throw new IllegalArgumentException("empty security key");
    }
    int spos = 0;
    for (int pos = 0; pos < input.length; ++pos) {
        output[pos] = (byte) (input[pos] ^ secret[spos]);
        ++spos;
        if (spos >= secret.length) {
            spos = 0;
        }
    }
    return output;
}

Fonctionne bien pour moi et est plutôt compact.

6
yegor256

Voici mon implémentation de meta64.com en tant que Spring Singleton. Si vous souhaitez créer une instance ciper pour chaque appel, cela fonctionnera également et vous pourrez alors supprimer les appels "synchronisés", mais méfiez-vous "cipher" n'est pas thread-safe.

import Java.security.Key;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("singleton")
public class Encryptor {

    @Value("${aeskey}")
    private String keyStr;

    private Key aesKey = null;
    private Cipher cipher = null;

    synchronized private void init() throws Exception {
        if (keyStr == null || keyStr.length() != 16) {
            throw new Exception("bad aes key configured");
        }
        if (aesKey == null) {
            aesKey = new SecretKeySpec(keyStr.getBytes(), "AES");
            cipher = Cipher.getInstance("AES");
        }
    }

    synchronized public String encrypt(String text) throws Exception {
        init();
        cipher.init(Cipher.ENCRYPT_MODE, aesKey);
        return toHexString(cipher.doFinal(text.getBytes()));
    }

    synchronized public String decrypt(String text) throws Exception {
        init();
        cipher.init(Cipher.DECRYPT_MODE, aesKey);
        return new String(cipher.doFinal(toByteArray(text)));
    }

    public static String toHexString(byte[] array) {
        return DatatypeConverter.printHexBinary(array);
    }

    public static byte[] toByteArray(String s) {
        return DatatypeConverter.parseHexBinary(s);
    }

    /*
     * DO NOT DELETE
     * 
     * Use this commented code if you don't like using DatatypeConverter dependency
     */
    // public static String toHexStringOld(byte[] bytes) {
    // StringBuilder sb = new StringBuilder();
    // for (byte b : bytes) {
    // sb.append(String.format("%02X", b));
    // }
    // return sb.toString();
    // }
    //
    // public static byte[] toByteArrayOld(String s) {
    // int len = s.length();
    // byte[] data = new byte[len / 2];
    // for (int i = 0; i < len; i += 2) {
    // data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i +
    // 1), 16));
    // }
    // return data;
    // }
}
4
user2080225

Vous pouvez utiliser Jasypt

Avec Jasypt, chiffrer et vérifier un mot de passe peut être aussi simple que ...

StrongTextEncryptor textEncryptor = new StrongTextEncryptor();
textEncryptor.setPassword(myEncryptionPassword);

Cryptage:

String myEncryptedText = textEncryptor.encrypt(myText);

Décryptage:

String plainText = textEncryptor.decrypt(myEncryptedText);

Gradle:

compile group: 'org.jasypt', name: 'jasypt', version: '1.9.2'

Caractéristiques:

Jasypt vous fournit des techniques simples de cryptage unidirectionnel (condensé) et bidirectionnel. 

API ouverte à utiliser avec n’importe quel fournisseur JCE, et pas seulement avec le fournisseur Java VM par défaut. Jasypt peut être facilement utilisé avec des fournisseurs bien connus comme Bouncy Castle. Apprendre encore plus.

Sécurité accrue pour les mots de passe de vos utilisateurs. Apprendre encore plus.

Prise en charge du cryptage binaire. Jasypt permet la digestion et le cryptage des fichiers binaires (tableaux d'octets). Cryptez vos objets ou vos fichiers si nécessaire (pour être envoyé sur le réseau, par exemple).

Prise en charge du cryptage numérique. Outre les textes et les fichiers binaires, il permet le condensé et le cryptage des valeurs numériques (BigInteger et BigDecimal, d'autres types numériques sont pris en charge lors du cryptage pour la persistance Hibernate). Apprendre encore plus.

Complètement thread-safe.

Prise en charge du regroupement encryptor/digester, afin d'obtenir des performances élevées dans les systèmes multiprocesseurs/multicœurs.

Inclut une version allégée ("allégée") de la bibliothèque pour une meilleure gestion dans des environnements restreints en taille tels que les plates-formes mobiles. 

Fournit à la fois des outils de chiffrement simples et sans configuration pour les utilisateurs débutants en chiffrement, ainsi que des outils de chiffrement standard hautement configurables pour les utilisateurs chevronnés.

Intégration optionnelle d'Hibernate 3 et 4 pour les champs persistants de vos entités mappées de manière cryptée. Le chiffrement des champs est défini dans les fichiers de mappage Hibernate et reste transparent pour le reste de l'application (utile pour les données personnelles sensibles, les bases de données contenant de nombreux utilisateurs à lecture activée ...). Cryptez des textes, des fichiers binaires, des nombres, des booléens, des dates ... En savoir plus.

Intégrable de manière transparente dans une application Spring, avec des fonctionnalités d'intégration spécifiques pour Spring 2, Spring 3.0 et Spring 3.1. Tous les digesteurs et les chiffreurs de jasypt sont conçus pour être facilement utilisés (instanciés, dépendants, injectés ...) à partir de Spring. Et, du fait de leur sécurité thread-safe, ils peuvent être utilisés sans souci de synchronisation dans un environnement orienté singleton, tel que Spring. En savoir plus: Spring 2, Spring 3.0, Spring 3.1.

Intégration facultative de Spring Security (anciennement Acegi Security) pour le chiffrement de mot de passe et les tâches correspondantes du cadre de sécurité, améliorant la sécurité des mots de passe de vos utilisateurs en utilisant des mécanismes de chiffrement de mot de passe plus sûrs et en vous fournissant un degré de configuration et de contrôle supérieur Apprendre encore plus.

Fournit des fonctionnalités avancées pour le cryptage de tout ou partie des fichiers de configuration d'une application, y compris des informations sensibles telles que les mots de passe de base de données. Intégrez de manière transparente la configuration chiffrée dans des applications standard, basées sur Spring et/ou compatibles Hibernate. Apprendre encore plus.

Fournit des outils CLI (Command Line Interface) faciles à utiliser pour permettre aux développeurs d’initialiser leurs données cryptées et d’inclure des opérations de cryptage/décryptage/digestion dans des tâches de maintenance ou des scripts. Apprendre encore plus.

S'intègre à Apache Wicket pour un cryptage plus robuste des URL dans vos applications sécurisées. 

Des guides complets et une documentation javadoc permettant aux développeurs de mieux comprendre ce qu’ils font réellement avec leurs données.

Prise en charge robuste des jeux de caractères, conçue pour chiffrer et digérer correctement les textes, quel que soit le jeu de caractères d'origine. Prise en charge complète de langues telles que le japonais, le coréen, l'arabe ... sans problèmes de codage ou de plate-forme.

Très haut niveau de capacités de configuration: le développeur peut mettre en œuvre des astuces telles que demander à un "chiffreur" de demander, par exemple, à un serveur HTTPS distant, le mot de passe à utiliser pour le chiffrement. Il vous permet de répondre à vos besoins de sécurité.

4
user3871754

J'envisagerais d'utiliser quelque chose comme https://www.bouncycastle.org/ C'est une bibliothèque pré-construite qui vous permet de crypter ce que vous voulez avec un certain nombre de chiffrements différents de fouiller, mais si vous voulez vraiment protéger les informations, utiliser Base64 ne vous protégera pas réellement.

3
hdost

Voici quelques liens que vous pouvez lire sur ce que Java supporte

Cryptage/décryptage d'un flux de données.

Cet exemple montre comment chiffrer (en utilisant un algorithme de chiffrement symétrique tel que AES, Blowfish, RC2, 3DES, etc.) une grande quantité de données. Le les données sont transmises par morceaux à l'un des fichiers méthodes de cryptage: EncryptBytes, EncryptString, EncryptBytesENC ou EncryptStringENC. (Le nom de la méthode Indique le type d'entrée (chaîne ou tableau d'octets .__) et le type de retour (Chaîne codée ou tableau d'octets). Les propriétés FirstChunk et LastChunk Sont utilisées pour indique si un bloc est le premier, le deuxième ou le dernier média à être crypté Par défaut, FirstChunk et LastChunk sont égaux true - signifiant que les données ont été transmises est le montant total.

JCERefGuide

Exemples de cryptage Java

2
Markus Lausberg

Voici une solution copier/coller. Je recommande également de lire et de voter pour @ la réponse de Konstantino même si elle ne fournit aucun code. Le vecteur d’initialisation (IV) ressemble à un sel - il n’a pas à le garder secret. Je suis nouveau sur GCM et apparemment, AAD est facultatif et utilisé uniquement dans certaines circonstances. Définissez la clé dans la variable d'environnement SECRET_KEY_BASE. Utilisez quelque chose comme KeePass pour générer un mot de passe de 32 caractères. Cette solution est calquée sur ma solution 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;
        }
    }

Voici un exemple:

    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
Chloe

Voici une solution simple avec uniquement les dépendances Java.* et javax.crypto.* pour le cryptage des octets fournissant privacy et intégrité. Il sera impossible de distinguer une attaque en texte clair choisie pour les messages courts de l'ordre du kilo-octets.

Il utilise AES dans le mode GCM sans remplissage, une clé de 128 bits est dérivée de PBKDF2 avec de nombreuses itérations et un sel statique à partir du mot de passe fourni. Cela garantit que le forçage brutal des mots de passe est difficile et distribue l'entropie sur la clé entière.

Un vecteur d’initialisation aléatoire (IV) est généré et sera ajouté au texte chiffré. De plus, l'octet statique 0x01 est ajouté avant le premier octet en tant que "version".

Le message entier entre dans le code d'authentification de message (MAC) généré par AES/GCM.

Voici la classe de chiffrement zéro dépendance externe fournissant privacy et intégrité:

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
     * </pre>
     * Note: There is no padding required for AES-GCM, but this also implies that
     * the exact plaintext length is revealed.
     *
     * @param password  password to use for encryption
     * @param plaintext plaintext to encrypt
     * @throws NoSuchAlgorithmException
     * @throws NoSuchProviderException
     * @throws NoSuchPaddingException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidKeySpecException
     */
    public byte[] encrypt(char[] password, byte[] plaintext)
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException,
            InvalidKeySpecException {

        // initialise random and generate IV (initialisation vector)
        SecretKey key = deriveAesKey(password, PBKDF2_SALT, AES_KEY_BITS_LENGTH);
        final byte[] iv = new byte[GCM_IV_BYTES_LENGTH];
        SecureRandom random = SecureRandom.getInstanceStrong();
        random.nextBytes(iv);

        // encrypt
        Cipher cipher = Cipher.getInstance(GCM_CRYPTO_NAME);
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_BYTES_LENGTH * 8, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);

        // add IV to MAC
        final byte[] versionBytes = new byte[]{VERSION_BYTE};
        cipher.updateAAD(versionBytes);
        cipher.updateAAD(iv);

        // encrypt and MAC plaintext
        byte[] ciphertext = cipher.doFinal(plaintext);

        // prepend VERSION and IV to ciphertext
        byte[] encrypted = new byte[1 + GCM_IV_BYTES_LENGTH + ciphertext.length];
        int pos = 0;
        System.arraycopy(versionBytes, 0, encrypted, 0, VERSION_BYTE_LENGTH);
        pos += VERSION_BYTE_LENGTH;
        System.arraycopy(iv, 0, encrypted, pos, iv.length);
        pos += iv.length;
        System.arraycopy(ciphertext, 0, encrypted, pos, ciphertext.length);

        return encrypted;
    }

    /**
     * We derive a fixed length AES key with uniform entropy from a provided
     * passphrase. This is done with PBKDF2/HMAC256 with a fixed count
     * of iterations and a provided salt.
     *
     * @param password passphrase to derive key from
     * @param salt     salt for PBKDF2 if possible use a per-key salt, alternatively
     *                 a random constant salt is better than no salt.
     * @param keyLen   number of key bits to output
     * @return a SecretKey for AES derived from a passphrase
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private SecretKey deriveAesKey(char[] password, byte[] salt, int keyLen)
            throws NoSuchAlgorithmException, InvalidKeySpecException {

        if (password == null || salt == null || keyLen <= 0) {
            throw new IllegalArgumentException();
        }
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password, salt, PBKDF2_ITERATIONS, keyLen);
        SecretKey pbeKey = factory.generateSecret(spec);

        return new SecretKeySpec(pbeKey.getEncoded(), "AES");
    }

    /**
     * Helper to convert hex strings to bytes.
     * <p>
     * May be used to read bytes from constants.
     */
    private static byte[] hexStringToByteArray(String s) {

        if (s == null) {
            throw new IllegalArgumentException("Provided `null` string.");
        }

        int len = s.length();
        if (len % 2 != 0) {
            throw new IllegalArgumentException("Invalid length: " + len);
        }

        byte[] data = new byte[len / 2];
        for (int i = 0; i < len - 1; i += 2) {
            byte b = (byte) toHexDigit(s, i);
            b <<= 4;
            b |= toHexDigit(s, i + 1);
            data[i / 2] = b;
        }
        return data;
    }

    private static int toHexDigit(String s, int pos) {
        int d = Character.digit(s.charAt(pos), 16);
        if (d < 0) {
            throw new IllegalArgumentException("Cannot parse hex digit: " + s + " at " + pos);
        }
        return d;
    }

    private static String byteArrayToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    public class CryptoException extends Exception {

        public CryptoException(Throwable cause) {
            super(cause);
        }
    }
}

Voici le projet entier avec une CLI de Nice: https://github.com/trichner/tcrypt

Edit: maintenant avec encryptString et decryptString appropriés

0
trichner