2012-08-02 16 views

Antwort

14

OpenSSL verwendet im Allgemeinen seine eigene passwortbasierte Schlüsselableitungsmethode, die in EVP_BytesToKey angegeben ist, siehe unten stehenden Code. Im Allgemeinen sollten Sie OpenSSL zwingen, den NIST-zugelassenen PBKDF2-Algorithmus zu verwenden.

import java.io.File; 
import java.io.IOException; 
import java.nio.charset.Charset; 
import java.nio.file.Files; 
import java.security.GeneralSecurityException; 
import java.security.MessageDigest; 
import java.util.Arrays; 
import java.util.List; 

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

import org.bouncycastle.util.encoders.Base64; 

/** 
* Class created for StackOverflow by owlstead. 
* This is open source, you are free to copy and use for any purpose. 
*/ 
public class OpenSSLDecryptor { 
    private static final Charset ASCII = Charset.forName("ASCII"); 
    private static final int INDEX_KEY = 0; 
    private static final int INDEX_IV = 1; 
    private static final int ITERATIONS = 1; 

    private static final int ARG_INDEX_FILENAME = 0; 
    private static final int ARG_INDEX_PASSWORD = 1; 

    private static final int SALT_OFFSET = 8; 
    private static final int SALT_SIZE = 8; 
    private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE; 

    private static final int KEY_SIZE_BITS = 256; 

    /** 
    * Thanks go to Ola Bini for releasing this source on his blog. 
    * The source was obtained from <a href="http://olabini.com/blog/tag/evp_bytestokey/">here</a> . 
    */ 
    public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, 
      byte[] salt, byte[] data, int count) { 
     byte[][] both = new byte[2][]; 
     byte[] key = new byte[key_len]; 
     int key_ix = 0; 
     byte[] iv = new byte[iv_len]; 
     int iv_ix = 0; 
     both[0] = key; 
     both[1] = iv; 
     byte[] md_buf = null; 
     int nkey = key_len; 
     int niv = iv_len; 
     int i = 0; 
     if (data == null) { 
      return both; 
     } 
     int addmd = 0; 
     for (;;) { 
      md.reset(); 
      if (addmd++ > 0) { 
       md.update(md_buf); 
      } 
      md.update(data); 
      if (null != salt) { 
       md.update(salt, 0, 8); 
      } 
      md_buf = md.digest(); 
      for (i = 1; i < count; i++) { 
       md.reset(); 
       md.update(md_buf); 
       md_buf = md.digest(); 
      } 
      i = 0; 
      if (nkey > 0) { 
       for (;;) { 
        if (nkey == 0) 
         break; 
        if (i == md_buf.length) 
         break; 
        key[key_ix++] = md_buf[i]; 
        nkey--; 
        i++; 
       } 
      } 
      if (niv > 0 && i != md_buf.length) { 
       for (;;) { 
        if (niv == 0) 
         break; 
        if (i == md_buf.length) 
         break; 
        iv[iv_ix++] = md_buf[i]; 
        niv--; 
        i++; 
       } 
      } 
      if (nkey == 0 && niv == 0) { 
       break; 
      } 
     } 
     for (i = 0; i < md_buf.length; i++) { 
      md_buf[i] = 0; 
     } 
     return both; 
    } 


    public static void main(String[] args) { 
     try { 
      // --- read base 64 encoded file --- 

      File f = new File(args[ARG_INDEX_FILENAME]); 
      List<String> lines = Files.readAllLines(f.toPath(), ASCII); 
      StringBuilder sb = new StringBuilder(); 
      for (String line : lines) { 
       sb.append(line.trim()); 
      } 
      String dataBase64 = sb.toString(); 
      byte[] headerSaltAndCipherText = Base64.decode(dataBase64); 

      // --- extract salt & encrypted --- 

      // header is "Salted__", ASCII encoded, if salt is being used (the default) 
      byte[] salt = Arrays.copyOfRange(
        headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE); 
      byte[] encrypted = Arrays.copyOfRange(
        headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length); 

      // --- specify cipher and digest for EVP_BytesToKey method --- 

      Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
      MessageDigest md5 = MessageDigest.getInstance("MD5"); 

      // --- create key and IV --- 

      // the IV is useless, OpenSSL might as well have use zero's 
      final byte[][] keyAndIV = EVP_BytesToKey(
        KEY_SIZE_BITS/Byte.SIZE, 
        aesCBC.getBlockSize(), 
        md5, 
        salt, 
        args[ARG_INDEX_PASSWORD].getBytes(ASCII), 
        ITERATIONS); 
      SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES"); 
      IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]); 

      // --- initialize cipher instance and decrypt --- 

      aesCBC.init(Cipher.DECRYPT_MODE, key, iv); 
      byte[] decrypted = aesCBC.doFinal(encrypted); 

      String answer = new String(decrypted, ASCII); 
      System.out.println(answer); 
     } catch (BadPaddingException e) { 
      // AKA "something went wrong" 
      throw new IllegalStateException(
        "Bad password, algorithm, mode or padding;" + 
        " no salt, wrong number of iterations or corrupted ciphertext."); 
     } catch (IllegalBlockSizeException e) { 
      throw new IllegalStateException(
        "Bad algorithm, mode or corrupted (resized) ciphertext."); 
     } catch (GeneralSecurityException e) { 
      throw new IllegalStateException(e); 
     } catch (IOException e) { 
      throw new IllegalStateException(e); 
     } 
    }   
} 

