我不是安全专家,但是在检查我们旗舰产品的AES实现时,我注意到了一些奇怪的事情,比如输出长度与输入长度有关,并且没有明显使用IV。
@Service
public class EncryptionServiceImpl implements EncryptionService {
/** The logger for this class */
private static final Logger LOGGER = new Logger(EncryptionServiceImpl.class);
/** There's one and only one instance of this class */
private volatile static EncryptionServiceImpl INSTANCE;
/** True if EncryptionService is initialized. */
private boolean isInitialized = false;
private Cipher cipherEncrypt;
private Cipher cipherDecrypt;
private String keyHex;
/**
* Constructor is private, use getInstance to get an instance of this class
*/
private EncryptionServiceImpl() {
initialize();
}
/**
* Returns the singleton instance of this class.
*
* @return the singleton instance of this class.
*/
public static EncryptionServiceImpl getInstance() {
if (INSTANCE == null) {
synchronized (EncryptionServiceImpl.class) {
if (INSTANCE == null) {
INSTANCE = new EncryptionServiceImpl();
}
}
}
return INSTANCE;
}
/**
* Initialize EncryptionService.
*/
private synchronized void initialize() {
if (!isInitialized){
// Get key from SystemSettings.
SystemSettingsService systemSettingsService = (SystemSettingsService) ServiceFactory.getInstance().createService(SystemSettingsService.class);
keyHex = systemSettingsService.getScmuuid();
byte[] keyBytes;
// If keyHex is not blank (field "scmuuid" already exists in the database):
if (StringUtils.isNotBlank(keyHex)) {
keyBytes = hexToBytes(keyHex);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
try {
cipherEncrypt = Cipher.getInstance("AES");
cipherDecrypt = Cipher.getInstance("AES");
cipherEncrypt.init(Cipher.ENCRYPT_MODE, secretKeySpec);
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec);
} catch (InvalidKeyException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
} catch (NoSuchAlgorithmException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
} catch (NoSuchPaddingException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
}
//EncryptionService is initialized.
isInitialized = true;
} else {
/*
* If keyHex is blank, either we have an SQL exception or the key hasn't
* been generated yet. Trying to use the EncryptionService without proper
* initialization, will throw a FatalException.
* If the key hasn't been generated yet, the next exception will trigger
* the caller to use the generateKey() method in the catch block.
*/
//throw new NoEncryptionkeyException();
}
}
}
/**
* @see shared.bs.encryption.EncryptionService#isInitialized()
*/
public boolean isInitialized() {
return isInitialized;
}
/**
* @see shared.bs.encryption.EncryptionService#decrypt()
*/
public String decrypt(String value) {
if (StringUtils.isBlank(value)){
return null;
}
// NULL values from log files can be interpreted as a String with value "null" (see e.g. bug REDACTED)
if (value != null && value.equalsIgnoreCase("null")) {
return null;
}
if (getCipherDecrypt() == null) {
throw new EncryptionFailureException("Decryption failure. EncryptionService is not properly initialized.");
}
byte[] encryptedBytes = null;
byte[] decryptedBytes = null;
try {
encryptedBytes = hexToBytes(value);
decryptedBytes = cipherDecrypt.doFinal(encryptedBytes);
} catch (NumberFormatException e) {
throw new EncryptionFailureException("Decryption failure.", e);
} catch (IllegalBlockSizeException e) {
throw new EncryptionFailureException("Decryption failure.", e);
} catch (BadPaddingException e) {
throw new EncryptionFailureException("Decryption failure.", e);
}
return new String(decryptedBytes);
}
/**
* @see shared.bs.encryption.EncryptionService#encrypt()
*/
public String encrypt(String value) {
if (StringUtils.isBlank(value)){
return null;
}
if (getCipherEncrypt() == null) {
throw new EncryptionFailureException("Encryption failure. EncryptionService is not properly initialized.");
}
byte[] encrypted = null;
String encHex = null;
try {
encrypted = cipherEncrypt.doFinal(value.getBytes());
encHex = asHex(encrypted);
} catch (IllegalBlockSizeException e) {
throw new EncryptionFailureException("Encryption failure.", e);
} catch (BadPaddingException e) {
throw new EncryptionFailureException("Encryption failure.", e);
} catch (NumberFormatException e) {
throw new EncryptionFailureException("Encryption failure.", e);
}
return encHex;
}
/** convert a byte array to a hex String */
private String asHex(byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
/** convert a hex String to a byte array */
private byte[] hexToBytes(String hex) {
byte[] bts = new byte[hex.length() / 2];
for (int i = 0; i < bts.length; i++) {
bts[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
}
return bts;
}
/**
* @see shared.bs.encryption.EncryptionService#generateKey()
*/
public synchronized void generateKey(){
if (!isInitialized){
/*
* Make sure the key really doesn't already exist and that an initialization failure
* isn't the result of an earlier SQLException.
* Try again retrieving the key from the database.
*/
SystemSettingsService systemSettingsService = (SystemSettingsService) ServiceFactory.getInstance().createService(SystemSettingsService.class);
String tryKeyHex = systemSettingsService.getScmuuid();
if (StringUtils.isNotBlank(tryKeyHex)) {
// Something came back from the database, we try to initialize again and return silently.
initialize();
return;
}
// Generate a new 128 bit strong AES key.
KeyGenerator kgen;
try {
kgen = KeyGenerator.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
}
kgen.init(128); // 128 is in standard JCE
SecretKey secretKey = kgen.generateKey();
byte[] keyBytes = secretKey.getEncoded();
keyHex = asHex(keyBytes);
// We have a keyHex, it's time to generate the ciphers:
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
try {
cipherEncrypt = Cipher.getInstance("AES");
cipherDecrypt = Cipher.getInstance("AES");
cipherEncrypt.init(Cipher.ENCRYPT_MODE, secretKeySpec);
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec);
} catch (InvalidKeyException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
} catch (NoSuchAlgorithmException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
} catch (NoSuchPaddingException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
}
/*
* Persist keyHex (field scmuuid in SystemSettings) and encrypt all existing non-encrypted
* passwords and secure build/deploy-parameters in the database with this new key.
* removed for brevity in this example: each of these is an extra method call.
*/
}//End: if (!isInitialized)
}我的印象是,这有改进的余地,因为有关明文的信息正在泄露。请注意,由于整个项目的遗留代码(主要是数据库字段长度),我们所有的密文输出必须小于255个字符。实际上,这意味着输出必须是224字节长。
是的,我知道我们在加密密码。这些不是用户密码。这些都是通过诸如和LDAP这样的外部系统来处理的。这些加密密码用于对外部第三方系统进行身份验证,在这些系统中,实现基于令牌的身份验证方案要么是不可能的,要么是不可行的,或者是尝试失败的。
发布于 2016-07-15 14:02:07
/**
* @see shared.bs.encryption.EncryptionService#decrypt()
*/
public String decrypt(String value) {
if (StringUtils.isBlank(value)){
return null;
}
// NULL values from log files can be interpreted as a String with value "null" (see e.g. bug REDACTED)
if (value != null && value.equalsIgnoreCase("null")) {
return null;
}来自StringUtils.isBlankStringUtils.isBlank的文档:
返回:如果字符串为空、空或空白,则为
true。
因此,这里不需要对value进行空检查,可以进行检查。
} catch (InvalidKeyException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
} catch (NoSuchAlgorithmException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
} catch (NoSuchPaddingException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
}对于这类事情,你有多个收获。
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new InitializationFailureException(
"Failure to generate a new encryption key.", e);
}多捕获保持您的代码紧凑,并避免您不得不复制粘贴异常消息。
https://codereview.stackexchange.com/questions/134980
复制相似问题