java - طريقة - شرح خوارزمية التشفير rsa



جافا 256 بت بتشفير AES كلمة المرور (6)

فكر في استخدام وحدة تشفير أمان الربيع

توفر وحدة Spring Security Crypto دعمًا للتشفير المتناظر وتوليد المفتاح وترميز كلمة المرور. يتم توزيع التعليمة البرمجية كجزء من الوحدة الأساسية ولكن لا تعتمد على أي رمز Spring (أو Spring) آخر.

يوفر تجريدًا بسيطًا للتشفير ويبدو أنه يتطابق مع ما هو مطلوب هنا ،

إن أسلوب التشفير "القياسي" هو 256-bit AES باستخدام PBKDF2 PKCS # 5 (دالة اشتقاق مفتاح المستندة إلى كلمة المرور # 2). تتطلب هذه الطريقة Java 6. يجب حفظ كلمة المرور المستخدمة لإنشاء SecretKey في مكان آمن وعدم مشاركتها. يتم استخدام الملح لمنع هجمات القاموس ضد المفتاح في حالة اختراق بياناتك المشفرة. كما يتم تطبيق متجه تهيئة عشوائية 16 بايت بحيث تكون كل رسالة مشفرة فريدة.

نظرة إلى internals تكشف عن بنية مشابهة لإجابة إيريكسون .

كما هو مذكور في السؤال ، يتطلب هذا أيضًا سياسة InvalidKeyException: Illegal Key Size التشفير (JCE) (وإلا ستواجه InvalidKeyException: Illegal Key Size ). إنه قابل للتحميل لـ Java 6 و Java 7 و Java 8 .

استخدام المثال

import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;

public class CryptoExample {
    public static void main(String[] args) {
        final String password = "I AM SHERLOCKED";  
        final String salt = KeyGenerators.string().generateKey();

        TextEncryptor encryptor = Encryptors.text(password, salt);      
        System.out.println("Salt: \"" + salt + "\"");

        String textToEncrypt = "*royal secrets*";
        System.out.println("Original text: \"" + textToEncrypt + "\"");

        String encryptedText = encryptor.encrypt(textToEncrypt);
        System.out.println("Encrypted text: \"" + encryptedText + "\"");

        // Could reuse encryptor but wanted to show reconstructing TextEncryptor
        TextEncryptor decryptor = Encryptors.text(password, salt);
        String decryptedText = decryptor.decrypt(encryptedText);
        System.out.println("Decrypted text: \"" + decryptedText + "\"");

        if(textToEncrypt.equals(decryptedText)) {
            System.out.println("Success: decrypted text matches");
        } else {
            System.out.println("Failed: decrypted text does not match");
        }       
    }
}

وناتج العينة ،

Salt: "feacbc02a3a697b0"
Original text: "*royal secrets*"
Encrypted text: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 
Decrypted text: "*royal secrets*"
Success: decrypted text matches

أحتاج إلى تنفيذ تشفير 256 بت AES ، ولكن كل الأمثلة التي وجدت على الإنترنت تستخدم "KeyGenerator" لإنشاء مفتاح 256 بت ، لكني أرغب في استخدام مفتاح المرور الخاص بي. كيف يمكنني إنشاء مفتاحي الخاص؟ لقد حاولت تعيينها إلى 256 بت ، ولكن بعد ذلك ظهرت رسالة خطأ تفيد بأن المفتاح طويل جدًا. لدي التصحيح القضائي غير محدود مثبتة ، لذلك هذا ليس مشكلة :)

أي. يبدو KeyGenerator مثل هذا ...

// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available

// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();

قانون مأخوذ من هنا

تصحيح

كنت في الواقع الحشو كلمة المرور إلى 256 بايت ، وليس البتات ، وهو طويل جدا. ما يلي هو بعض التعليمات البرمجية التي أستخدمها الآن ولدي بعض الخبرة في هذا.

byte[] key = null; // TODO
byte[] input = null; // TODO
byte[] output = null;
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
output = cipher.doFinal(input)

بت "TODO" تحتاج إلى القيام بنفسك :-)


Answer #1

