Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/221.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java CBC-MAC AES自身的实现极其缓慢_Java_Android_Performance_Encryption_Cbc Mac - Fatal编程技术网

Java CBC-MAC AES自身的实现极其缓慢

Java CBC-MAC AES自身的实现极其缓慢,java,android,performance,encryption,cbc-mac,Java,Android,Performance,Encryption,Cbc Mac,对于一个项目,我需要在Android(使用java)中实现一个函数,该函数从一个文件生成CBC-MAC(AES)。因此,基本上该函数从文件中获取不同的“块”,并计算每个块的标识符,最后将其合并为整个文件的标识符 该函数工作得很好,但是,对于较大的文件,由于实现了循环,它的速度非常慢(可能需要几分钟到几小时)。然而,我对密码学的了解还不多,所以我不确定如何提高速度,或者是否可能。输出的CBC-MAC与不同编程语言中的其他库完全相同,因此工作正常 不幸的是,我在使用外部库方面非常有限。。虽然boun

对于一个项目,我需要在Android(使用java)中实现一个函数,该函数从一个文件生成CBC-MAC(AES)。因此,基本上该函数从文件中获取不同的“块”,并计算每个块的标识符,最后将其合并为整个文件的标识符

该函数工作得很好,但是,对于较大的文件,由于实现了循环,它的速度非常慢(可能需要几分钟到几小时)。然而,我对密码学的了解还不多,所以我不确定如何提高速度,或者是否可能。输出的CBC-MAC与不同编程语言中的其他库完全相同,因此工作正常

不幸的是,我在使用外部库方面非常有限。。虽然bouncycastle中的CBCBlockCipherMac类是可能的,因为我只能够包含几个依赖项,但从未让它提供与下面提到的函数相同的输出

欢迎所有反馈,我已经试着解决了三天了,但还是没办法解决。谢谢

*更新 似乎for循环中的函数str_to_a32(每16个字节循环一次)造成了最大的速度问题。因此,如果该功能可以更快,它将主要解决问题。 此外,不幸的是,每16个字节的循环是必要的,因为我正在实现与云提供商Mega同样的CBC-MAC功能

代码

        //TEST IMPLEMENTATION

    String _path_to_file = "";

    Random _random = new Random();
    long[] _key_file = new long[4];
    _key_file[0] = _random.nextInt(Integer.MAX_VALUE);
    _key_file[1] = _random.nextInt(Integer.MAX_VALUE);
    _key_file[2] = _random.nextInt(Integer.MAX_VALUE);
    _key_file[3] = _random.nextInt(Integer.MAX_VALUE);

    long[] _iv_file = new long[4];
    _iv_file[0] = _random.nextInt(Integer.MAX_VALUE);
    _iv_file[1] = _random.nextInt(Integer.MAX_VALUE);
    _iv_file[2] = 0;
    _iv_file[3] = 0;

    long[] _returned = cbc_mac(_path_to_file, _key_file, _iv_file);


//FUNCTIONS

