AndroidでAES暗号化を使用するためのベストプラクティスは何ですか?


89

この質問をする理由:

Androidであっても、AES暗号化について多くの質問があったことは知っています。また、Webを検索すると、多数のコードスニペットがあります。しかし、すべてのページ、すべてのStack Overflowの質問で、大きな違いがある別の実装を見つけました。

そこで、「ベストプラクティス」を見つけるためにこの質問を作成しました。最も重要な要件のリストを収集し、本当に安全な実装をセットアップできることを願っています!

初期化ベクトルとソルトについて読みました。私が見つけたすべての実装にこれらの機能があったわけではありません。それであなたはそれが必要ですか?セキュリティは大幅に向上しますか?どのように実装しますか?暗号化されたデータを復号化できない場合、アルゴリズムは例外を発生させる必要がありますか?または、それは安全ではなく、単に読み取り不可能な文字列を返す必要がありますか?アルゴリズムはSHAの代わりにBcryptを使用できますか?

私が見つけたこれらの2つの実装についてはどうですか?彼らは大丈夫ですか?完璧またはいくつかの重要なものが欠けていますか?これらのうちどれが安全ですか?

アルゴリズムは、暗号化のために文字列と「パスワード」を受け取り、そのパスワードで文字列を暗号化する必要があります。出力は文字列(hexまたはbase64?)である必要があります。もちろん、復号化も可能であるべきです。

Androidに最適なAES実装は何ですか?

実装#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;
        }

}

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

実装#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));
    }

}

ソース:http : //www.tutorials-android.com/learn/How_to_encrypt_and_decrypt_strings.rhtml


ソリューション1を実装しようとしていますが、いくつかのクラスが必要でした。完全なソースコードはありますか?
albanx 2012年

1
いいえ、申し訳ありません。しかし、私はそれを単に削除implements ICryptoして変更throws CryptoExceptionするthrows Exceptionなどしてそれを機能させました。したがって、これらのクラスはもう必要ありません。
21:37

しかし、HexEncoderクラスもありませんか?どこにありますか?
albanx 2012年

HexEncoderはBouncyCastleライブラリの一部だと思います。ダウンロードするだけです。または、Javaで「byte [] to hex」やその逆をグーグルできます。
CAW

マルコありがとう。しかし、私は3つの方法があることがわかりgetSecretKeygetHashgenerateSalt未使用の最初の実装では。多分私は間違っていますが、実際にこのクラスを使用して文字列を暗号化するにはどうすればよいですか?
albanx 2012年

回答:


37

質問で指定した実装は完全に正しいものではありません。また、指定した実装もそのまま使用するべきではありません。以下では、Androidにおけるパスワードベースの暗号化の側面について説明します。

キーとハッシュ

ソルトを使用したパスワードベースのシステムについて説明します。ソルトはランダムに生成された数値です。「推定」されていません。実装1には、generateSalt()暗号的に強い乱数を生成方法がます。ソルトはセキュリティにとって重要であるため、一度だけ生成する必要がありますが、生成後は秘密にしておく必要があります。これがWebサイトである場合、saltを秘密に保つことは比較的簡単ですが、インストールされたアプリケーション(デスクトップおよびモバイルデバイス用)の場合、これははるかに困難になります。

このメソッドgetHash()は、指定されたパスワードとソルトのハッシュを単一の文字列に連結して返します。使用されるアルゴリズムはSHA-512で、512ビットのハッシュを返します。このメソッドは、文字列の整合性をチェックするのに役立つハッシュを返すため、次の呼び出しで使用することもできますgetHash()ます。両方のパラメーターを連結するだけなので、パスワードまたはソルトだけでできます。この方法はパスワードベースの暗号化システムでは使用されないため、これ以上は説明しません。

メソッドgetSecretKey()は、からchar返されるように、パスワードと16進エンコードされたソルトの配列からキーを導出しますgenerateSalt()。使用されているアルゴリズムは、ハッシュ関数としてSHA-256を使用したPKCS5のPBKDF1(私が思う)であり、256ビットのキーを返します。ブルートフォース攻撃を仕掛けるのに必要な時間を増やすためにgetSecretKey()、パスワード、ソルト、およびカウンターのハッシュ(で指定された反復回数までPBE_ITERATION_COUNT、ここでは100)を繰り返し生成することによってキーを生成します。ソルトの長さは、少なくとも生成されるキーと同じ長さである必要があり、この場合、少なくとも256ビットです。反復回数は、不当な遅延を引き起こさずに、できるだけ長く設定する必要があります。キー導出におけるソルトと反復カウントの詳細については、RFC2898のセクション4を参照してください。

