Android'de AES şifrelemesini kullanmak için en iyi uygulamalar nelerdir?


90

Neden bu soruyu soruyorum:

Android için bile AES şifrelemesiyle ilgili birçok soru olduğunu biliyorum. Web'de arama yaparsanız çok sayıda kod parçacığı vardır. Ancak her sayfada, her Stack Overflow sorusunda, büyük farklılıklara sahip başka bir uygulama buluyorum.

Bu yüzden bir "en iyi uygulama" bulmak için bu soruyu oluşturdum. Umarım en önemli gereksinimlerin bir listesini toplayabilir ve gerçekten güvenli bir uygulama oluşturabiliriz!

Başlatma vektörlerini ve tuzlarını okudum. Bulduğum tüm uygulamalar bu özelliklere sahip değildi. Yani buna ihtiyacın var mı? Güvenliği çok artırıyor mu? Nasıl uyguluyorsunuz? Şifrelenmiş verilerin şifresi çözülemiyorsa algoritma istisnalar yaratmalı mı? Yoksa bu güvensiz ve okunamayan bir dizge mi döndürmeli? Algoritma SHA yerine Bcrypt kullanabilir mi?

Bulduğum bu iki uygulama ne olacak? İyiler mi? Mükemmel mi yoksa bazı önemli şeyler eksik mi? Bunlardan hangisi güvenli?

Algoritma, şifreleme için bir dizi ve bir "şifre" almalı ve ardından dizeyi bu şifre ile şifrelemelidir. Çıktı yine bir dize (hex veya base64?) Olmalıdır. Elbette şifre çözme de mümkün olmalıdır.

Android için mükemmel AES uygulaması nedir?

Uygulama 1:

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
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 AdvancedCrypto implements ICrypto {

        public static final String PROVIDER = "BC";
        public static final int SALT_LENGTH = 20;
        public static final int IV_LENGTH = 16;
        public static final int PBE_ITERATION_COUNT = 100;

        private static final String RANDOM_ALGORITHM = "SHA1PRNG";
        private static final String HASH_ALGORITHM = "SHA-512";
        private static final String PBE_ALGORITHM = "PBEWithSHA256And256BitAES-CBC-BC";
        private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
        private static final String SECRET_KEY_ALGORITHM = "AES";

        public String encrypt(SecretKey secret, String cleartext) throws CryptoException {
                try {

                        byte[] iv = generateIv();
                        String ivHex = HexEncoder.toHex(iv);
                        IvParameterSpec ivspec = new IvParameterSpec(iv);

                        Cipher encryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
                        encryptionCipher.init(Cipher.ENCRYPT_MODE, secret, ivspec);
                        byte[] encryptedText = encryptionCipher.doFinal(cleartext.getBytes("UTF-8"));
                        String encryptedHex = HexEncoder.toHex(encryptedText);

                        return ivHex + encryptedHex;

                } catch (Exception e) {
                        throw new CryptoException("Unable to encrypt", e);
                }
        }

        public String decrypt(SecretKey secret, String encrypted) throws CryptoException {
                try {
                        Cipher decryptionCipher = Cipher.getInstance(CIPHER_ALGORITHM, PROVIDER);
                        String ivHex = encrypted.substring(0, IV_LENGTH * 2);
                        String encryptedHex = encrypted.substring(IV_LENGTH * 2);
                        IvParameterSpec ivspec = new IvParameterSpec(HexEncoder.toByte(ivHex));
                        decryptionCipher.init(Cipher.DECRYPT_MODE, secret, ivspec);
                        byte[] decryptedText = decryptionCipher.doFinal(HexEncoder.toByte(encryptedHex));
                        String decrypted = new String(decryptedText, "UTF-8");
                        return decrypted;
                } catch (Exception e) {
                        throw new CryptoException("Unable to decrypt", e);
                }
        }

        public SecretKey getSecretKey(String password, String salt) throws CryptoException {
                try {
                        PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), HexEncoder.toByte(salt), PBE_ITERATION_COUNT, 256);
                        SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGORITHM, PROVIDER);
                        SecretKey tmp = factory.generateSecret(pbeKeySpec);
                        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), SECRET_KEY_ALGORITHM);
                        return secret;
                } catch (Exception e) {
                        throw new CryptoException("Unable to get secret key", e);
                }
        }

        public String getHash(String password, String salt) throws CryptoException {
                try {
                        String input = password + salt;
                        MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM, PROVIDER);
                        byte[] out = md.digest(input.getBytes("UTF-8"));
                        return HexEncoder.toHex(out);
                } catch (Exception e) {
                        throw new CryptoException("Unable to get hash", e);
                }
        }

        public String generateSalt() throws CryptoException {
                try {
                        SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
                        byte[] salt = new byte[SALT_LENGTH];
                        random.nextBytes(salt);
                        String saltHex = HexEncoder.toHex(salt);
                        return saltHex;
                } catch (Exception e) {
                        throw new CryptoException("Unable to generate salt", e);
                }
        }

        private byte[] generateIv() throws NoSuchAlgorithmException, NoSuchProviderException {
                SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
                byte[] iv = new byte[IV_LENGTH];
                random.nextBytes(iv);
                return iv;
        }

}