إضافة إلى تعديلات Wufoo @ ، يستخدم الإصدار التالي InputStreams بدلاً من الملفات لجعل العمل مع مجموعة متنوعة من الملفات أسهل. كما يقوم بتخزين IV والملح في بداية الملف ، مما يجعل من كلمة المرور التي يجب تتبعها. بما أن الرابع والملح لا يحتاجان لأن يكونا سراً ، فهذا يجعل الحياة أسهل قليلاً.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {
    public final static int SALT_LEN     = 8;
    static final String     HEXES        = "0123456789ABCDEF";
    String                  mPassword    = null;
    byte[]                  mInitVec     = null;
    byte[]                  mSalt        = new byte[SALT_LEN];
    Cipher                  mEcipher     = null;
    Cipher                  mDecipher    = null;
    private final int       KEYLEN_BITS  = 128;    // see notes below where this is used.
    private final int       ITERATIONS   = 65536;
    private final int       MAX_FILE_BUF = 1024;

    /**
     * create an object with just the passphrase from the user. Don't do anything else yet
     * @param password
     */
    public AES(String password) {
        mPassword = password;
    }

    public static String byteToHex(byte[] raw) {
        if (raw == null) {
            return null;
        }

        final StringBuilder hex = new StringBuilder(2 * raw.length);

        for (final byte b : raw) {
            hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
        }

        return hex.toString();
    }

    public static byte[] hexToByte(String hexString) {
        int    len = hexString.length();
        byte[] ba  = new byte[len / 2];

        for (int i = 0; i < len; i += 2) {
            ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                                + Character.digit(hexString.charAt(i + 1), 16));
        }

        return ba;
    }

    /**
     * debug/print messages
     * @param msg
     */
    private void Db(String msg) {
        System.out.println("** Crypt ** " + msg);
    }

    /**
     * This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
     * Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
     *
     * there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
     * into uncertain problems with that.
     *
     * @param input - the cleartext file to be encrypted
     * @param output - the encrypted data file
     * @throws IOException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     */
    public void WriteEncryptedFile(InputStream inputStream, OutputStream outputStream)
            throws IOException, IllegalBlockSizeException, BadPaddingException {
        try {
            long             totalread = 0;
            int              nread     = 0;
            byte[]           inbuf     = new byte[MAX_FILE_BUF];
            SecretKeyFactory factory   = null;
            SecretKey        tmp       = null;

            // crate secureRandom salt and store  as member var for later use
            mSalt = new byte[SALT_LEN];

            SecureRandom rnd = new SecureRandom();

            rnd.nextBytes(mSalt);
            Db("generated salt :" + byteToHex(mSalt));
            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

            /*
             *  Derive the key, given password and salt.
             *
             * in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
             * The end user must also install them (not compiled in) so beware.
             * see here:  http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
             */
            KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

            tmp = factory.generateSecret(spec);

            SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

            /*
             *  Create the Encryption cipher object and store as a member variable
             */
            mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            mEcipher.init(Cipher.ENCRYPT_MODE, secret);

            AlgorithmParameters params = mEcipher.getParameters();

            // get the initialization vectory and store as member var
            mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
            Db("mInitVec is :" + byteToHex(mInitVec));
            outputStream.write(mSalt);
            outputStream.write(mInitVec);

            while ((nread = inputStream.read(inbuf)) > 0) {
                Db("read " + nread + " bytes");
                totalread += nread;

                // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
                // and results in full blocks of MAX_FILE_BUF being written.
                byte[] trimbuf = new byte[nread];

                for (int i = 0; i < nread; i++) {
                    trimbuf[i] = inbuf[i];
                }

                // encrypt the buffer using the cipher obtained previosly
                byte[] tmpBuf = mEcipher.update(trimbuf);

                // I don't think this should happen, but just in case..
                if (tmpBuf != null) {
                    outputStream.write(tmpBuf);
                }
            }

            // finalize the encryption since we've done it in blocks of MAX_FILE_BUF
            byte[] finalbuf = mEcipher.doFinal();

            if (finalbuf != null) {
                outputStream.write(finalbuf);
            }

            outputStream.flush();
            inputStream.close();
            outputStream.close();
            outputStream.close();
            Db("wrote " + totalread + " encrypted bytes");
        } catch (InvalidKeyException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidParameterSpecException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeySpecException ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
     * to disk as (output) File.
     *
     * I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
     *  and still have a correctly decrypted file in the end. Seems to work so left it in.
     *
     * @param input - File object representing encrypted data on disk
     * @param output - File object of cleartext data to write out after decrypting
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public void ReadEncryptedFile(InputStream inputStream, OutputStream outputStream)
            throws IllegalBlockSizeException, BadPaddingException, IOException {
        try {
            CipherInputStream cin;
            long              totalread = 0;
            int               nread     = 0;
            byte[]            inbuf     = new byte[MAX_FILE_BUF];

            // Read the Salt
            inputStream.read(this.mSalt);
            Db("generated salt :" + byteToHex(mSalt));

            SecretKeyFactory factory = null;
            SecretKey        tmp     = null;
            SecretKey        secret  = null;

            factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

            KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

            tmp    = factory.generateSecret(spec);
            secret = new SecretKeySpec(tmp.getEncoded(), "AES");

            /* Decrypt the message, given derived key and initialization vector. */
            mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            // Set the appropriate size for mInitVec by Generating a New One
            AlgorithmParameters params = mDecipher.getParameters();

            mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();

            // Read the old IV from the file to mInitVec now that size is set.
            inputStream.read(this.mInitVec);
            Db("mInitVec is :" + byteToHex(mInitVec));
            mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));

            // creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
            cin = new CipherInputStream(inputStream, mDecipher);

            while ((nread = cin.read(inbuf)) > 0) {
                Db("read " + nread + " bytes");
                totalread += nread;

                // create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
                byte[] trimbuf = new byte[nread];

                for (int i = 0; i < nread; i++) {
                    trimbuf[i] = inbuf[i];
                }

                // write out the size-adjusted buffer
                outputStream.write(trimbuf);
            }

            outputStream.flush();
            cin.close();
            inputStream.close();
            outputStream.close();
            Db("wrote " + totalread + " encrypted bytes");
        } catch (Exception ex) {
            Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * adding main() for usage demonstration. With member vars, some of the locals would not be needed
     */
    public static void main(String[] args) {

        // create the input.txt file in the current directory before continuing
        File   input   = new File("input.txt");
        File   eoutput = new File("encrypted.aes");
        File   doutput = new File("decrypted.txt");
        String iv      = null;
        String salt    = null;
        AES    en      = new AES("mypassword");

        /*
         * write out encrypted file
         */
        try {
            en.WriteEncryptedFile(new FileInputStream(input), new FileOutputStream(eoutput));
            System.out.printf("File encrypted to " + eoutput.getName() + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
        } catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }

        /*
         * decrypt file
         */
        AES dc = new AES("mypassword");

        /*
         * write out decrypted file
         */
        try {
            dc.ReadEncryptedFile(new FileInputStream(eoutput), new FileOutputStream(doutput));
            System.out.println("decryption finished to " + doutput.getName());
        } catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
            e.printStackTrace();
        }
    }
}

