菜鸟教程小白 发表于 2022-12-12 19:22:20

android - 在 Android 和 iPhone 中使用 AES 256 加密(不同的结果)


                                            <p><p>我正在尝试通过引用 IOS 实现在 Android 平台上实现客户端加密/解密。我正在努力解决Android和IOS平台上的加密和解密不同的问题,即使它们使用了相同的算法。比方说,当Android设备加密上传文件到服务器时,IOS设备无法正确下载和解密。</p>

<p>我正在使用的算法</p>

<ol>
<li>使用用户提供的密码加密文件 key 。我们首先使用 PBKDF2 算法(SHA256 的 1000 次迭代)从密码中导出 key /iv 对,然后使用 AES 256/CBC 加密文件 key 。结果称为“加密文件 key ”。这个加密的文件 key 将被发送到并存储在服务器上。当您需要访问数据时,您可以从加密的文件 key 中解密文件 key 。</li>
<li>所有文件数据均使用 AES 256/CBC 文件 key 加密。我们使用 PBKDF2 算法(SHA256 的 1000 次迭代)从文件 key 中导出 key /iv 对。 </li>
<li>在本地存储派生的 key /iv 对并使用它们来加密文件。加密后,数据上传到服务器。下载文件时解密文件也是如此。</li>
</ol>

<p>安卓代码</p>

<pre><code>    private static final String TAG = Crypto.class.getSimpleName();

    private static final String CIPHER_ALGORITHM = &#34;AES/CBC/NoPadding&#34;;

    private static int KEY_LENGTH = 32;
    private static int KEY_LENGTH_SHORT = 16;
    // minimum values recommended by PKCS#5, increase as necessary
    private static int ITERATION_COUNT = 1000;
    // Should generate random salt for each repo
    private static byte[] salt = {(byte) 0xda, (byte) 0x90, (byte) 0x45, (byte) 0xc3, (byte) 0x06, (byte) 0xc7, (byte) 0xcc, (byte) 0x26};

    private Crypto() {
    }

    /**
   * decrypt repo encKey
   *
   * @param password
   * @param randomKey
   * @param version
   * @return
   * @throws UnsupportedEncodingException
   * @throws NoSuchAlgorithmException
   */
    public static String deriveKeyPbkdf2(String password, String randomKey, int version) throws UnsupportedEncodingException, NoSuchAlgorithmException {
      if (TextUtils.isEmpty(password) || TextUtils.isEmpty(randomKey)) {
            return null;
      }

      PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
      gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password.toCharArray()), salt, ITERATION_COUNT);
      byte[] keyBytes;

      if (version == 2) {
            keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH * 8)).getKey();
      } else
            keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey();

      SecretKey realKey = new SecretKeySpec(keyBytes, &#34;AES&#34;);

      final byte[] iv = deriveIVPbkdf2(realKey.getEncoded());

      return seafileDecrypt(fromHex(randomKey), realKey, iv);
    }

    public static byte[] deriveIVPbkdf2(byte[] key) throws UnsupportedEncodingException {
      PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
      gen.init(key, salt, 10);
      return ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey();
    }

    /**
   * All file data is encrypted by the file key with AES 256/CBC.
   *
   * We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key.
   * After encryption, the data is uploaded to the server.
   *
   * @param plaintext
   * @param key
   * @return
   */
    private static byte[] seafileEncrypt(byte[] plaintext, SecretKey key, byte[] iv) {
      try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);

            IvParameterSpec ivParams = new IvParameterSpec(iv);
            cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);

            return cipher.doFinal(plaintext);
      } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            Log.e(TAG, &#34;NoSuchAlgorithmException &#34; + e.getMessage());
            return null;
      } catch (InvalidKeyException e) {
            e.printStackTrace();
            Log.e(TAG, &#34;InvalidKeyException &#34; + e.getMessage());
            return null;
      } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
            Log.e(TAG, &#34;InvalidAlgorithmParameterException &#34; + e.getMessage());
            return null;
      } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            Log.e(TAG, &#34;NoSuchPaddingException &#34; + e.getMessage());
            return null;
      } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
            Log.e(TAG, &#34;IllegalBlockSizeException &#34; + e.getMessage());
            return null;
      } catch (BadPaddingException e) {
            e.printStackTrace();
            Log.e(TAG, &#34;BadPaddingException &#34; + e.getMessage());
            return null;
      }
    }

    /**
   * All file data is encrypted by the file key with AES 256/CBC.
   *
   * We use PBKDF2 algorithm (1000 iterations of SHA256) to derive key/iv pair from the file key.
   * After encryption, the data is uploaded to the server.
   *
   * @param plaintext
   * @param key
   * @return
   */
    public static byte[] encrypt(byte[] plaintext, String key, byte[] iv, int version) throws NoSuchAlgorithmException {
      PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(new SHA256Digest());
      gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(key.toCharArray()), salt, ITERATION_COUNT);
      byte[] keyBytes;

      if (version == 2) {
            keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH * 8)).getKey();
      } else
            keyBytes = ((KeyParameter) gen.generateDerivedMacParameters(KEY_LENGTH_SHORT * 8)).getKey();

      SecretKey realKey = new SecretKeySpec(keyBytes, &#34;AES&#34;);
      return seafileEncrypt(plaintext, realKey , iv);
    }
