14

Android と IPhone の両方のプラットフォームで AES アルゴリズムを使用してテキストを暗号化しようとしています。私の問題は、同じ暗号化/復号化アルゴリズム (AES-128) と同じ固定変数 (キー、IV、モード) を使用しても、両方のプラットフォームで異なる結果が得られることです。暗号化/復号化をテストするために使用している両方のプラットフォームのコード サンプルを含めています。私が間違っていることを特定するための助けをいただければ幸いです。

  • キー: 「123456789abcdefg」</li>
  • IV: 「1111111111111111」</li>
  • 平文: 「こんにちは」</li>
  • モード: 「AES/CBC/NoPadding」</li>

Android コード:

public class Crypto {
    private final static String HEX = "0123456789ABCDEF";

    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("CBC");
        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 static void appendHex(StringBuffer sb, byte b) {
        sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
    }
}

iPhone (Objective-C) コード:

- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData { 

    NSData* secretKey = [Cipher md5:cipherKey];

    CCCryptorRef cryptor = NULL;
    CCCryptorStatus status = kCCSuccess;

    uint8_t iv[kCCBlockSizeAES128];
    memset((void *) iv, 0x0, (size_t) sizeof(iv));

    status = CCCryptorCreate(encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                         [secretKey bytes], kCCKeySizeAES128, iv, &cryptor);

    if (status != kCCSuccess) {
        return nil;
    }

    size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length], true);

    void * buf = malloc(bufsize * sizeof(uint8_t));
    memset(buf, 0x0, bufsize);

    size_t bufused = 0;
    size_t bytesTotal = 0;

    status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
                         buf, bufsize, &bufused);

    if (status != kCCSuccess) {
        free(buf);
        CCCryptorRelease(cryptor);
        return nil;
    }

    bytesTotal += bufused;

    status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);

    if (status != kCCSuccess) {
        free(buf);
        CCCryptorRelease(cryptor);
        return nil;
    }

    bytesTotal += bufused;

    CCCryptorRelease(cryptor);

    return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
}

+ (NSData *) md5:(NSString *) stringToHash {

    const char *src = [stringToHash UTF8String];

    unsigned char result[CC_MD5_DIGEST_LENGTH];

    CC_MD5(src, strlen(src), result);

    return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
}

私の参照のいくつか:

4

6 に答える 6

19

iPhoneの場合はAESCrypt-ObjCを使用し、Android の場合は次のコードを使用します。

public class AESCrypt {

  private final Cipher cipher;
  private final SecretKeySpec key;
  private AlgorithmParameterSpec spec;


  public AESCrypt(String password) throws Exception
  {
    // hash password with SHA-256 and crop the output to 128-bit for key
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    digest.update(password.getBytes("UTF-8"));
    byte[] keyBytes = new byte[32];
    System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);

    cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    key = new SecretKeySpec(keyBytes, "AES");
    spec = getIV();
  }       

  public AlgorithmParameterSpec getIV()
  {
    byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
    IvParameterSpec ivParameterSpec;
    ivParameterSpec = new IvParameterSpec(iv);

    return ivParameterSpec;
  }

  public String encrypt(String plainText) throws Exception
  {
    cipher.init(Cipher.ENCRYPT_MODE, key, spec);
    byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
    String encryptedText = new String(Base64.encode(encrypted, Base64.DEFAULT), "UTF-8");

    return encryptedText;
  }

  public String decrypt(String cryptedText) throws Exception
  {
    cipher.init(Cipher.DECRYPT_MODE, key, spec);
    byte[] bytes = Base64.decode(cryptedText, Base64.DEFAULT);
    byte[] decrypted = cipher.doFinal(bytes);
    String decryptedText = new String(decrypted, "UTF-8");

    return decryptedText;
  }
}
于 2013-05-31T10:07:03.850 に答える
7

異なる結果が得られても不思議ではありません。

問題は、鍵の導出に SHA1PRNG を誤用していることです。私の知る限り、SHA1PRNGが内部でどのように機能するかという共通の標準はありません。AFAIR J2SE と Bouncycaste の実装でさえ、同じシードを使用して異なる結果を出力します。

したがって、あなたの実装はgetRawKey(byte[] seed)ランダムキーを生成します。暗号化にキーを使用すると、そのキーに依存する結果が得られます。キーはランダムであるため、iOS では同じキーを取得できないため、異なる結果が得られます。

キー派生関数が必要な場合は、キー派生に関してほぼ完全に標準化された PBKDF2 のような関数を使用します。

于 2012-11-09T16:11:24.537 に答える
3

Android では、 を使用してgetBytes()います。これは、既知の文字セットではなくデフォルトの文字セットを使用していることを意味するため、エラーです。代わりに使用getBytes("UTF-8")して、取得するバイト数を正確に把握してください。

Objective-C に相当するものはわかりませんが、デフォルトには依存していません。文字列をバイトに変換するときは、UTF-8 を明示的に指定します。そうすれば、両側で同じバイトを取得できます。

また、Objective-C コードでは MD5 を使用していますが、Android コードでは使用していません。これは意図的なものですか?

于 2012-11-09T15:38:00.243 に答える
2

「シード」をパスワードとして効果的に使用しているため、パスワードベースのAES暗号化については私の回答を参照してください。(必要に応じて、キーの長さ256から128に変更するだけです。)

同じ値でDRBGをシードすることによって同じキーを生成しようとすることは、信頼できません。

次に、Android暗号化でCBCまたはIVを使用していません。私の例は、それを適切に行う方法も示しています。ちなみに、私の例が示すように、暗号化するメッセージごとに新しいIVを生成し、暗号文と一緒に送信する必要があります。そうでなければ、CBCを使用しても意味がありません。

于 2012-11-10T18:11:36.933 に答える
1

Android と iPhone で互換性のあるコードの例が必要な場合は、iOS のRNCryptorライブラリと Java/Android の JNCryptor ライブラリを参照してください。

どちらのプロジェクトもオープン ソースであり、共通のデータ形式を共有しています。これらのライブラリでは、AES 256 ビットが使用されますが、128 ビット AES をサポートするために必要に応じてコードを変更するのは簡単です。

受け入れられた回答によると、両方のライブラリがPBKDF2を使用しています。

于 2013-03-04T15:23:22.993 に答える