ただし、JavaのPBEでの実装には、パスワードにUnicode文字、つまり8ビット以上の表現が必要な文字が含まれている場合、欠陥があります。で述べたようにPBEKeySpec、「PKCS#5で定義されたPBEメカニズムは、各文字の下位8ビットのみを調べます」。この問題を回避するには、パスワードをに渡す前に、パスワード内のすべての16ビット文字の16進文字列(8ビット文字のみを含む)を生成してみてくださいPBEKeySpec。たとえば、「ABC」は「004100420043」になります。また、PBEKeySpecは「char配列としてパスワードを要求するためclearPassword()、完了時に[ ] で上書きできる」ことに注意してください。(「メモリ内の文字列の保護」に関しては、この質問を参照してください。)ただし、問題はありませんが、

暗号化

キーが生成されたら、それを使用してテキストを暗号化および復号化できます。

実装1では、使用される暗号アルゴリズムはAES/CBC/PKCS5Padding、つまり、PKCS#5でパディングが定義された、Cipher Block Chaining(CBC)暗号モードのAESです。(その他のAES暗号モードには、カウンターモード(CTR)、電子コードブックモード(ECB)、およびガロアカウンターモード(GCM)が含まれます。スタックオーバーフローに関する別の質問には、さまざまなAES暗号モードおよび使用する推奨モードを詳細に説明する回答が含まれています。 CBCモードの暗号化にはいくつかの攻撃があり、そのうちのいくつかはRFC 7457で言及されていることにも注意してください。

暗号化されたデータの整合性もチェックする暗号化モードを使用する必要があることに注意してください(たとえば、RFC 5116で説明されている、関連付けられたデータ認証された暗号化、AEAD)。ただし、AES/CBC/PKCS5Paddingは整合性チェックを提供しないため、単体ではお勧めできません。AEADの目的では、関連するキー攻撃を回避するために、通常の暗号化キーの少なくとも2倍の長さのシークレットを使用することをお勧めします。前半は暗号化キーとして機能し、後半は整合性チェックのキーとして機能します。(つまり、この場合、パスワードとソルトから1つのシークレットを生成し、そのシークレットを2つに分割します。)

Java実装

実装1のさまざまな関数は、そのアルゴリズムに特定のプロバイダー、つまり「BC」を使用します。ただし、一般に、サポートの欠如、コードの重複の回避、その他の理由のために、すべてのプロバイダーがすべてのJava実装で使用できるわけではないため、特定のプロバイダーを要求することはお勧めできません。2018年の初めにAndroid Pプレビューがリリースされて以来、このアドバイスは特に重要になっています。これは、「BC」プロバイダーの一部の機能が廃止されたためです。Android開発者ブログの「Android Pでの暗号化の変更」の記事を参照してください。Introduction to Oracle Providersも参照してください。

したがって、PROVIDER存在してはならず、文字列-BCはから削除されPBE_ALGORITHMます。この点で実装2は正しいです。

メソッドがすべての例外をキャッチするのではなく、可能な例外のみを処理するのは不適切です。質問で与えられた実装は、さまざまなチェック例外をスローする可能性があります。メソッドは、チェックされた例外のみをCryptoExceptionでラップするか、チェックされた例外を句で指定するかを選択できますthrows。便宜上、クラスがスローする可能性のあるチェック済み例外が多数存在する可能性があるため、ここでは元の例外をCryptoExceptionでラップするのが適切な場合があります。

SecureRandom Androidで

Androidデベロッパーブログの記事「いくつかのSecureRandom Thoughts」で詳しく説明されているように、java.security.SecureRandom2013年より前のAndroidリリースでの実装には、配信する乱数の強度を低下させる欠陥があります。この欠陥は、その記事で説明されているように軽減できます。


私の考えでは、この2つの秘密の生成は少し無駄です。生成された秘密を2つに簡単に分割できます。または、十分なビットがない場合は、カウンター(1つ目のキーに1つ、2つ目のキーに2つ)を秘密にし、単一のハッシュを実行します。すべての反復を2回実行する必要はありません。
Maarten Bodewes、2011

HMACとsaltについての情報をありがとう。今回はHMACを使用しませんが、後で非常に役立つかもしれません。そして一般的に、これは間違いなく良いことです。
2011

すべての編集と、JavaでのAES暗号化の(今)素晴らしい紹介をありがとうございました!
1

1
そうすべき。getInstanceアルゴリズムの名前のみを取るオーバーロードがあります。例:Cipher.getInstance() Bouncy Castleを含むいくつかのプロバイダーがJava実装に登録されている可能性があります。この種のオーバーロードは、プロバイダーのリストで、指定されたアルゴリズムを実装するプロバイダーの1つを検索します。試してみてください。
Peter O.12年

1
そう、Security.getProviders()で指定された順序でプロバイダーを検索します。ただし、ハードウェア支援の暗号化を可能にするinit()呼び出し中に、プロバイダーによってキーが受け入れられるかどうかもチェックします。詳細はこちら:docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/…
Maarten Bodewes 2012年

18

#2は暗号に "AES"(つまり、テキストのECBモード暗号化、大きな禁止)のみを使用するため、決して使用しないでください。#1についてお話します。

