gpt4 book ai didi

java - winzipaes 在 Android 上解密 10 MB 的文件很慢

转载 作者:太空宇宙 更新时间:2023-11-03 13:20:59 29 4
gpt4 key购买 nike

我试图在 Samsung S5 上用 AES 加密从 zip 文件中解密一个 10 MB 的文件,但速度太慢了,这让我很吃惊。我对AES很熟悉,所以我不知道它是否消耗了很多时间。以下是我的测试结果。谁能告诉我这些结果是否合理?

有没有加速 AES 解密的方法?

附言。我使用 SpongyCaSTLe 来避免类加载器冲突,我还修改了 winzipaes 以使用 SpongyCaSTLe。

测试 1
设备:三星S5
压缩包:7za a -tzip -mx=0 -p1234 -mem=AES256 test.zip 1MB_file 10MB_file
1MB_文件:1 MB
10MB_文件:10 MB
测试.zip:12.5 MB
压缩率:1.00
解密解压:
--> 1MB_文件:3167 毫秒
--> 10MB_文件:34137 毫秒

测试 2
设备:三星S5
压缩包:7za a -tzip -mx=1 -p1234 -mem=AES256 test.zip 1MB_file 10MB_file
1MB_文件:1 MB
10MB_文件:10 MB
test.zip:5.4 MB
压缩率:2.31
解密解压:
--> 1MB_文件:1290 毫秒
--> 10MB_文件:15369 毫秒

测试 3
设备:三星S5
压缩包:7za a -tzip -mx=9 -p1234 -mem=AES256 test.zip 1MB_file 10MB_file
1MB_文件:1 MB
10MB_文件:10 MB
test.zip:5.1 MB
压缩率:2.46
解密解压:
--> 1MB_文件:1202 毫秒
--> 10MB_文件:14460 毫秒

winzipaes:https://code.google.com/p/winzipaes/
海绵城堡:http://rtyley.github.io/spongycastle/

============================================= ===================================

使用@Maarten Bodewes - owlstead 解决方案

测试 2
设备:三星S5
压缩包:7za a -tzip -mx=1 -p1234 -mem=AES256 test.zip 1MB_file 10MB_file
1MB_文件:1 MB
10MB_文件:10 MB
test.zip:5.4 MB
压缩率:2.31
解密解压:
--> 1MB_file:206 毫秒(原为 1290 毫秒)
--> 10MB_file:1782 毫秒(原为 15369 毫秒)

最佳答案

是的,有一些方法可以加快这个速度,因为 winzipaes 的源代码使用了一种相当低效的解密方式:它通过计算 IV 和初始化密码(用于 CTR 模式解密)来解密每个 block 。这可能意味着 key 重新初始化过于频繁。此外,以 16 字节为单位处理数据也不是很有效。这主要是因为 WinZip 执行的 AES-CTR 使用小端计数器而不是大端计数器(作为标准)。

解密似乎还包括在密文上计算 HMAC-SHA1,这也会增加大量开销。如果您只需要存储文本的 secret 性,那么您可以跳过该步骤,尽管 MAC 确实具有显着的安全优势,提供加密安全完整性和真实性。

为了说明我的意思,我创建了一个小示例代码,它至少在我的 Java SE 机器上运行得更快。根据 Wayne(原始发布者)的说法,这将 Android 代码的速度提高了 10 倍左右,在我的 Java SE 测试中,我“只”看到了大约 3 倍于原始代码的速度。