OpenSSL 1.1.0c changed the digest algorithm in einigen internen Komponenten verwendet. Früher wurde MD5 verwendet, und 1.1.0 wechselte zu SHA256. Seien Sie vorsichtig, die Änderung betrifft Sie nicht in EVP_BytesToKey und Befehle wie openssl enc.

+0

sollten sie sich nicht unterscheiden - das ist der ganze Sinn von Standards wie PKCS5. Ihr Ratschlag ist im Großen und Ganzen jedoch gut, – mfrankli

+1

"Vergessen Sie nicht, das [Tag: Zeichencodierung] anzupassen ..." Die Verschlüsselung sollte in der Tat übereinstimmen, aber die Codierung kann abweichen. –

+0

Das hat bei mir funktioniert. Und vorhersehbar hatte ich Probleme mit ASCII vs UTF8. –

2

Unten sind OpenSSLPBEInputStream und OpenSSLPBEOutputStream die verwendet werden können, um beliebige Ströme von Bytes in einer Weise zu verschlüsseln/entschlüsseln, die mit OpenSSL kompatibel ist.

Beispiel Nutzung:

// The original clear text bytes 
    byte[] originalBytes = ... 

    // Encrypt these bytes 
    char[] pwd = "thePassword".toCharArray(); 
    ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); 
    OpenSSLPBEOutputStream encOS = new OpenSSLPBEOutputStream(byteOS, ALGORITHM, 1, pwd); 
    encOS.write(originalBytes); 
    encOS.flush(); 
    byte[] encryptedBytes = byteOS.toByteArray(); 

    // Decrypt the bytes 
    ByteArrayInputStream byteIS = new ByteArrayInputStream(encryptedBytes); 
    OpenSSLPBEInputStream encIS = new OpenSSLPBEInputStream(byteIS, ALGORITHM, 1, pwd); 

Wo Algorithm (mit nur Klassen JDK) kann sein: "PBEWithMD5AndDES", "PBEWithMD5AndTripleDES", "PBEWithSHA1AndDESede", "PBEWithSHA1AndRC2_40".

Um "openssl aes-256-cbc -a-salz -in password.txt -out password.txt.enc" des ursprünglichen Posters zu behandeln, fügen Sie bouncey castle dem Klassenpfad hinzu und verwenden Sie algorthm = "PBEWITHMD5AND256BITAES-CBC -OpenSL ".

/* Add BC provider, and fail fast if BC provider is not in classpath for some reason */ 
Security.addProvider(new BouncyCastleProvider()); 

Die Abhängigkeit:

<dependency> 
     <groupId>org.bouncycastle</groupId> 
     <artifactId>bcprov-jdk16</artifactId> 
     <version>1.44</version> 
    </dependency> 

Eingabestrom:

import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 
import java.io.IOException; 
import java.io.InputStream; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.spec.InvalidKeySpecException; 

public class OpenSSLPBEInputStream extends InputStream { 

    private final static int READ_BLOCK_SIZE = 64 * 1024; 

    private final Cipher cipher; 
    private final InputStream inStream; 
    private final byte[] bufferCipher = new byte[READ_BLOCK_SIZE]; 

    private byte[] bufferClear = null; 

    private int index = Integer.MAX_VALUE; 
    private int maxIndex = 0; 

