Encryption mit AES 256 in Android und IPhone (anderes Ergebnis)

Ich versuche, clientseitige Verschlüsselung / Entschlüsselung auf der Android-Plattform zu implementieren, indem ich auf IOS-Implementierungen verweise. Ich ringe mit der Frage, ob sich die Verschlüsselung und Entschlüsselung auf Android- und IOS-Plattformen unterscheiden, obwohl sie denselben Algorithmus verwendet haben. Sagen wir, wenn ein Android-Gerät eine Datei verschlüsselt und auf den Server hochlädt, kann das iOS-Gerät sie nicht korrekt herunterladen und entschlüsseln.

Der Algorithmus, den ich verwende

Verschlüsseln Sie den Dateischlüssel mit dem vom Benutzer angegebenen Kennwort. Wir verwenden zuerst den PBKDF2-Algorithmus (1000 Iterationen von SHA256), um ein Schlüssel / IV-Paar aus dem Kennwort abzuleiten, und verwenden dann AES 256 / CBC, um den Dateischlüssel zu verschlüsseln. Das Ergebnis wird als "verschlüsselter Dateischlüssel" bezeichnet. Dieser verschlüsselte Dateischlüssel wird an den Server gesendet und dort gespeichert. Wenn Sie auf die Daten zugreifen müssen, können Sie den Dateischlüssel aus dem verschlüsselten Dateischlüssel entschlüsseln. Alle Dateidaten werden mit dem Dateischlüssel mit AES 256 / CBC verschlüsselt. Wir verwenden den PBKDF2-Algorithmus (1000 Iterationen von SHA256), um das Schlüssel / IV-Paar vom Dateischlüssel abzuleiten.Speichern Sie das abgeleitete Schlüssel / IV-Paar lokal und verwenden Sie es zum Verschlüsseln von Dateien. Nach der Verschlüsselung werden die Daten auf den Server hochgeladen. Dasselbe gilt für das Entschlüsseln von Dateien beim Herunterladen von Dateien.

Android Code

    private static final String TAG = Crypto.class.getSimpleName();

    private static final String CIPHER_ALGORITHM = "AES/CBC/NoPadding";

    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, "AES");

        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, "NoSuchAlgorithmException " + e.getMessage());
            return null;
        } catch (InvalidKeyException e) {
            e.printStackTrace();
            Log.e(TAG, "InvalidKeyException " + e.getMessage());
            return null;
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
            Log.e(TAG, "InvalidAlgorithmParameterException " + e.getMessage());
            return null;
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            Log.e(TAG, "NoSuchPaddingException " + e.getMessage());
            return null;
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
            Log.e(TAG, "IllegalBlockSizeException " + e.getMessage());
            return null;
        } catch (BadPaddingException e) {
            e.printStackTrace();
            Log.e(TAG, "BadPaddingException " + 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, "AES");
        return seafileEncrypt(plaintext, realKey , iv);
    }

IOS 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[8] = { 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 << 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 (&ctx);
    if (version == 2)
        ret = EVP_EncryptInit_ex (&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 (&ctx,
                                  EVP_aes_128_cbc(), /* cipher mode */
                                  NULL, /* engine, NULL for default */
                                  key,  /* derived key */
                                  iv);  /* initial vector */
    else
        ret = EVP_EncryptInit_ex (&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 ("failed to allocate the output buffer.\n");
        goto enc_error;
    }
    int update_len, final_len;

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

    if (ret == ENC_FAILURE)
        goto enc_error;


    /* Finish the possible partial block. */
    ret = EVP_EncryptFinal_ex (&ctx,
                               (unsigned char*)*data_out + update_len,
                               &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 (&ctx);

    return 0;

enc_error:
    EVP_CIPHER_CTX_cleanup (&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[32], iv0[16];
    char passwordPtr[256] = {0}; // room for terminator (unused)
    [password getCString:passwordPtr maxLength:sizeof(passwordPtr) encoding:NSUTF8StringEncoding];
    if (version < 2) {
        [NSData deriveKey:passwordPtr inlen:(int)password.length version:version key:key iv:iv];
        return;
    }
    [NSData deriveKey:passwordPtr inlen:(int)password.length version:version key:key0 iv:iv0];
    char enc_random_key[48], dec_random_key[48];
    int outlen;
    hex_to_rawdata(encKey.UTF8String, enc_random_key, 48);
    [NSData seafileDecrypt:dec_random_key outlen:&outlen datain:(char *)enc_random_key inlen:48 version:2 key:key0 iv:iv0];
    [NSData deriveKey:dec_random_key inlen:32 version:2 key:key iv:iv];
}

- (NSData *)encrypt:(NSString *)password encKey:(NSString *)encKey version:(int)version
{
    uint8_t key[kCCKeySizeAES256+1] = {0}, iv[kCCKeySizeAES128+1];
    [NSData generateKey:password version:version encKey:encKey key:key iv:iv];
    char *data_out;
    int outlen;
    int ret = [NSData seafileEncrypt:&data_out outlen:&outlen datain:self.bytes inlen:(int)self.length version:version key:key iv:iv];
    if (ret < 0) return nil;
    return [NSData dataWithBytesNoCopy:data_out length:outlen];
}

Hier ist das vollständige Projekt für alle, die dies nützlich fanden.

Unterstützung der clientseitigen Verschlüsselung # 487Wie funktioniert eine verschlüsselte Bibliothek?

Antworten auf die Frage(2)

Ihre Antwort auf die Frage