变化:

  • 创建了用于 ZIP 的特殊小端计数器模式
  • 由于上述原因简化/优化解密器代码
  • 删除了每个文件的双 key 派生(D'oh!)
  • 选择不验证 MAC(返回相对较小,SHA1 非常快)
  • 使用AESFastEngine,没关系,但是嘿...

很可能可以为加密器创建相同类型的优化。

注意事项:

  • .zip 加密是每个存储的文件,因此效率相当低,因为 key 派生也需要对每个存储的文件进行一次。 .zip 文件本身的加密会更有效率。
  • 使用 JCA 版本的解密器可能会提速,而且 Android 可能会在更高版本中使用 OpenSSL 代码(尽管它必须逐 block 加密)。

-

/**
* Adapter for bouncy castle crypto implementation (decryption).
*
* @author olaf@merkert.de
* @author owlstead
*/
public class AESDecrypterOwlstead extends AESCryptoBase implements AESDecrypter {


private final boolean verify;

public AESDecrypterOwlstead(boolean verify) {
this.verify = verify;
}

// TODO consider keySize (but: we probably need to adapt the key size for the zip file as well)
public void init( String pwStr, int keySize, byte[] salt, byte[] pwVerification ) throws ZipException {
byte[] pwBytes = pwStr.getBytes();

super.saltBytes = salt;

PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
generator.init( pwBytes, salt, ITERATION_COUNT );

cipherParameters = generator.generateDerivedParameters(KEY_SIZE_BIT*2 + 16);
byte[] keyBytes = ((KeyParameter)cipherParameters).getKey();

this.cryptoKeyBytes = new byte[ KEY_SIZE_BYTE ];
System.arraycopy( keyBytes, 0, cryptoKeyBytes, 0, KEY_SIZE_BYTE );

this.authenticationCodeBytes = new byte[ KEY_SIZE_BYTE ];
System.arraycopy( keyBytes, KEY_SIZE_BYTE, authenticationCodeBytes, 0, KEY_SIZE_BYTE );

// based on SALT + PASSWORD (password is probably correct)
this.pwVerificationBytes = new byte[ 2 ];
System.arraycopy( keyBytes, KEY_SIZE_BYTE*2, this.pwVerificationBytes, 0, 2 );

if( !ByteArrayHelper.isEqual( this.pwVerificationBytes, pwVerification ) ) {
throw new ZipException("wrong password - " + ByteArrayHelper.toString(this.pwVerificationBytes) + "/ " + ByteArrayHelper.toString(pwVerification));
}

cipherParameters = new KeyParameter(cryptoKeyBytes);

// checksum added to the end of the encrypted data, update on each encryption call

if (this.verify) {
this.mac = new HMac( new SHA1Digest() );
this.mac.init( new KeyParameter(authenticationCodeBytes) );
}

this.aesCipher = new SICZIPBlockCipher(new AESFastEngine());
this.blockSize = aesCipher.getBlockSize();

// incremented on each 16 byte block and used as encryption NONCE (ivBytes)

// warning: non-CTR; little endian IV and starting with 1 instead of 0

nonce = 1;

byte[] ivBytes = ByteArrayHelper.toByteArray( nonce, 16 );

ParametersWithIV ivParams = new ParametersWithIV(cipherParameters, ivBytes);
aesCipher.init( false, ivParams );
}

// --------------------------------------------------------------------------

protected CipherParameters cipherParameters;

protected SICZIPBlockCipher aesCipher;

protected HMac mac;


@Override
public void decrypt(byte[] in, int length) {
if (verify) {
mac.update(in, 0, length);
}
aesCipher.processBytes(in, 0, length, in, 0);
}

public byte[] getFinalAuthentication() {
if (!verify) {
return null;
}
byte[] macBytes = new byte[ mac.getMacSize() ];
mac.doFinal( macBytes, 0 );
byte[] macBytes10 = new byte[10];
System.arraycopy( macBytes, 0, macBytes10, 0, 10 );
return macBytes10;
}
}

当然你还需要引用的SICZIPCipher:

/**
* Implements the Segmented Integer Counter (SIC) mode on top of a simple
* block cipher. This mode is also known as CTR mode. This CTR mode
* was altered to comply with the ZIP little endian counter and
* different starting point.
*/
public class SICZIPBlockCipher
extends StreamBlockCipher
implements SkippingStreamCipher
{
private final BlockCipher cipher;
private final int blockSize;

private byte[] IV;
private byte[] counter;
private byte[] counterOut;
private int byteCount;

/**
* Basic constructor.
*
* @param c the block cipher to be used.
*/
public SICZIPBlockCipher(BlockCipher c)
{
super(c);

this.cipher = c;
this.blockSize = cipher.getBlockSize();
this.IV = new byte[blockSize];
this.counter = new byte[blockSize];
this.counterOut = new byte[blockSize];
this.byteCount = 0;
}

public void init(
boolean forEncryption, //ignored by this CTR mode
CipherParameters params)
throws IllegalArgumentException
{
if (params instanceof ParametersWithIV)
{
ParametersWithIV ivParam = (ParametersWithIV)params;
byte[] iv = ivParam.getIV();
System.arraycopy(iv, 0, IV, 0, IV.length);

// if null it's an IV changed only.
if (ivParam.getParameters() != null)
{
cipher.init(true, ivParam.getParameters());
}

reset();
}
else
{
throw new IllegalArgumentException("SICZIP mode requires ParametersWithIV");
}
}

public String getAlgorithmName()
{
return cipher.getAlgorithmName() + "/SICZIP";
}

public int getBlockSize()
{
return cipher.getBlockSize();
}

public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
throws DataLengthException, IllegalStateException
{
processBytes(in, inOff, blockSize, out, outOff);

return blockSize;
}

protected byte calculateByte(byte in)
throws DataLengthException, IllegalStateException
{
if (byteCount == 0)
{
cipher.processBlock(counter, 0, counterOut, 0);

return (byte)(counterOut[byteCount++] ^ in);
}

byte rv = (byte)(counterOut[byteCount++] ^ in);

if (byteCount == counter.length)
{
byteCount = 0;

incrementCounter();
}

return rv;
}

private void incrementCounter()
{
// increment counter by 1.
for (int i = 0; i < counter.length && ++counter[i] == 0; i++)
{
; // do nothing - pre-increment and test for 0 in counter does the job.
}
}

private void decrementCounter()
{
// TODO test - owlstead too lazy to test

if (counter[counter.length - 1] == 0)
{
boolean nonZero = false;

for (int i = 0; i < counter.length; i++)
{
if (counter[i] != 0)
{
nonZero = true;
}
}

if (!nonZero)
{
throw new IllegalStateException("attempt to reduce counter past zero.");
}
}

// decrement counter by 1.
for (int i = 0; i < counter.length && --counter[i] == -1; i++)
{
;
}
}

private void adjustCounter(long n)
{
if (n >= 0)
{
long numBlocks = (n + byteCount) / blockSize;

for (long i = 0; i != numBlocks; i++)
{
incrementCounter();
}

byteCount = (int)((n + byteCount) - (blockSize * numBlocks));
}
else
{
long numBlocks = (-n - byteCount) / blockSize;

for (long i = 0; i != numBlocks; i++)
{
decrementCounter();
}

int gap = (int)(byteCount + n + (blockSize * numBlocks));

if (gap >= 0)
{
byteCount = 0;
}
else
{
decrementCounter();
byteCount = blockSize + gap;
}
}
}

public void reset()
{
System.arraycopy(IV, 0, counter, 0, counter.length);
cipher.reset();
this.byteCount = 0;
}

public long skip(long numberOfBytes)
{
adjustCounter(numberOfBytes);

cipher.processBlock(counter, 0, counterOut, 0);

return numberOfBytes;
}

public long seekTo(long position)
{
reset();

return skip(position);
}

public long getPosition()
{
byte[] res = new byte[IV.length];

System.arraycopy(counter, 0, res, 0, res.length);

for (int i = 0; i < res.length; i++)
{
int v = (res[i] - IV[i]);

if (v < 0)
{
res[i + 1]--;
v += 256;
}

res[i] = (byte)v;
}

// TODO still broken - owlstead too lazy to fix for zip
return Pack.bigEndianToLong(res, res.length - 8) * blockSize + byteCount;
}
}

关于java - winzipaes 在 Android 上解密 10 MB 的文件很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27703709/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com