//this function loops over the parts of the file to calculate the cbc-mac and is the problem
public static long[] cbc_mac(String _path, long[] k, long[] n) throws Exception {
    File _file = new File(_path);
    long _file_length = _file.length();
    RandomAccessFile _raf = new RandomAccessFile(_file, "r");

    //This works fine and fast
    ArrayList<chunksData> chunks = get_chunks(_file_length);

    long[] file_mac = new long[4];
    file_mac[0] = 0;
    file_mac[1] = 0;
    file_mac[2] = 0;
    file_mac[3] = 0;

    //prepare encrypt
    String iv = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
    IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
    SecretKeySpec keySpec = new SecretKeySpec(a32_to_str(k).getBytes("ISO-8859-1"), "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
    //end prepare encrypt

    for(chunksData _chunksData : chunks) {

        int pos = (int)_chunksData._key;
        int size = (int)_chunksData._value;

        long[] chunk_mac = new long[4];
        chunk_mac[0] = n[0];
        chunk_mac[1] = n[1];
        chunk_mac[2] = n[0];
        chunk_mac[3] = n[1];

        byte[] bytes = new byte[16];

        //this loop is the really slow part since it loops over every 16 bytes
        for (int i = pos; i < pos + size; i += 16) {
            _raf.seek(i);
            int _did_read = _raf.read(bytes, 0, 16);
            if(_did_read != 16) {
                for(int o = _did_read;o<16;o++) {
                    bytes[o] = (byte)((char)'\0');
                }
            }

            long[] block = str_to_a32(new String(bytes, "ISO-8859-1"));

            chunk_mac[0] = chunk_mac[0] ^ block[0];
            chunk_mac[1] = chunk_mac[1] ^ block[1];
            chunk_mac[2] = chunk_mac[2] ^ block[2];
            chunk_mac[3] = chunk_mac[3] ^ block[3];

            chunk_mac = str_to_a32(new String(cipher.doFinal(a32_to_str(chunk_mac).getBytes("ISO-8859-1")), "ISO-8859-1"));

        }

        file_mac[0] = file_mac[0] ^ chunk_mac[0];
        file_mac[1] = file_mac[1] ^ chunk_mac[1];
        file_mac[2] = file_mac[2] ^ chunk_mac[2];
        file_mac[3] = file_mac[3] ^ chunk_mac[3];
        file_mac = str_to_a32(new String(cipher.doFinal(a32_to_str(file_mac).getBytes("ISO-8859-1")), "ISO-8859-1"));

    }

    _raf.close();

    return file_mac;

}

//this function works fine and fast
public static ArrayList<chunksData> get_chunks(long size) {

    ArrayList<chunksData> chunks = new ArrayList<chunksData>();

    long p = 0;
    long pp = 0;

    for (int i = 1; i <= 8 && p < size - i * 0x20000; i++) {
        chunksData chunks_temp = new chunksData(p, i*0x20000);
        chunks.add(chunks_temp);
        pp = p;
        p += chunks_temp._value;
    }

    while(p < size) {
        chunksData chunks_temp = new chunksData(p, 0x100000);
        chunks.add(chunks_temp);
        pp = p;
        p += chunks_temp._value;            
    }

    chunks.get(chunks.size()-1)._value = size-pp;
    if((int)chunks.get(chunks.size()-1)._value == 0) {
        chunks.remove(chunks.size()-1);
    }

    return chunks;

}

public static class chunksData {
    public long _key = 0;
    public long _value = 0;
    public chunksData(long _keyT, long _valueT){
        this._key = _keyT;
        this._value = _valueT;
    }
}

//helper function which also contains a loop and is used in the problematic loop, so might be a problem though I don't know how to speed it up
public static long[] str_to_a32(String string) {
    if (string.length() % 4 != 0) {
        string += new String(new char[4 - string.length() % 4]);
    }
    long[] data = new long[string.length() / 4];

    byte[] part = new byte[8];
    for (int k = 0, i = 0; i < string.length(); i += 4, k++) {
        String sequence = string.substring(i, i + 4);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(sequence.getBytes("ISO-8859-1"));
            System.arraycopy(baos.toByteArray(), 0, part, 4, 4);
            ByteBuffer bb = ByteBuffer.wrap(part);
            data[k] = bb.getLong();
        } catch (IOException e) {
            data[k] = 0;
        }
    }
    return data;
}