</code></pre>

<p>IOS代码</p>

<pre><code>+ (int)deriveKey:(const char *)data_in inlen:(int)in_len version:(int)version key:(unsigned char *)key iv:(unsigned char *)iv
{
    unsigned char salt = { 0xda, 0x90, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26 };
    if (version == 2) {
      PKCS5_PBKDF2_HMAC (data_in, in_len,
                           salt, sizeof(salt),
                           1000,
                           EVP_sha256(),
                           32, key);
      PKCS5_PBKDF2_HMAC ((char *)key, 32,
                           salt, sizeof(salt),
                           10,
                           EVP_sha256(),
                           16, iv);
      return 0;
    } else if (version == 1)
      return EVP_BytesToKey (EVP_aes_128_cbc(), /* cipher mode */
                               EVP_sha1(),      /* message digest */
                               salt,            /* salt */
                               (unsigned char*)data_in,
                               in_len,
                               1 &lt;&lt; 19,   /* iteration times */
                               key, /* the derived key */
                               iv); /* IV, initial vector */
    else
      return EVP_BytesToKey (EVP_aes_128_ecb(), /* cipher mode */
                               EVP_sha1(),      /* message digest */
                               NULL,            /* salt */
                               (unsigned char*)data_in,
                               in_len,
                               3,   /* iteration times */
                               key, /* the derived key */
                               iv); /* IV, initial vector */
}