    public OpenSSLPBEInputStream(final InputStream streamIn, String algIn, int iterationCount, char[] password) 
      throws IOException { 
     this.inStream = streamIn; 
     try { 
      byte[] salt = readSalt(); 
      cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.DECRYPT_MODE, algIn, iterationCount); 
     } catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) { 
      throw new IOException(e); 
     } 
    } 

    @Override 
    public int available() throws IOException { 
     return inStream.available(); 
    } 

    @Override 
    public int read() throws IOException { 

     if (index > maxIndex) { 
      index = 0; 
      int read = inStream.read(bufferCipher); 
      if (read != -1) { 
       bufferClear = cipher.update(bufferCipher, 0, read); 
      } 
      if (read == -1 || bufferClear == null || bufferClear.length == 0) { 
       try { 
        bufferClear = cipher.doFinal(); 
       } catch (IllegalBlockSizeException | BadPaddingException e) { 
        bufferClear = null; 
       } 
      } 
      if (bufferClear == null || bufferClear.length == 0) { 
       return -1; 
      } 
      maxIndex = bufferClear.length - 1; 
     } 
     return bufferClear[index++] & 0xff; 

    } 

    private byte[] readSalt() throws IOException { 

     byte[] headerBytes = new byte[OpenSSLPBECommon.OPENSSL_HEADER_STRING.length()]; 
     inStream.read(headerBytes); 
     String headerString = new String(headerBytes, OpenSSLPBECommon.OPENSSL_HEADER_ENCODE); 

     if (!OpenSSLPBECommon.OPENSSL_HEADER_STRING.equals(headerString)) { 
      throw new IOException("unexpected file header " + headerString); 
     } 

     byte[] salt = new byte[OpenSSLPBECommon.SALT_SIZE_BYTES]; 
     inStream.read(salt); 

     return salt; 
    } 

} 

Der Ausgabestrom:

import javax.crypto.BadPaddingException; 
import javax.crypto.Cipher; 
import javax.crypto.IllegalBlockSizeException; 
import javax.crypto.NoSuchPaddingException; 
import java.io.IOException; 
import java.io.OutputStream; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.SecureRandom; 
import java.security.spec.InvalidKeySpecException; 

public class OpenSSLPBEOutputStream extends OutputStream { 

private static final int BUFFER_SIZE = 5 * 1024 * 1024; 

private final Cipher cipher; 
private final OutputStream outStream; 
private final byte[] buffer = new byte[BUFFER_SIZE]; 
private int bufferIndex = 0; 

public OpenSSLPBEOutputStream(final OutputStream outputStream, String algIn, int iterationCount, 
           char[] password) throws IOException { 
    outStream = outputStream; 
    try { 
     /* Create and use a random SALT for each instance of this output stream. */ 
     byte[] salt = new byte[PBECommon.SALT_SIZE_BYTES]; 
     new SecureRandom().nextBytes(salt); 
     cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.ENCRYPT_MODE, algIn, iterationCount); 
     /* Write header */ 
     writeHeader(salt); 
    } catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) { 
     throw new IOException(e); 
    } 
} 

@Override 
public void write(int b) throws IOException { 
    buffer[bufferIndex] = (byte) b; 
    bufferIndex++; 
    if (bufferIndex == BUFFER_SIZE) { 
     byte[] result = cipher.update(buffer, 0, bufferIndex); 
     outStream.write(result); 
     bufferIndex = 0; 
    } 
} 

@Override 
public void flush() throws IOException { 
    if (bufferIndex > 0) { 
     byte[] result; 
     try { 
      result = cipher.doFinal(buffer, 0, bufferIndex); 
      outStream.write(result); 
     } catch (IllegalBlockSizeException | BadPaddingException e) { 
      throw new IOException(e); 
     } 
     bufferIndex = 0; 
    } 
} 

@Override 
public void close() throws IOException { 
    flush(); 
    outStream.close(); 
} 

private void writeHeader(byte[] salt) throws IOException { 
    outStream.write(OpenSSLPBECommon.OPENSSL_HEADER_STRING.getBytes(OpenSSLPBECommon.OPENSSL_HEADER_ENCODE)); 
    outStream.write(salt); 
} 

} 

Kleine gemeinsame Klasse:

0.123.
import javax.crypto.Cipher; 
import javax.crypto.NoSuchPaddingException; 
import javax.crypto.SecretKey; 
import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.PBEKeySpec; 
import javax.crypto.spec.PBEParameterSpec; 
import java.security.InvalidAlgorithmParameterException; 
import java.security.InvalidKeyException; 
import java.security.NoSuchAlgorithmException; 
import java.security.spec.InvalidKeySpecException; 