Kaynak: http://pocket-for-android.1047292.n5.nabble.com/Encryption-method-and-reading-the-Dropbox-backup-td4344194.html

Uygulama 2:

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Usage:
 * <pre>
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
 * ...
 * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
 * </pre>
 * @author ferenc.hechler
 */
public class SimpleCrypto {

    public static String encrypt(String seed, String cleartext) throws Exception {
        byte[] rawKey = getRawKey(seed.getBytes());
        byte[] result = encrypt(rawKey, cleartext.getBytes());
        return toHex(result);
    }

    public static String decrypt(String seed, String encrypted) throws Exception {
        byte[] rawKey = getRawKey(seed.getBytes());
        byte[] enc = toByte(encrypted);
        byte[] result = decrypt(rawKey, enc);
        return new String(result);
    }

    private static byte[] getRawKey(byte[] seed) throws Exception {
        KeyGenerator kgen = KeyGenerator.getInstance("AES");
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        sr.setSeed(seed);
        kgen.init(128, sr); // 192 and 256 bits may not be available
        SecretKey skey = kgen.generateKey();
        byte[] raw = skey.getEncoded();
        return raw;
    }


    private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(clear);
        return encrypted;
    }

    private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] decrypted = cipher.doFinal(encrypted);
        return decrypted;
    }

    public static String toHex(String txt) {
        return toHex(txt.getBytes());
    }
    public static String fromHex(String hex) {
        return new String(toByte(hex));
    }

    public static byte[] toByte(String hexString) {
        int len = hexString.length()/2;
        byte[] result = new byte[len];
        for (int i = 0; i < len; i++)
            result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
        return result;
    }

    public static String toHex(byte[] buf) {
        if (buf == null)
            return "";
        StringBuffer result = new StringBuffer(2*buf.length);
        for (int i = 0; i < buf.length; i++) {
            appendHex(result, buf[i]);
        }
        return result.toString();
    }
    private final static String HEX = "0123456789ABCDEF";
    private static void appendHex(StringBuffer sb, byte b) {
        sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
    }

}

Kaynak: http://www.tutorials-android.com/learn/How_to_encrypt_and_decrypt_strings.rhtml


Çözüm 1'i uygulamaya çalışıyorum ama bazı sınıflara ihtiyacı vardı. tam kaynak koduna sahip misin?
albanx

1
Hayır, yapmadım, üzgünüm. Ama sadece silerek çalışma var implements ICryptove değişen throws CryptoExceptionetmek throws Exceptionvb. Böylece artık bu derslere ihtiyacınız olmayacak.
37'de caw

