2

アプリケーションで使用する暗号化された設定を保存するために使用する次のクラスがあります(OAuthをサポートしないサードパーティサイトとのインターフェースを使用)...

public class CryptoTranslator {
private static SecretKey SEC_KEY;



/**
 * @return the sEC_KEY
 */
public static SecretKey getSEC_KEY() {
    return SEC_KEY;
}

public static String getSEC_KEY_String(){
    return Base64.encodeToString(SEC_KEY.getEncoded(), Base64.DEFAULT);
}

/**
 * @param sEC_KEY the sEC_KEY to set
 */
public static void setSEC_KEY(SecretKey sEC_KEY) {
    SEC_KEY = sEC_KEY;
}

public static void setSEC_KEY_STRING(String sEC_KEY){
    byte[] key = Base64.decode(sEC_KEY, Base64.DEFAULT);
    SEC_KEY = new SecretKeySpec(key, 0, key.length, "AES");
}

public static void generateKey() throws NoSuchAlgorithmException {
    // Generate a 256-bit key
    final int outputKeyLength = 256;
    SecureRandom secureRandom = new SecureRandom();
    // Do *not* seed secureRandom! Automatically seeded from system entropy.
    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
    keyGenerator.init(outputKeyLength, secureRandom);
    SecretKey key = keyGenerator.generateKey();
    SEC_KEY = key;
}

private static byte[] getRawKey() throws Exception {
    if (SEC_KEY == null){
        generateKey();
    }
    byte[] raw = SEC_KEY.getEncoded();
    return raw;
    }

/**
 * 
 * 
 * @param clear clear text string
 * @param mode this should either be Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
 * @return
 * @throws Exception
 */
private static String translate(String clear, int mode) throws Exception {
    if(mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
        throw new IllegalArgumentException("Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
    SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(mode, skeySpec);
            byte[] encrypted = cipher.doFinal(clear.getBytes());
            return new String(encrypted);
    }

public static String encrypt(String clear) throws Exception {
    return translate(clear,Cipher.ENCRYPT_MODE);
    }
public static String decrypt(String encrypted) throws Exception {
    return translate(encrypted,Cipher.DECRYPT_MODE);
    }

}

これで、データを暗号化して保存しました。今、抜きたい…

            String secString = settings.getString(SEC_KEY, null);
    if (secString == null) {
        try {
            CryptoTranslator.generateKey();
            settings.edit()
                    .putString(SEC_KEY,
                            CryptoTranslator.getSEC_KEY_String()).commit();
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } else {
        CryptoTranslator.setSEC_KEY_STRING(secString);
    }
    try {
        getUserNamePassword();
    } catch (Exception ex) {
        Log.i("Preferences",
                "There was an issue getting username and password");
        isStored = CRED_STATUS_DEF;
    }
            ...
            private static void getUserNamePassword() throws Exception {
      isStored = settings.getBoolean(CRED_STATUS, CRED_STATUS_DEF);
      if (isStored) {
        if (settings.contains(USERNAME_KEY))
            username = settings.getString(USERNAME_KEY, "");
        if (settings.contains(PASSWORD_KEY))
            password = settings.getString(PASSWORD_KEY, "");
      }
      isUsernamePasswordValid();
      if (isStored) {
        String username2 = CryptoTranslator.decrypt(username);
        Log.d("Security", "Username encrypted");
        String password2 = CryptoTranslator.decrypt(password);
        username = username2;
        password = password2;
        Log.d("Security", "Password encrypted");
      }
        }

しかし、これにより次のエラーが発生します....

javax.crypto.IllegalBlockSizeException: 復号化で最後のブロックが不完全です

誰かが私が間違っていることを見ることができますか?

アップデート

応答に従って、先に進み、コードを次のように変更しました...

public static final int IV_LENGTH = 16;
private static final String RANDOM_ALGORITHM = "SHA1PRNG";
...
    private static String translate(String clear, int mode) throws Exception {
    if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
        throw new IllegalArgumentException(
                "Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
    SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    IvParameterSpec ivSpec = new IvParameterSpec(generateIv());
    cipher.init(mode, skeySpec, ivSpec);
    byte[] encrypted = cipher.doFinal(clear.getBytes());
    return new String(encrypted);
}
...
    private static byte[] generateIv() throws NoSuchAlgorithmException,
        NoSuchProviderException {
    SecureRandom random = SecureRandom.getInstance(RANDOM_ALGORITHM);
    byte[] iv = new byte[IV_LENGTH];
    random.nextBytes(iv);
    return iv;
}

今、私は得る...

javax.crypto.BadPaddingException: パッド ブロックが壊れています

16 進数を次のように変更して使用するには...

private static byte[] translate(byte[] val, int mode) throws Exception {
    if (mode != Cipher.ENCRYPT_MODE && mode != Cipher.DECRYPT_MODE)
        throw new IllegalArgumentException(
                "Encryption invalid. Mode should be either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE");
    SecretKeySpec skeySpec = new SecretKeySpec(getRawKey(), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    IvParameterSpec ivSpec = new IvParameterSpec(generateIv());
    cipher.init(mode, skeySpec, ivSpec);
    byte[] encrypted = cipher.doFinal(val);
    return encrypted;
}

これはほとんど機能しているように見えます (.com を取り戻しています) が、文字はまだかなりごちゃごちゃしています。public static String encrypt(String clear) throws Exception { byte[] test = translate(clear.getBytes(), Cipher.ENCRYPT_MODE); 新しい文字列を返します (Hex.encodeHex(テスト)); }

public static String decrypt(String encrypted) throws Exception {
    return new String(translate(Hex.decodeHex(encrypted.toCharArray()), Cipher.DECRYPT_MODE));
}

* Hex とその逆への変換は、ここで失敗します。

4

2 に答える 2

2

したがって、コードにはいくつかの問題があります。

まず、AES暗号の出力は文字データではありません.文字列に入れようとして暗号文を壊しています. 壊れた暗号文を解読しようとすると、長さが正しくありません。暗号文を文字列に格納する場合は、暗号文を Base64 または Hex でエンコードし、byte[]復号化する前にデコードして に戻す必要があります。

次に、AES暗号仕様のみを指定すると、Java はそれを に展開しAES/ECB/PKCS5Paddingます。ECB複数のデータ ブロック (AES の場合は 16 バイト) を暗号化する場合、安全でない暗号モードです。別の仕様に切り替えることをお勧めしますAES/CBC/PKCS5Padding。以外のモードを使用するECBには、初期化ベクトル (IV) が必要です。IV はランダムに生成する必要がありますが、秘密にする必要はありません。そのため、復号化にも必要になるため、暗号文とともにプレーンテキストとして保存できます。初期化ベクトルは、長さが 1 ブロック (AES の場合は 16 バイト) である必要があります。同じ AES キーで同じ IV を再利用しないでください。暗号化が行われるたびに新しい IV を生成してください。

最後に、IV + 暗号文をサードパーティ サービスに保存する場合は、MAC (HMACSHA1 など) を追加することをお勧めします。MAC は、復号化を試みる前に、IV + 暗号文の完全性を保証します。MAC には秘密鍵も必要であり、暗号自体に生成した同じ鍵を使用しないでください。生成された MAC を IV + 暗号文の先頭に追加できるため、MAC + IV + 暗号文を保存できます。

于 2013-10-25T18:17:23.483 に答える
0

Android AES クライアント側 + PHP AES サーバー側では、このエラーがスローされます:)

解決策は次のとおりです。

cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

完全なソース コードについては、インターネットで検索してください。私は秘密保持契約を結んでおり、この部分に関するコード全体を匿名にするのを怠っていますが、きっと見つかると思います。

于 2013-10-25T17:41:47.903 に答える