Почему шифротекст 32 байта при шифровании 16 байтов с AES?

Я использую алгоритм шифрования AES, когда я шифрую 16 байт (один блок), результат равен 32 байт. Это нормально?

Мой исходный код, который я использовал:

package net.sf.andhsli.hotspotlogin;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Usage:
 * <pre>
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
 * ...
 * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
 * </pre>
 * @author ferenc.hechler
 */
public class SimpleCrypto {

    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("AES");
        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 final static String HEX = "0123456789ABCDEF";
    private static void appendHex(StringBuffer sb, byte b) {
        sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
    }

}

Ответ 1

Если вы посмотрите раздел 5, то вы увидите, что вход, вывод и состояние все 128 бит. Единственное, что меняется, это размер ключа: 128, 196 или 256 бит. Таким образом, шифрование 16-байтового входного состояния даст 16-байтовое выходное состояние.

Вы уверены, что не смешиваете его с длиной в шестнадцатеричной нотации или аналогичной? Если он в шестнадцатеричной нотации, то он правильный, потому что для каждого байта для его представления необходимы два символа: 00-FF (для диапазона 0-255).

Другой способ проверить правильность шифрования - выполнить эквивалентное дешифрование, посмотреть, совпадает ли он с входной строкой открытого текста.

В любом случае, он делает правильную вещь. Здесь тест:

public static void main(String[] args) {
  try {
    String plaintext = "Hello world", key = "test";
    String ciphertext = encrypt(key, plaintext);
    String plaintext2 = decrypt(key, ciphertext);
    System.out.println("Encrypting '" + plaintext +
                       "' yields: (" + ciphertext.length() + ") " + ciphertext);
    System.out.println("Decrypting it yields: " + plaintext2);
  }
  catch (Exception ex) {
      ex.printStackTrace();
  }
}

Что дает:

Шифрование "Hello world" дает: (32) 5B68978D821FCA6022D4B90081F76B4F

Расшифровка дает: Hello world

Ответ 2

AES по умолчанию использует шифрование в режиме ECB с использованием режима дополнений, совместимого с PKCS # 7 (для всех поставщиков, наблюдаемых до сих пор). Для шифрования режима ECB и CBC требуется заполнить, если вход не является точно кратным размеру блока, а 16 - размер блока AES в байтах.

К сожалению, не может быть никакого способа для разблокирования механизма разграничения между заполнением и данными; сами данные могут представлять допустимое заполнение. Таким образом, для 16 байтов ввода вы получите еще 16 байтов заполнения. Режимы заполнения, которые детерминированы, такие как PKCS # 7, всегда заполняются байтами размером от 1 до [блоков].

Если вы посмотрите int output = cipher.getOutputSize(16);, вы получите 32 байта. Используйте "AES/ECB/NoPadding" во время расшифровки, чтобы увидеть байты заполнения (например, 4D61617274656E20426F64657765732110101010101010101010101010101010).

Вам лучше, когда вы полностью определяете алгоритм (обычно используется "AES/CBC/PKCS5Padding"). В противном случае вы будете продолжать гадать, какой режим фактически используется.

Обратите внимание, что использование режима ECB небезопасно, так как злоумышленник может извлекать информацию из шифрованного текста. Идентичные блоки простого текста кодируются в одинаковые блоки шифрованного текста.

Ответ 3

package com.cipher;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Encrypt {

    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        // TODO Auto-generated method stub
String s="You are doing encryption at deep level";
SecureRandom sr=SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(s.getBytes());
byte[] k=new byte[128/8];
sr.nextBytes(k);
SecretKeySpec spec=new SecretKeySpec(k,"AES");
byte[] iv={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
IvParameterSpec ivs=new IvParameterSpec(iv);
Cipher cps=Cipher.getInstance("AES/CBC/PKCS5Padding");
cps.init(Cipher.ENCRYPT_MODE,spec,ivs);
byte[] iv2=cps.doFinal(s.getBytes());
System.out.println("En"+iv2);
Cipher cpr=Cipher.getInstance("AES/CBC/PKCS5Padding");
cpr.init(Cipher.DECRYPT_MODE, spec,ivs);
byte[] iv3=cpr.doFinal(iv2);
String ds=new String(iv3);
System.out.println(ds);


    }

}