Ama HexEncoder sınıfı da eksik mi? Onu nerede bulabilirim?
albanx

HexEncoder, BouncyCastle kütüphanesinin bir parçası sanırım. Sadece indirebilirsiniz. Veya "bayt [] onaltılık" için google'da ve tam tersini Java'da yapabilirsiniz.
05'te caw

Teşekkür ederim Marco. Ama 3 yöntem vardır fark getSecretKey, getHash, generateSaltkullanılmamış olan ilk uygulamada. Belki yanılıyorum ama bu sınıf pratikte bir dizgiyi şifrelemek için nasıl kullanılabilir?
albanx

Yanıtlar:


37

Sorunuzda verdiğiniz uygulama tam olarak doğru değildir ve verdiğiniz uygulama da olduğu gibi kullanılmamalıdır. Aşağıda, Android'de şifre tabanlı şifrelemenin özelliklerini tartışacağım.

Tuşlar ve Kısaltmalar

Şifre tabanlı sistemi tuzlarla tartışmaya başlayacağım. Tuz, rastgele oluşturulmuş bir sayıdır. "Çıkarılmış" değildir. Uygulama 1, generateSalt()kriptografik olarak güçlü bir rasgele sayı üreten bir yöntemi içerir . Tuz, güvenlik açısından önemli olduğundan, oluşturulduktan sonra gizli tutulmalıdır, ancak yalnızca bir kez oluşturulması gerekir. Bu bir Web sitesiyse, gizli tutulması nispeten kolaydır, ancak yüklü uygulamalar için (masaüstü ve mobil cihazlar için) bu çok daha zor olacaktır.

Yöntem getHash(), verilen parola ve tuzun karmasını tek bir dizede birleştirilmiş olarak döndürür. Kullanılan algoritma, 512 bitlik bir karma döndüren SHA-512'dir. Bu yöntem, bir dizenin bütünlüğünü kontrol etmek için yararlı olan bir karma değeri döndürür, bu nedenle getHash(), her iki parametreyi de birleştirdiği için yalnızca bir parola veya yalnızca bir tuzla çağrılarak da kullanılabilir . Bu yöntem parola tabanlı şifreleme sisteminde kullanılmayacağı için daha fazla tartışmayacağım.

Yöntem getSecretKey(), bir charparola dizisinden bir anahtar ve onaltılı olarak kodlanmış bir tuzdan döndürüldüğü gibi türetir generateSalt(). Kullanılan algoritma, karma işlevi olarak SHA-256 ile PKCS5'ten PBKDF1 (sanırım) ve 256 bitlik bir anahtar döndürüyor. brute-force saldırısı gerçekleştirmek için gereken süreyi arttırmak için getSecretKey()parola, tuz ve bir sayacın ( PBE_ITERATION_COUNTburada 100'de verilen yineleme sayısına kadar) tekrar tekrar oluşturarak bir anahtar üretir . Tuzun uzunluğu, en az üretilen anahtar kadar uzun, bu durumda en az 256 bit olmalıdır. Yineleme sayısı, makul olmayan bir gecikmeye neden olmadan mümkün olduğu kadar uzun ayarlanmalıdır. Anahtar türetmede tuzlar ve yineleme sayıları hakkında daha fazla bilgi için RFC2898'de bölüm 4'e bakın .

Bununla birlikte, Java'nın PBE'sindeki uygulama, parola Unicode karakterleri, yani temsil edilmesi için 8 bitten fazlasını gerektiren karakterleri içeriyorsa kusurludur. Belirtildiği gibi PBEKeySpec, "PKCS # 5'te tanımlanan PBE mekanizması her karakterin sadece düşük sıralı 8 bitine bakar". Bu soruna geçici bir çözüm bulmak için, parolayı geçirmeden önce paroladaki tüm 16 bitlik karakterlerden (yalnızca 8 bitlik karakterler içerecek) bir onaltılık dize oluşturmayı deneyebilirsiniz PBEKeySpec. Örneğin, "ABC", "004100420043" olur. Ayrıca PBEKeySpec'in "şifreyi bir karakter dizisi olarak talep ettiğini, böylece clearPassword()bittiğinde [ile ] üzerine yazılabileceğini " unutmayın. ("Bellekteki dizeleri koruma" ile ilgili olarak, bu soruya bakın .) Yine de herhangi bir sorun görmüyorum,