+(int)seafileEncrypt:(char **)data_out outlen:(int *)out_len datain:(const char *)data_in inlen:(const int)in_len version:(int)version key:(uint8_t *)key iv:(uint8_t *)iv
{
    int ret, blks;
    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init (&amp;ctx);
    if (version == 2)
      ret = EVP_EncryptInit_ex (&amp;ctx,
                                  EVP_aes_256_cbc(), /* cipher mode */
                                  NULL, /* engine, NULL for default */
                                  key,/* derived key */
                                  iv);/* initial vector */
    else if (version == 1)
      ret = EVP_EncryptInit_ex (&amp;ctx,
                                  EVP_aes_128_cbc(), /* cipher mode */
                                  NULL, /* engine, NULL for default */
                                  key,/* derived key */
                                  iv);/* initial vector */
    else
      ret = EVP_EncryptInit_ex (&amp;ctx,
                                  EVP_aes_128_ecb(), /* cipher mode */
                                  NULL, /* engine, NULL for default */
                                  key,/* derived key */
                                  iv);/* initial vector */
    if (ret == DEC_FAILURE)
      return -1;

    blks = (in_len / BLK_SIZE) + 1;
    *data_out = (char *)malloc (blks * BLK_SIZE);
    if (*data_out == NULL) {
      Debug (&#34;failed to allocate the output buffer.\n&#34;);
      goto enc_error;
    }
    int update_len, final_len;

    /* Do the encryption. */
    ret = EVP_EncryptUpdate (&amp;ctx,
                           (unsigned char*)*data_out,
                           &amp;update_len,
                           (unsigned char*)data_in,
                           in_len);

    if (ret == ENC_FAILURE)
      goto enc_error;


    /* Finish the possible partial block. */
    ret = EVP_EncryptFinal_ex (&amp;ctx,
                               (unsigned char*)*data_out + update_len,
                               &amp;final_len);

    *out_len = update_len + final_len;

    /* out_len should be equal to the allocated buffer size. */
    if (ret == ENC_FAILURE || *out_len != (blks * BLK_SIZE))
      goto enc_error;

    EVP_CIPHER_CTX_cleanup (&amp;ctx);

    return 0;

enc_error:
    EVP_CIPHER_CTX_cleanup (&amp;ctx);
    *out_len = -1;

    if (*data_out != NULL)
      free (*data_out);

    *data_out = NULL;

    return -1;
}

+ (void)generateKey:(NSString *)password version:(int)version encKey:(NSString *)encKey key:(uint8_t *)key iv:(uint8_t *)iv
{
    unsigned char key0, iv0;
    char passwordPtr = {0}; // room for terminator (unused)
    ;
    if (version &lt; 2) {
      ;
      return;
    }
    ;
    char enc_random_key, dec_random_key;
    int outlen;
    hex_to_rawdata(encKey.UTF8String, enc_random_key, 48);
    ;
    ;
}

- (NSData *)encrypt:(NSString *)password encKey:(NSString *)encKey version:(int)version
{
    uint8_t key = {0}, iv;
    ;
    char *data_out;
    int outlen;
    int ret = ;
    if (ret &lt; 0) return nil;
    return ;
}
</code></pre>

<p>这是一个完整的项目,任何人都觉得这很有用。</p>

<ol>
<li> <a href="https://github.com/haiwen/seadroid/pull/487" rel="noreferrer noopener nofollow">Support client side encryption #487</a> </li>
<li> <a href="http://manual.seafile.com/security/security_features.html" rel="noreferrer noopener nofollow">How does an encrypted library work?</a> </li>
</ol></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>你好,我也有同样的问题。</p>

<p>我发现 2 件事导致我的代码不匹配:
1. ios 和 android 加密算法不一样(我在 ios 中请求了 PKCS7Padding 算法,我在 android 中尝试了 NoPadding 和 AES/CBC/PKCS5Padding 算法。
2. ios生成的key与Android不同。</p>

<p>请查看我的工作代码在 ios 和 android 中都给出了相同的输出:</p>

<p>IOS:</p>

<pre><code>- (NSString *) encryptString:(NSString*)plaintext withKey:(NSString*)key {
NSData *data = [ AES256EncryptWithKey:key];
return ;
}
- (NSString *) decryptString:(NSString *)ciphertext withKey:(NSString*)key {
if (]) {

    NSData *data = [ initWithBase64EncodedString:ciphertext options:kNilOptions];
    return [ initWithData: encoding:NSUTF8StringEncoding];
}
return nil;
}
</code></pre>

<p>Android:(我们修改了 aes w.r.t iOS 默认方法)</p>

<pre><code>private static String Key = &#34;your key&#34;;
public static String encryptString(String stringToEncode) throws NullPointerException {

    try {
      SecretKeySpec skeySpec = getKey(Key);
      byte[] clearText = stringToEncode.getBytes(&#34;UTF8&#34;);
      final byte[] iv = new byte;
      Arrays.fill(iv, (byte) 0x00);
      IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
      Cipher cipher = Cipher.getInstance(&#34;AES/CBC/PKCS7Padding&#34;);
      cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
      String encrypedValue = Base64.encodeToString(cipher.doFinal(clearText), Base64.DEFAULT);
      return encrypedValue;

    } catch (Exception e) {
      e.printStackTrace();
    }
    return &#34;&#34;;
}
public static String encryptString(String stringToEncode) throws NullPointerException {

    try {
      SecretKeySpec skeySpec = getKey(Key);
      byte[] clearText = stringToEncode.getBytes(&#34;UTF8&#34;);
      final byte[] iv = new byte;
      Arrays.fill(iv, (byte) 0x00);
      IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
      Cipher cipher = Cipher.getInstance(&#34;AES/CBC/PKCS7Padding&#34;);
      cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
      byte[] cipherData = cipher.doFinal(Base64.decode(stringToEncode.getBytes(&#34;UTF-8&#34;), Base64.DEFAULT));
      String decoded = new String(cipherData, &#34;UTF-8&#34;);
      return decoded;

    } catch (Exception e) {
      e.printStackTrace();
    }
    return &#34;&#34;;
}


private static SecretKeySpec getKey(String password) throws UnsupportedEncodingException {
    int keyLength = 256;
    byte[] keyBytes = new byte;
    Arrays.fill(keyBytes, (byte) 0x0);
    byte[] passwordBytes = password.getBytes(&#34;UTF-8&#34;);
    int length = passwordBytes.length &lt; keyBytes.length ? passwordBytes.length : keyBytes.length;
    System.arraycopy(passwordBytes, 0, keyBytes, 0, length);
    SecretKeySpec key = new SecretKeySpec(keyBytes, &#34;AES&#34;);
    return key;
}
</code></pre>

<p>希望这对你们有帮助
谢谢</p></p>
                                   
                                                <p style="font-size: 20px;">关于android - 在 Android 和 iPhone 中使用 AES 256 加密(不同的结果),我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/36589644/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/36589644/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: android - 在 Android 和 iPhone 中使用 AES 256 加密(不同的结果)