最初の実装は、暗号化のベストプラクティスに準拠しているようです。定数は通常OKですが、PBEを実行するためのソルトサイズと反復回数はどちらも短めです。さらに、PBEキー生成は256をハードコードされた値として使用するため(これらすべての定数の後の恥)、AES-256のようです。CBCとPKCS5Paddingを使用していますが、これは少なくとも期待どおりのものです。

認証/完全性の保護は完全に欠落しているため、攻撃者は暗号テキストを変更できます。つまり、クライアント/サーバーモデルでは、オラクル攻撃のパディングが可能です。また、攻撃者が暗号化されたデータを変更しようとする可能性もあります。これは、パディングまたはコンテンツがアプリケーションによって受け入れられないため、どこかで何らかのエラーが発生する可能性がありますが、それはあなたが入りたい状況ではありません。

私の本では、例外処理と入力検証を強化することができ、例外のキャッチは常に間違っています。さらに、クラスはICryptを実装していますが、これは私にはわかりません。クラスに副作用のないメソッドだけがあるのは少し変だと知っています。通常、これらは静的にします。Cipherインスタンスなどのバッファリングはないため、必要なすべてのオブジェクトが悪用されます。ただし、ICryptoを定義から安全に削除できます。その場合、コードを静的メソッドにリファクタリングすることもできます(または、選択したオブジェクト指向に変更することもできます)。

問題は、ラッパーは常にユースケースについての仮定を行うことです。したがって、ラッパーが正しいか間違っていると言うのは二段です。これが、ラッパークラスの生成を常に回避しようとする理由です。しかし、少なくともそれは明らかに間違っているようには見えません。


この詳細な回答をありがとうございました!残念ですが、コードレビューセクションはまだ知りませんでした:Dこのヒントをありがとう、チェックします。しかし、私はこれらのコードスニペットのレビューだけを望んでいないので、この質問も私の意見ではここに当てはまります。代わりに、AndroidにAES暗号化を実装するときに重要な側面をすべてお聞きしたいと思います。このコードスニペットはAES-256用です。それで、これはAES-256の一般的な安全な実装だと言えますか?使用例は、データベースにテキスト情報を安全に保存したい場合です。
2011

1
見た目は良いですが、整合性チェックと認証を行わないという考えは私を悩ませます。十分なスペースがある場合は、暗号文にHMACを追加することを真剣に検討します。とは言っても、おそらく機密性を追加しようとしているだけなので、それを大きな要件と見なしますが、直接要件ではありません。
Maarten Bodewes、2011

しかし、暗号化された情報に他の人がアクセスできないようにすることだけが目的の場合は、HMACは必要ありません。彼らが暗号文を変更して、「間違った」解読結果を強制する場合、実際の問題はありませんか?
2011

それがリスクシナリオにない場合は、問題ありません。暗号テキストを変更した後、システムによって何らかの方法で繰り返し復号化をトリガーできる場合(パディングオラクル攻撃)、キーを知らなくてもデータを復号化できます。キーを持たないシステム上のデータを単に取得するだけでは、これを行うことはできません。しかし、それがHMACを追加することが常にベストプラクティスである理由です。個人的には、AES-128とHMACを備えたシステムは、AES-256を備えていないシステムよりも安全であると考えます。
Maarten Bodewes、2011

1
整合性が必要な場合は、ガロア/カウンターモード(AES-GCM)でAESを使用しないのはなぜですか。
Kimvais 2011

1

あなたはかなり興味深い質問をしました。他のすべてのアルゴリズムと同様に、暗号鍵は「秘密のソース」です。いったんそれが一般に知られると、他のすべてもそうなります。Googleによるこのドキュメントの方法を調べます

安心

Googleアプリ内課金に加えて、洞察に富むセキュリティについても考えます

billing_best_practices


これらのリンクをありがとう!「暗号鍵が出ているとき、他のすべても出ている」とはどういう意味ですか?
CAW

つまり、暗号化キーは安全である必要があるということです。誰かがそれを入手できる場合、暗号化されたデータはプレーンテキストと同じくらい優れています。私の回答がある程度役に立ったと
思われる場合は投票してください

0

BouncyCastle Lightweight APIを使用します。PBEとソルトで256 AESを提供します。
ここでは、ファイルを暗号化/復号化できるサンプルコードを示します。

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();
    }
}

ありがとうございました!これはおそらく適切で安全なソリューションですが、サードパーティのソフトウェアを使用したくありません。自分で安全にAESを実装することが可能でなければならないと私は確信しています。
2011

2
サイドチャネル攻撃に対する保護を含めるかどうかによって異なります。一般に、暗号アルゴリズムを自分で実装するのはかなり安全ではないと想定する必要があります。AES CBCはOracleのJavaランタイムライブラリで使用できるため、アルゴリズムを使用できない場合は、それらを使用し、Bouncy Castleライブラリを使用するのがおそらく最善です。
Maarten Bodewes、2012年

これは、の定義が欠落していますbuf(私は実際にそれはありません願っていstaticフィールド)。また、両方のように見えるencrypt()decrypt()入力が1024バイトの倍数であれば、正しく最終ブロックの処理に失敗します。
tc。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.