在android中使用AES CTR模式随机访问InputStream

时间:2014-05-19 17:35:25

标签: java android encryption cryptography

我无法找到任何针对随机访问的AES CTR加密的工作示例。有人可以指导我如何在CTR MODE中使用计数器以及如何实现跳转到流中的特定位置?

默认的流实现(CipherInputStream)不会跳过流,它会破坏纯文本。

我正在尝试解密存储在Android中的SD卡上的加密视频文件。嵌入式HTTP文件服务器即时解密它。一切正常,直到用户在视频中执行搜索:视频会立即停止,因为它会收到损坏的视频流。

我正在使用以下代码来初始化和加密/解密流(为了简单起见,我对密钥进行了硬编码。它不会在生产中进行硬编码)

    ByteBuffer bb = ByteBuffer.allocate(16);
    bb.put("1234567891230000".getBytes());
    byte[] ivString = bb.array();

    // INITIALISATION
    String keyString = "1234567812345678";
    IvParameterSpec iv = new IvParameterSpec(ivString);
    SecretKeySpec keySpec = new SecretKeySpec(keyString.getBytes(), "AES");

    // FOR ENCRYPTION
    Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivString));
    Inputstream encrypted_is = new CipherInputStream(in, cipher);

    // FOR DECRYPTION
    cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivString));
            Inputstream decrypted_is = new CipherInputStream(in, cipher);

1 个答案:

答案 0 :(得分:3)

您不应使用流来实现此功能。流用于顺序访问数据。跳绳只应用于向前跳短距离,并标记/重置仅用于跳回小距离。

使用文件map可能效率最高。对于稍微简单的方法但效率较​​低的方法,您可以使用RandomAccessFile代替。您还应该使用" IV" Cipher.getInstance("AES/CTR/NoPadding")设置为您在文件中开始的位置所期望的计数器。


使用CTR与偏移量的示例代码:

private static final int AES_BLOCK_SIZE = 16;

public static final void jumpToOffset(final Cipher c,
        final SecretKey aesKey, final IvParameterSpec iv, final long offset) {
    if (!c.getAlgorithm().toUpperCase().startsWith("AES/CTR")) {
        throw new IllegalArgumentException(
                "Invalid algorithm, only AES/CTR mode supported");
    }

    if (offset < 0) {
        throw new IllegalArgumentException("Invalid offset");
    }

    final int skip = (int) (offset % AES_BLOCK_SIZE);
    final IvParameterSpec calculatedIVForOffset = calculateIVForOffset(iv,
            offset - skip);
    try {
        c.init(Cipher.ENCRYPT_MODE, aesKey, calculatedIVForOffset);
        final byte[] skipBuffer = new byte[skip];
        c.update(skipBuffer, 0, skip, skipBuffer);
        Arrays.fill(skipBuffer, (byte) 0);
    } catch (ShortBufferException | InvalidKeyException
            | InvalidAlgorithmParameterException e) {
        throw new IllegalStateException(e);
    }
}

private static IvParameterSpec calculateIVForOffset(final IvParameterSpec iv,
        final long blockOffset) {
    final BigInteger ivBI = new BigInteger(1, iv.getIV());
    final BigInteger ivForOffsetBI = ivBI.add(BigInteger.valueOf(blockOffset
            / AES_BLOCK_SIZE));

    final byte[] ivForOffsetBA = ivForOffsetBI.toByteArray();
    final IvParameterSpec ivForOffset;
    if (ivForOffsetBA.length >= AES_BLOCK_SIZE) {
        ivForOffset = new IvParameterSpec(ivForOffsetBA, ivForOffsetBA.length - AES_BLOCK_SIZE,
                AES_BLOCK_SIZE);
    } else {
        final byte[] ivForOffsetBASized = new byte[AES_BLOCK_SIZE];
        System.arraycopy(ivForOffsetBA, 0, ivForOffsetBASized, AES_BLOCK_SIZE
                - ivForOffsetBA.length, ivForOffsetBA.length);
        ivForOffset = new IvParameterSpec(ivForOffsetBASized);
    }

    return ivForOffset;
}