class OpenSSLPBECommon { 

protected static final int SALT_SIZE_BYTES = 8; 
protected static final String OPENSSL_HEADER_STRING = "Salted__"; 
protected static final String OPENSSL_HEADER_ENCODE = "ASCII"; 

protected static Cipher initializeCipher(char[] password, byte[] salt, int cipherMode, 
             final String algorithm, int iterationCount) throws NoSuchAlgorithmException, InvalidKeySpecException, 
     InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException { 

    PBEKeySpec keySpec = new PBEKeySpec(password); 
    SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm); 
    SecretKey key = factory.generateSecret(keySpec); 

    Cipher cipher = Cipher.getInstance(algorithm); 
    cipher.init(cipherMode, key, new PBEParameterSpec(salt, iterationCount)); 

    return cipher; 
} 

} 
-1

Verwenden Sie nicht Ase-128-CBC, verwenden Sie Ase-128-ecb.

nehmen nur die ersten 16 Bytes als Schlüssel, weil Schlüssel 128 Bit ist

Hash-Ausgabe in hex gedruckt wird, die alle 2 Zeichen stellt einen Bytewert

hashpwd = echo -n $password| openssl sha1 | sed 's#.*=\\s*##g' | cut -c 1-32

Openssl enc -AES -128-ECB -salt -in -out -K $ hashpwd

Java-Code ist hier:

import sun.misc.BASE64Decoder; 
import sun.misc.BASE64Encoder; 

import javax.crypto.Cipher; 
import javax.crypto.spec.SecretKeySpec; 
import java.io.*; 
import java.security.MessageDigest; 
import java.security.NoSuchAlgorithmException; 
import java.util.ArrayList; 
import java.util.Arrays; 


    //openssl enc -nosalt -aes-128-ecb 
    // -in <input file> 
    // -out <output file> 
    // -K <16 bytes in hex, for example : "abc" can be hashed in SHA-1, the first 16 bytes in hex is a9993e364706816aba3e25717850c26c> 
    private final static String TRANSFORMATION = "AES"; // use aes-128-ecb in openssl 

public static byte[] encrypt(String passcode, byte[] data) throws CryptographicException { 
     try { 
      Cipher cipher = Cipher.getInstance(TRANSFORMATION); 
      cipher.init(Cipher.ENCRYPT_MODE, genKeySpec(passcode)); 
      return cipher.doFinal(data); 
     } catch (Exception ex) { 
      throw new CryptographicException("Error encrypting", ex); 
     } 
    } 


    public static String encryptWithBase64(String passcode, byte[] data) throws CryptographicException { 
     return new BASE64Encoder().encode(encrypt(passcode, data)); 
    } 

    public static byte[] decrypt(String passcode, byte[] data) throws CryptographicException { 
     try { 
      Cipher dcipher = Cipher.getInstance(TRANSFORMATION); 
      dcipher.init(Cipher.DECRYPT_MODE, genKeySpec(passcode)); 
      return dcipher.doFinal(data); 
     } catch (Exception e) { 
      throw new CryptographicException("Error decrypting", e); 
     } 
    } 


    public static byte[] decryptWithBase64(String passcode, String encrptedStr) throws CryptographicException { 
     try { 
      return decrypt(passcode, new BASE64Decoder().decodeBuffer(encrptedStr)); 
     } catch (Exception e) { 
      throw new CryptographicException("Error decrypting", e); 
     } 
    } 

    public static SecretKeySpec genKeySpec(String passcode) throws UnsupportedEncodingException, NoSuchAlgorithmException { 
     byte[] key = passcode.getBytes("UTF-8"); 
     MessageDigest sha = MessageDigest.getInstance("SHA-1"); 
     key = sha.digest(key); 
     key = Arrays.copyOf(key, 16); // use only first 128 bit 
     return new SecretKeySpec(key, TRANSFORMATION); 
    } 

Getestet und in jdk6 und jdk8 übergeben.

+0

Verwenden Sie nicht den ECB-Modus, es ist nicht sicher, siehe [ECB-Modus] (https: //en.wikipedia. org/wiki/Block_cipher_mode_of_operation # Electronic_Codebook_.28ECB.29), scrollen Sie nach unten zum Pinguin. Verwenden Sie stattdessen CBC-Modus mit einem zufälligen IV, nur die verschlüsselten Daten mit dem IV für die Verwendung in der Entschlüsselung, es muss nicht geheim sein. – zaph

Verwandte Themen