/*
* Copyright 2017 ZhangJiupeng
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cc.agentx.security;
import cc.agentx.util.KeyHelper;
import org.bouncycastle.crypto.StreamBlockCipher;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CFBBlockCipher;
import org.bouncycastle.crypto.modes.OFBBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import javax.crypto.spec.SecretKeySpec;
public class AesCipher extends Cipher {
// encryption mode
public static final int AES_128_CFB = 16;
public static final int AES_192_CFB = 24;
public static final int AES_256_CFB = 32;
public static final int AES_128_OFB = -16;
public static final int AES_192_OFB = -24;
public static final int AES_256_OFB = -32;
private final int keyLength;
private final StreamBlockCipher cipher;
/**
* <b>Notice: </b><br/>
* 1. the <code>AESFastEngine</code> was replaced by <code>AESEngine</code> now.<br/>
* 2. in <code>new CFBBlockCipher(engine, <b>16</b> * 8);</code> the IV length (16) is
* reference to the shadowsocks's design.
*
* @see <a href="https://www.bouncycastle.org/releasenotes.html">
* https://www.bouncycastle.org/releasenotes.html</a>#CVE-2016-1000339<br/>
* <a href="https://shadowsocks.org/en/spec/cipher.html">
* https://shadowsocks.org/en/spec/cipher.html</a>#Cipher
*/
public AesCipher(String password, int mode) {
key = new SecretKeySpec(password.getBytes(), "AES");
keyLength = Math.abs(mode);
AESEngine engine = new AESEngine();
if (mode > 0) {
cipher = new CFBBlockCipher(engine, 16 * 8);
} else {
cipher = new OFBBlockCipher(engine, 16 * 8);
}
}
public static boolean isValidMode(int mode) {
int modeAbs = Math.abs(mode);
return modeAbs == 16 || modeAbs == 24 || modeAbs == 32;
}
@Override
protected void _init(boolean isEncrypt, byte[] iv) {
String keyStr = new String(key.getEncoded());
ParametersWithIV params = new ParametersWithIV(
new KeyParameter(KeyHelper.generateKeyDigest(keyLength, keyStr)), iv
);
cipher.init(isEncrypt, params);
}
@Override
protected byte[] _encrypt(final byte[] originData) {
byte[] encryptedData = new byte[originData.length];
cipher.processBytes(originData, 0, originData.length, encryptedData, 0);
return encryptedData;
}
@Override
protected byte[] _decrypt(final byte[] encryptedData) {
byte[] originData = new byte[encryptedData.length];
cipher.processBytes(encryptedData, 0, encryptedData.length, originData, 0);
return originData;
}
@Override
public int getIVLength() {
return 16;
}
}