android AES/CTR/NoPadding 中的解密、密码问题

当我在 android Marshmallow(Android 6.0.1) 上使用此代码时,解密正常,但是当我在带有 android Oreo(Android 8) 的设备上运行时,解密值不相同并且数据不正确。


private void decrypt(Cipher cipher, Uri uri) throws Exception {

    long a = 113845229;

    InputStream inputStream = getContentResolver().openInputStream(uri);

    CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);

    cipherInputStream.skip(a);

    byte[] buffer = new byte[8];

    cipherInputStream.read(buffer);

}


// create cipher

private Cipher createCipher(byte[] iv, byte[] salt, String password) throws Exception {

    IvParameterSpec mIvParameterSpec = new IvParameterSpec(iv);

    SecretKeySpec mSecretKeySpec = generate(password, salt);

    Cipher mCipher = Cipher.getInstance("AES/CTR/NoPadding");

    mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, mIvParameterSpec);

    return mCipher;

}

// generate key

private SecretKeySpec generate(String password, byte[] salt) throws Exception {

    MessageDigest md = MessageDigest.getInstance("SHA-256");

    md.update(salt);

    byte[] key = md.digest(password.getBytes(StandardCharsets.UTF_8));

    return new SecretKeySpec(key, "AES");

}

缓冲区数据在 android 6 中正常,但在 android 8 中数据不正确。


jeck猫
浏览 210回答 2
2回答

皈依舞

我相信您正在寻找对 ctr 加密数据的随机访问;CipherInputStream 中的 Skip 方法只是不这样做,并且是“独立于 Android 版本”(仍在使用;自 api 级别1以来未弃用或替换!);查看 CipherInputStream 类文件;它有一些内部属性:private Cipher cipher;//the cipher you pass to constructor;// the underlying input streamprivate InputStream input;/* the buffer holding data that have been read in from the   underlying stream, but have not been processed by the cipher   engine. the size 512 bytes is somewhat randomly chosen */private byte[] ibuffer = new byte[512];//holds encrypted data// having reached the end of the underlying input streamprivate boolean done = false;/* the buffer holding data that have been processed by the cipher   engine, but have not been read out */private byte[] obuffer;//a portion of data that's decrypted but not yet read;// the offset pointing to the next "new" byteprivate int ostart = 0;// the offset pointing to the last "new" byteprivate int ofinish = 0;这就是 skip 在 CipherInputStream 中所做的;public long skip(long n) throws IOException {    int available = ofinish - ostart;    if (n > available) {        n = available;    }    if (n < 0) {        return 0;    }    ostart += n;    return n;}它不会加载新数据到 obuffer 或 ibuffer;它只跳过 obuffer 中可用的内容(只是增加 ostart);应该这样做(有改进的余地):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;}long offset = 113845229;// aka aprivate void decrypt(Cipher cipher, Uri uri) throws Exception {    long skip_this_much=offset-(offset%16);    InputStream inputStream = getContentResolver().openInputStream(uri);    do{        skip_this_much=skip_this_much-inputStream.skip(skip_this_much);//InputStream.skip does not necessarily skip as much as specified in parameter and returns the actually skipped value;    }while(skip_this_much!=0);//not there yet; keep skipping;    CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);    int read_this_much=8;    byte[] buffer=new byte[read_this_much+(offset%16)];    cipherInputStream.read(buffer,0,read_this_much+(offset%16));//improve this yourself    buffer= Arrays.copyOfRange(buffer,offset%16,read_this_much+(offset%16));}// create cipher for offsetprivate Cipher createCipher(byte[] iv, byte[] salt, String password) throws Exception {    IvParameterSpec mIvParameterSpec = new IvParameterSpec(iv);    SecretKeySpec mSecretKeySpec = generate(password, salt);    Cipher mCipher = Cipher.getInstance("AES/CTR/NoPadding");    mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, calculateIVForOffset(mIvParameterSpec,offset));    return mCipher;}// generate keyprivate SecretKeySpec generate(String password, byte[] salt) throws Exception {    MessageDigest md = MessageDigest.getInstance("SHA-256");    md.update(salt);    byte[] key = md.digest(password.getBytes(StandardCharsets.UTF_8));    return new SecretKeySpec(key, "AES");}

红颜莎娜

经过研究我得出结论。你应该用特定的密码实现 InputStream。private static final int AES_BLOCK_SIZE = 16;private InputStream mUpstream;private Cipher mCipher;private SecretKeySpec mSecretKeySpec;private IvParameterSpec mIvParameterSpec;public StreamingCipherInputStream(InputStream inputStream, Cipher cipher,&nbsp;&nbsp; &nbsp; SecretKeySpec secretKeySpec, IvParameterSpec ivParameterSpec) {&nbsp; &nbsp; super(inputStream, cipher);&nbsp; &nbsp; mUpstream = inputStream;&nbsp; &nbsp; mCipher = cipher;&nbsp; &nbsp; mSecretKeySpec = secretKeySpec;&nbsp; &nbsp; mIvParameterSpec = ivParameterSpec; }@Overridepublic int read(byte[] b, int off, int len) throws IOException {&nbsp; &nbsp; return super.read(b, off, len);&nbsp; }public long forceSkip(long bytesToSkip) throws IOException {&nbsp; &nbsp; long skipped = mUpstream.skip(bytesToSkip);&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; int skip = (int) (bytesToSkip % AES_BLOCK_SIZE);&nbsp; &nbsp; &nbsp; &nbsp; long blockOffset = bytesToSkip - skip;&nbsp; &nbsp; &nbsp; &nbsp; long numberOfBlocks = blockOffset / AES_BLOCK_SIZE;&nbsp; &nbsp; &nbsp; &nbsp; BigInteger ivForOffsetAsBigInteger = new BigInteger(1,&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; mIvParameterSpec.getIV()).add(BigInteger.valueOf(numberOfBlocks));&nbsp; &nbsp; &nbsp; &nbsp; byte[] ivForOffsetByteArray = ivForOffsetAsBigInteger.toByteArray();&nbsp; &nbsp; &nbsp; &nbsp; IvParameterSpec computedIvParameterSpecForOffset;&nbsp; &nbsp; &nbsp; &nbsp; if (ivForOffsetByteArray.length < AES_BLOCK_SIZE) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; byte[] resizedIvForOffsetByteArray = new byte[AES_BLOCK_SIZE];&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.arraycopy(ivForOffsetByteArray, 0, resizedIvForOffsetByteArray,&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AES_BLOCK_SIZE - ivForOffsetByteArray.length, ivForOffsetByteArray.length);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; computedIvParameterSpecForOffset = new IvParameterSpec(resizedIvForOffsetByteArray);&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; computedIvParameterSpecForOffset = new IvParameterSpec(ivForOffsetByteArray, ivForOffsetByteArray.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec, computedIvParameterSpecForOffset);&nbsp; &nbsp; &nbsp; &nbsp; byte[] skipBuffer = new byte[skip];&nbsp; &nbsp; &nbsp; &nbsp; mCipher.update(skipBuffer, 0, skip, skipBuffer);&nbsp; &nbsp; &nbsp; &nbsp; Arrays.fill(skipBuffer, (byte) 0);&nbsp; &nbsp; } catch (Exception e) {&nbsp; &nbsp; &nbsp; &nbsp; return 0;&nbsp; &nbsp; }&nbsp; &nbsp; return skipped;}@Overridepublic int available() throws IOException {&nbsp; &nbsp; return mUpstream.available();}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java