//helper function which also contains a loop and is used in the problematic loop, so might be a problem though I don't know how to speed it up
public static String a32_to_str(long[] data) {
    byte[] part = null;
    StringBuilder builder = new StringBuilder();
    ByteBuffer bb = ByteBuffer.allocate(8);
    for (int i = 0; i < data.length; i++) {
        bb.putLong(data[i]);
        part = copyOfRange(bb.array(), 4, 8);
        bb.clear();
        ByteArrayInputStream bais = new ByteArrayInputStream(part);
        while (bais.available() > 0) {
            builder.append((char) bais.read());
        }
    }
    return builder.toString();
}
//测试实现
字符串_path_to_file=“”;
随机_Random=新随机();
long[]_key_file=new long[4];
_key\u file[0]=\u random.nextInt(Integer.MAX\u值);
_key_file[1]=_random.nextInt(Integer.MAX_值);
_key_file[2]=_random.nextInt(Integer.MAX_值);
_key_file[3]=_random.nextInt(Integer.MAX_值);
long[]_iv_文件=新long[4];
_iv_文件[0]=_random.nextInt(Integer.MAX_值);
_iv_文件[1]=_random.nextInt(Integer.MAX_值);
_iv_文件[2]=0;
_iv_文件[3]=0;
long[]_returned=cbc_mac(_path_to_file,_key_file,_iv_file);
//功能
//此函数循环文件的各个部分以计算cbc mac,这就是问题所在
公共静态长[]cbc_mac(字符串_路径,长[]k,长[]n)引发异常{
文件_File=新文件(_路径);
long_file_length=_file.length();
RandomAccessFile _raf=新的RandomAccessFile(_file,“r”);
//这工作又好又快
ArrayList chunks=获取块(文件长度);
long[]file_mac=new long[4];
文件_mac[0]=0;
文件\u mac[1]=0;
文件_mac[2]=0;
文件_mac[3]=0;
//准备加密
字符串iv=“\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0”;
IvParameterSpec ivSpec=新的IvParameterSpec(iv.getBytes());
SecretKeySpec keySpec=新的SecretKeySpec(a32_至_str(k).getBytes(“ISO-8859-1”),“AES”);
Cipher Cipher=Cipher.getInstance(“AES/CBC/NOPADDING”);
cipher.init(cipher.ENCRYPT_模式,keySpec,ivSpec);
//结束准备加密
for(chunksData\u chunksData:chunks){
int pos=(int)\u chunksData.\u键;
int size=(int)\u chunksData.\u值;
long[]chunk_mac=new long[4];
chunk_mac[0]=n[0];
chunk_mac[1]=n[1];
chunk_mac[2]=n[0];
chunk_mac[3]=n[1];
字节[]字节=新字节[16];
//这个循环非常慢,因为它每16个字节循环一次
对于(int i=pos;i对于(int o=_did_read;o我的主要怀疑是第一个循环中的seek操作,只处理16个字节。我不知道算法,但您的代码表明可以读取完整的“chunk”,然后您就可以处理它,因为部分是必要的

而且,区块似乎是连续的(除非我错过了一些东西),所以整个阅读可以在没有搜索的情况下连续完成

在助手方法中不需要ByteArrayOutput流。同时,生成子字符串也会产生影响,因此对整个字符串调用toBytes,然后提取字节数组的部分将更有效

下面的代码大约比原始代码快两倍

public long[] fast_str_to_a32(String string) throws UnsupportedEncodingException {
    if (string.length() % 4 != 0) {
        string += new String(new char[4 - string.length() % 4]);
    }
    long[] data = new long[string.length() / 4];

    byte[] bytes = string.getBytes("ISO-8859-1");

    byte[] part = new byte[8];
    ByteBuffer bb = ByteBuffer.wrap(part); 
    for (int k = 0, i = 0; i < bytes.length; i += 4, k++) {
        System.arraycopy(bytes, i, part, 4, 4);
        bb.rewind();
        data[k] = bb.getLong();
    }
    return data;
}
public long[]fast\u str\u to\u a32(字符串字符串)引发不支持的编码异常{
if(string.length()%4!=0){
字符串+=新字符串(新字符[4-string.length()%4]);
}
long[]data=new long[string.length()/4];
byte[]bytes=string.getBytes(“ISO-8859-1”);
字节[]部分=新字节[8];
ByteBuffer bb=ByteBuffer.wrap(零件);
for(int k=0,i=0;i
同样,在main方法中,您将字节转换为字符串,以便在str_to_a32的开头将它们转换回byte[],您应该只使用byte[]作为此方法的输入

我仍然认为您应该一次读取整个块,然后以16字节为一组进行处理


您的代码中可能存在一个问题:您尝试读取16个字节,但如果读取的字节数较少,则开始填充。但是,读取的约定是“尝试读取多达len个字节的字节数,但可能读取较小的字节数。”通常,在文件末尾发生的数字较小,但原则上它随时都可能发生。如果是这样,您将开始在流的中间填充并完全弄乱您的部分。

您为每个单独的文件块计算一个MAC。为什么不为整个文件只做一个MAC?该文件是否被作为一个集合O来处理?f块还是始终将其视为单个实体?只需使用不同的密钥和零IV对AES-CBC中的文件进行加密。CBC加密的最后一块是整个文件的MAC。@rossum感谢您的想法,但是,更具体地说,我正在实现云提供的CBC-MAC功能