Answer #2

لقد قمت بتطبيق إجابة إريكسون في فصل بسيط حقًا:
Java AES 256-bit Encryption / Decryption class

إذا حصلت على java.security.InvalidKeyException يجب عليك تثبيت ملفات سياسة الاختصاص اللامحدودة java.security.InvalidKeyException التشفير (JCE) في جافا:

ما عليك سوى وضع الجرار في {JDK HOME}\jre\lib\security


Answer #3

ما قمت به في الماضي هو تجزئة المفتاح عبر شيء مثل SHA256 ، ثم استخراج البايتات من التجزئة إلى بايت المفتاح [].

بعد أن يكون لديك بايت [] ، يمكنك ببساطة القيام بما يلي:

SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());

Answer #4

من السهل إنشاء مفتاحك الخاص من صفيف بايت:

byte[] raw = ...; // 32 bytes in size for a 256 bit key
Key skey = new javax.crypto.spec.SecretKeySpec(raw, "AES");

لكن إنشاء مفتاح 256 بت لا يكفي. إذا لم يتمكن مولد المفتاح من إنشاء مفاتيح 256 بت لك ، فإن فئة Cipher الأرجح لا تدعم AES 256-بت إما. أنت تقول أن لديك تصحيح الاختصاص اللامحدود مثبتًا ، لذا يجب دعم تشفير AES-256 (ولكن يجب أن تكون مفاتيح 256 بت أيضًا ، لذا قد تكون هذه مشكلة في التكوين).

Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
byte[] encrypted = cipher.doFinal(plainText.getBytes());

هناك حل بديل لعدم وجود دعم AES-256 يتمثل في تنفيذ بعض التطبيقات المجانية المتاحة لـ AES-256 ، واستخدامها كموفر مخصص. يتضمن ذلك إنشاء الفئة الفرعية الخاصة Cipher.getInstance(String, Provider) واستخدامها مع Cipher.getInstance(String, Provider) . ولكن هذا يمكن أن يكون عملية معنية.


Answer #5

Consider using Encryptor4j

First make sure you have Unlimited Strength Jurisdiction Policy files installed before your proceed so that you can use 256-bit AES keys.

Then do the following:

String password = "mysupersecretpassword"; 
Key key = KeyFactory.AES.keyFromPassword(password.toCharArray());
Encryptor encryptor = new Encryptor(key, "AES/CBC/PKCS7Padding", 16);

You can now use the encryptor to encrypt your message. You can also perform streaming encryption if you'd like. It automatically generates and prepends a secure IV for your convenience.

If it's a file that you wish to compress take a look at this answer Encrypting a large file with AES using JAVA for an even simpler approach.





aes