Şifreleme

Bir anahtar oluşturulduktan sonra, metni şifrelemek ve şifresini çözmek için kullanabiliriz.

Uygulama l'de, kullanılan şifreleme algoritması AES/CBC/PKCS5Padding, yani PKCS # 5'te tanımlanan doldurma ile Şifreleme Bloğu Zincirleme (CBC) şifreleme modunda AES'dir. (Diğer AES şifre modları arasında sayaç modu (CTR), elektronik kod kitabı modu (ECB) ve Galois sayaç modu (GCM) bulunur. Stack Overflow'daki bir başka soru , çeşitli AES şifre modlarını ve kullanılması önerilenleri ayrıntılı olarak tartışan cevapları içerir. Ayrıca, CBC modu şifrelemesine yönelik, bazıları RFC 7457'de bahsedilmiş olan birkaç saldırı olduğunu da unutmayın.)

Şifrelenmiş verileri bütünlük açısından da kontrol eden bir şifreleme modu kullanmanız gerektiğini unutmayın (örneğin, ilişkili verilerle doğrulanmış şifreleme , AEAD, RFC 5116'da açıklanan). Ancak, AES/CBC/PKCS5Paddingbütünlük denetimi sağlamaz, bu nedenle tek başına tavsiye edilmez . AEAD amaçları için, ilgili anahtar saldırılarını önlemek için normal bir şifreleme anahtarının en az iki katı uzunluğunda bir sır kullanılması önerilir: ilk yarı şifreleme anahtarı, ikinci yarı ise bütünlük kontrolü için anahtar görevi görür. (Yani, bu durumda, bir parola ve tuzdan tek bir sır oluşturun ve bu sırrı ikiye bölün.)

Java Uygulaması

Uygulama 1'deki çeşitli fonksiyonlar, algoritmaları için özel bir sağlayıcı, yani "BC" kullanır. Genel olarak, belirli sağlayıcıların talep edilmesi tavsiye edilmez, çünkü tüm Java uygulamalarında, destek eksikliği, kod tekrarını önlemek için veya başka nedenlerle tüm sağlayıcılar kullanılamaz. Bu tavsiye, 2018'in başlarında Android P önizlemesinin yayınlanmasından bu yana özellikle önemli hale geldi, çünkü burada "BC" sağlayıcısının bazı işlevleri kullanımdan kaldırıldı - Android Geliştiricileri Blogu'ndaki "Android P'deki Kriptografi Değişiklikleri" makalesine bakın. Oracle Sağlayıcılara Giriş bölümüne de bakın .

Bu nedenle, PROVIDERmevcut olmamalı ve dizeden -BCçıkarılmalıdır PBE_ALGORITHM. Uygulama 2 bu açıdan doğrudur.

Bir yöntemin tüm istisnaları yakalaması, sadece yapabildiği istisnaları ele alması uygun değildir. Sorunuzda verilen uygulamalar çeşitli kontrol edilmiş istisnalar atabilir. Bir yöntem, yalnızca kontrol edilen istisnaları CryptoException ile sarmayı veya throwsyan tümcede kontrol edilen istisnaları belirtmeyi seçebilir . Kolaylık sağlamak için, sınıfların atabileceği potansiyel olarak birçok denetlenmiş istisna olduğundan, orijinal istisnayı CryptoException ile sarmak uygun olabilir.

SecureRandom Android'de

Android Geliştiricileri Blogu'ndaki "Bazı SecureRandom Düşünceleri" başlıklı makalede ayrıntılı olarak açıklandığı gibi, java.security.SecureRandom2013'ten önceki Android sürümlerinde uygulanması, sunduğu rastgele sayıların gücünü azaltan bir kusura sahiptir. Bu kusur, bu makalede anlatıldığı gibi azaltılabilir.


Bu çift gizli nesil benim görüşüme göre biraz savurgan, üretilen sırrı kolayca ikiye bölebilir veya - yeterli bit yoksa - bir sayaç (ilk anahtar için 1, ikinci anahtar için 2) gizli ve tek bir hash gerçekleştirin. Tüm yinelemeleri iki kez gerçekleştirmenize gerek yok.
Maarten Bodewes

HMAC ve tuz hakkındaki bilgiler için teşekkürler. Bu sefer HMAC kullanmayacağım ama daha sonra çok faydalı olabilir. Ve genel olarak, bu hiç şüphesiz iyi bir şeydir.
gaga

Tüm düzenlemeler ve (şimdi) Java'da AES şifrelemesine harika giriş için çok teşekkür ederiz!
00

1
Olması gerekiyor. getInstanceyalnızca algoritmanın adını alan bir aşırı yüklemeye sahiptir. Örnek: Cipher.getInstance () Bouncy Castle dahil olmak üzere birçok sağlayıcı Java uygulamasına kaydedilebilir ve bu tür bir aşırı yükleme, verilen algoritmayı uygulayan biri için sağlayıcıların listesini arar. Denemelisin ve görmelisin.
Peter O.

1
Evet, sağlayıcıları Security.getProviders () tarafından verilen sırayla arayacaktır - ancak şimdi donanım destekli şifrelemeye izin veren init () çağrısı sırasında anahtarın bu sağlayıcı tarafından kabul edilip edilmediğini de kontrol edecektir. Daha fazla ayrıntı burada: docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/… .
Maarten Bodewes

18

# 2, şifre için yalnızca "AES" (metinde ECB modu şifreleme, büyük bir hayır-hayır anlamına gelir) kullandığından asla kullanılmamalıdır. Ben sadece 1 numara hakkında konuşacağım.

İlk uygulama, şifreleme için en iyi uygulamalara bağlı görünüyor. PBE gerçekleştirmek için hem tuz boyutu hem de yineleme sayısı kısa kenarda olmasına rağmen, sabitler genellikle uygundur. Dahası, PBE anahtar üretimi sabit kodlanmış değer olarak 256 kullandığından AES-256 için gibi görünüyor (tüm bu sabitlerden sonra bir utanç). En azından beklediğiniz gibi CBC ve PKCS5Padding kullanır.

Herhangi bir kimlik doğrulama / bütünlük koruması tamamen eksiktir, bu nedenle bir saldırgan şifre metnini değiştirebilir. Bu, bir istemci / sunucu modelinde dolgu oracle saldırılarının mümkün olduğu anlamına gelir. Ayrıca, bir saldırganın şifrelenmiş verileri deneyip değiştirebileceği anlamına gelir. Bu, dolgunun veya içeriğin uygulama tarafından kabul edilmemesi nedeniyle bir yerde bazı hatalara yol açacaktır, ancak bu, içinde olmak istediğiniz bir durum değildir.

İstisna işleme ve giriş doğrulaması geliştirilebilir, İstisnayı yakalamak kitabımda her zaman yanlıştır. Dahası, sınıf bilmediğim ICrypt'i uyguluyor. Bir sınıfta yalnızca yan etkileri olmayan yöntemlere sahip olmanın biraz tuhaf olduğunu biliyorum. Normalde bunları statik yaparsınız. Cipher örneklerinin arabelleğe alınması vb. Yoktur, bu nedenle gerekli her nesne ara sıra oluşturulur. Bununla birlikte, ICrypto'yu göründüğü tanımdan güvenle kaldırabilirsiniz, bu durumda kodu statik yöntemlere yeniden düzenleyebilirsiniz (veya seçiminize göre daha nesne yönelimli olacak şekilde yeniden yazabilirsiniz).

Sorun, herhangi bir paketleyicinin her zaman kullanım durumu hakkında varsayımlarda bulunmasıdır. Bir ambalajın doğru veya yanlış olduğunu söylemek bu nedenle saçmadır. Bu yüzden her zaman sarmalayıcı sınıfları oluşturmaktan kaçınmaya çalışıyorum. Ama en azından açıkça yanlış görünmüyor.


Bu detaylı cevap için çok teşekkür ederim! Bunun utanç verici olduğunu biliyorum ama henüz kod inceleme bölümünü bilmiyordum: D Bu ipucu için teşekkürler, bunu kontrol edeceğim. Ancak bu soru, sadece bu kod parçalarının gözden geçirilmesini istemediğim için bence buraya da uyuyor. Bunun yerine, Android'de AES şifrelemesini uygularken hangi yönlerin önemli olduğunu sormak istiyorum. Ve yine haklısınız, bu kod parçası AES-256 içindir. Yani bunun genel olarak güvenli bir AES-256 uygulaması olduğunu söyleyebilir misiniz? Kullanım durumu, yalnızca metin bilgilerini bir veritabanında güvenli bir şekilde saklamak istememdir.
2011'de

1
İyi görünüyor, ancak bütünlük kontrollerine ve kimlik doğrulamasına sahip olmama fikri beni rahatsız ederdi. Yeterli alanınız varsa, şifreli metnin üzerine bir HMAC eklemeyi ciddi olarak düşünürdüm. Bununla birlikte, muhtemelen sadece gizlilik eklemeye çalıştığınız için, bunu büyük bir artı olarak görüyorum, ancak doğrudan bir gereklilik değil.
Maarten Bodewes

Ancak niyet sadece başkalarının şifrelenmiş bilgilere erişmemesi ise, bir HMAC'a ihtiyacım yok, değil mi? Şifreli metni değiştirirler ve şifre çözmenin "yanlış" sonucunu zorlarlarsa, gerçek bir sorun yok, değil mi?
gak

Risk senaryonuzda bu yoksa, sorun değil. Şifre metnini değiştirdikten sonra sistem tarafından tekrarlanan bir şifre çözme işlemini bir şekilde tetikleyebilirlerse (doldurma oracle saldırısı), anahtarı bilmeden verilerin şifresini çözebilirler. Anahtarı olmayan bir sistemdeki verileri ele geçirirlerse bunu yapamazlar. Ancak bu nedenle, bir HMAC eklemek her zaman en iyi uygulamadır. Şahsen, AES-128 ve HMAC içeren bir sistemin AES-256'dan daha güvenli olduğunu düşünürdüm - ama söylendiği gibi muhtemelen gerekli değil.
Maarten Bodewes

1
Bütünlük istiyorsanız neden Galois / Counter-mode (AES-GCM) 'de AES kullanmıyorsunuz?
Kimvais

1

Oldukça ilginç bir soru sordunuz. Tüm algoritmalarda olduğu gibi, şifre anahtarı "gizli sos" dur, çünkü bir zamanlar herkes tarafından bilinse de, diğer her şey de öyle. Bu dokümanı Google'dan bulmanın yollarını arıyorsun

güvenlik

Google Uygulama İçi Faturalandırmanın yanı sıra, bilgi sağlayan güvenlik hakkında da düşünceler verir

billing_best_practices


Bu bağlantılar için teşekkürler! "Şifre anahtarı çıktığında, diğer her şey de dışarıda" derken tam olarak ne demek istiyorsun?
caw

Demek istediğim, şifreleme anahtarının güvenli olması gerektiğidir, eğer biri bunu ele geçirebilirse, şifrelenmiş verileriniz düz metin kadar iyidir. Cevabımı bir dereceye kadar yararlı bulduysanız lütfen olumlu oy verin :-)
the100rabh

