AES 128 Реализация с предопределенным ключом - PullRequest
1 голос
/ 12 сентября 2011

Я пытаюсь реализовать алгоритм AES128 на Android, и я ссылался на эту ссылку для базовой реализации AES (http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html).

Проблема в том, что для моего проекта ключ предопределен, и это 36 байтов, а не 16/24/32 байтов. Поэтому я всегда получал исключение «длина ключа не 128/194/256 бит». Я пробую решение от iphone sdk (см. Эту ссылку: Структура шифрования iOS ), и оно работает, даже когда я передаю 36-байтовый предопределенный ключ. Поскольку я не могу найти подробности реализации BlockCipher.c / CommonCryptor.c, выпущенного Apple, может ли какой-либо орган помочь мне выяснить, как они выбирают 16 байтов из 36 байтов?

Спасибо.

----------------------------------- обновление от 13 сентября --------- --------------------------- Чтобы избежать путаницы, я привожу пример и мой прогресс. Я изменяю некоторые данные, которые являются конфиденциальными, но длина и формат остаются неизменными. А для экономии времени я раскрываю только основные функции. Нет комментариев к коду, так как я думаю, что код достаточно понятен.

пример iOS:

NSString * _key = @"some 36 byte key";
StringEncryption *crypto = [[[StringEncryption alloc] init] autorelease]; 
NSData *_inputData = [inputString dataUsingEncoding:NSUTF8StringEncoding]; 
CCOptions padding = kCCOptionPKCS7Padding;
NSData *encryptedData = [crypto encrypt:_inputData key:[_key dataUsingEncoding:NSUTF8StringEncoding] padding:&padding];
NSString *encryptedString = [encryptedData base64EncodingWithLineLength:0];
return encryptedString;

реализация [crypto encrypt] точно такая же, как ссылка, которую я упомянул выше. Он вызывает doCipher в режиме шифрования. Основные функции включают CCCryptorCreate, CCCryptorUpdate и CCCryptorFinal, которые взяты из. CCCryptorCreate имеет дело с длиной ключа. Он передает необработанные байты ключа и передает целое число 16 (kCCKeySizeAES128) в качестве размера ключа и делает свое дело. Иерархия вызовов похожа на CCCryptorCreate / CommonCryptor.c => ccBlockCipherCallouts-> CCBlockCipherInit / BlockCipher.c => ccAlgInfo-> setkey / BlockCipher.c. На самом деле setkey - это указатель на функцию, для AES он указывает на aes_cc_set_key. И не могу найти реализацию aes_cc_set_key, потерялся здесь.

---------------------------------------- обновление 13 сентября ---- ------------------------- Я изменяю _key в образце кода iOS, вручную беря первые 16 байт в качестве нового ключа, остальные части остаются прежними, и он работает !!! До этого момента я решал проблему длины ключа.

Но версия Android отличается от версии iOS для некоторого длинного простого текста, такого как 30 или 40 байт. моя реализация Java похожа на приведенную ниже:

String key = "some 16 byte key";
byte[] keyBytes = key.getBytes("UTF-8");
byte[] plainBytes = plainText.getBytes("UTF-8");
SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(plainBytes);
String result = Base64.encodeBytes(encrypted);
return result;

Base64 от org.apache.commons.codec.binary.Base64. В чем проблема? или какие-либо советы по библиотекам c / c ++, которые могут сделать то же самое? Я могу импортировать его и в Android.

Ответы [ 4 ]

5 голосов
/ 13 сентября 2011

Оставшаяся разница (при условии, что вы использовали только первые 16 байтов ключа) - это режим потокового шифрования.Код iOS использует режим CBC с инициализацией, установленной на все нули.Однако в коде Android используется ECB.

Итак, правильный код Java / Android:

// convert key to bytes
byte[] keyBytes = key.getBytes("UTF-8");
// Use the first 16 bytes (or even less if key is shorter)
byte[] keyBytes16 = new byte[16];
System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, 16));

// convert plain text to bytes
byte[] plainBytes = plainText.getBytes("UTF-8");

// setup cipher
SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[16]; // initialization vector with all 0
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));

// encrypt
byte[] encrypted = cipher.doFinal(plainBytes);

Я протестировал его с примерно 100 байтами данных и получил точно такой же результат на iOS ина Java.

1 голос
/ 12 сентября 2011

36 байтов на самом деле являются парольной фразой?Если это так, то вероятно, что используемый ключ - это SHA-256 (кодовая фраза) или SHA-512 (кодовая фраза).

ETA:
Повторное обновление.Я отмечаю, что ваш код использует режим ECB.Это небезопасно.Вполне возможно, что Apple использует режим CBC, поэтому вам сложно расшифровывать более длинные (более 16 байт) сообщения.Попробуйте изменить режим на CBC и использовать еще 16 байтов вашего таинственного ввода в качестве IV.Глядя на код Apple для CommonCryptor.c , они, похоже, используют заполнение PKCS7, так что вы должны использовать это также.

1 голос
/ 12 сентября 2011

Нет такой вещи, как 36-битный (288 бит) ключ AES.AES 256 будет использовать 32-байтовый ключ, так что, возможно, это то, что у вас есть, с некоторыми дополнительными байтами заголовка / трейлера.Откуда вы взяли этот ключ?Какой формат?Реализация Apple может отбрасывать ненужные байты или она уже знает о том специальном формате, который вы используете.

0 голосов
/ 29 сентября 2013

Если вы хотите применить кодировку base64 для передачи по сети, это правильный код:

public String encryptString(String string, String key)
{
    byte[] aesData;
    String base64="";

    try 
    {
        aesData = encrypt(key, string.getBytes("UTF8"));
        base64 = Base64.encodeToString(aesData, Base64.DEFAULT);
    } 
    catch (Exception e) 
    {
        e.printStackTrace();
    }       

    return base64;
}

public String decryptString(String string, String key)
{
    byte[] debase64 = null;
    String result="";

    try 
    {
        debase64=Base64.decode(string, Base64.DEFAULT);
        byte[] aesDecrypted = decrypt(key, debase64);;

        result = new String(aesDecrypted, "UTF8");
    } 
    catch (Exception e) 
    {
        e.printStackTrace();
    }       

    return result;
}

private byte[] decrypt(String k, byte[] plainBytes) throws Exception 
{
    // convert key to bytes
    byte[] keyBytes = k.getBytes("UTF-8");
    // Use the first 16 bytes (or even less if key is shorter)
    byte[] keyBytes16 = new byte[16];
    System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, 16));

    // setup cipher
    SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] iv = new byte[16]; // initialization vector with all 0
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(iv));

    // encrypt
    byte[] encrypted = cipher.doFinal(plainBytes);

    return encrypted;
}

private byte[] encrypt(String k, byte[] plainBytes) throws Exception 
{
    // convert key to bytes
    byte[] keyBytes = k.getBytes("UTF-8");
    // Use the first 16 bytes (or even less if key is shorter)
    byte[] keyBytes16 = new byte[16];
    System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, 16));

    // setup cipher
    SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] iv = new byte[16]; // initialization vector with all 0
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));

    // encrypt
    byte[] encrypted = cipher.doFinal(plainBytes);

    return encrypted;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...