0

BouncyCastle Lightweight API'sini kullanın. PBE ve Tuz ile 256 AES sağlar.
Dosyaları şifreleyebilen / şifresini çözebilen örnek kod.

public void encrypt(InputStream fin, OutputStream fout, String password) {
    try {
        PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(new SHA256Digest());
        char[] passwordChars = password.toCharArray();
        final byte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
        pGen.init(pkcs12PasswordBytes, salt.getBytes(), iterationCount);
        CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
        ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(256, 128);
        aesCBC.init(true, aesCBCParams);
        PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
        aesCipher.init(true, aesCBCParams);

        // Read in the decrypted bytes and write the cleartext to out
        int numRead = 0;
        while ((numRead = fin.read(buf)) >= 0) {
            if (numRead == 1024) {
                byte[] plainTemp = new byte[aesCipher.getUpdateOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                final byte[] plain = new byte[offset];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            } else {
                byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                int last = aesCipher.doFinal(plainTemp, offset);
                final byte[] plain = new byte[offset + last];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            }
        }
        fout.close();
        fin.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

public void decrypt(InputStream fin, OutputStream fout, String password) {
    try {
        PKCS12ParametersGenerator pGen = new PKCS12ParametersGenerator(new SHA256Digest());
        char[] passwordChars = password.toCharArray();
        final byte[] pkcs12PasswordBytes = PBEParametersGenerator.PKCS12PasswordToBytes(passwordChars);
        pGen.init(pkcs12PasswordBytes, salt.getBytes(), iterationCount);
        CBCBlockCipher aesCBC = new CBCBlockCipher(new AESEngine());
        ParametersWithIV aesCBCParams = (ParametersWithIV) pGen.generateDerivedParameters(256, 128);
        aesCBC.init(false, aesCBCParams);
        PaddedBufferedBlockCipher aesCipher = new PaddedBufferedBlockCipher(aesCBC, new PKCS7Padding());
        aesCipher.init(false, aesCBCParams);

        // Read in the decrypted bytes and write the cleartext to out
        int numRead = 0;
        while ((numRead = fin.read(buf)) >= 0) {
            if (numRead == 1024) {
                byte[] plainTemp = new byte[aesCipher.getUpdateOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                // int last = aesCipher.doFinal(plainTemp, offset);
                final byte[] plain = new byte[offset];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            } else {
                byte[] plainTemp = new byte[aesCipher.getOutputSize(numRead)];
                int offset = aesCipher.processBytes(buf, 0, numRead, plainTemp, 0);
                int last = aesCipher.doFinal(plainTemp, offset);
                final byte[] plain = new byte[offset + last];
                System.arraycopy(plainTemp, 0, plain, 0, plain.length);
                fout.write(plain, 0, plain.length);
            }
        }
        fout.close();
        fin.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Teşekkür ederim! Bu muhtemelen iyi ve güvenli bir çözümdür ancak üçüncü taraf yazılımları kullanmak istemiyorum. Eminim AES'i güvenli bir şekilde kendi başına uygulamak mümkün olmalıdır.
gak

2
Yan kanal saldırılarına karşı koruma eklemek isteyip istemediğinize bağlıdır. Genel olarak, kriptografik algoritmaları kendi başınıza uygulamanın oldukça güvenli olmadığını varsaymalısınız . AES CBC, Oracle'ın Java çalışma zamanı kitaplıklarında mevcut olduğundan, bunları kullanmak ve bir algoritma yoksa Bouncy Castle kitaplıklarını kullanmak muhtemelen en iyisidir.
Maarten Bodewes

Bu tanımı eksik bufgerçekten o değil umut staticalanı). Aynı zamanda her ikisine de benziyor encrypt()ve decrypt()eğer giriş 1024 baytın katları ise son bloğu doğru şekilde işleyemeyecek.
tc.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.