这个答案为@sanketshah的伟大answer增加了一点解释。
以下代码展示了用法:
import org.identityconnectors.common.security.GuardedString;
import java.security.SecureRandom;
public class Main {
public static void main(String[] args) {
/*
* Using:
* "password".toCharArray();
* would create an immutable String "password",
* which remains in memory until GC is called.
*/
char[] password = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
GuardedString guardedString = new GuardedString(password);
/*
* Securely wipe the char array by storing random values in it.
* Some standards require multiple rounds of overwriting; see:
* https://en.wikipedia.org/wiki/Data_erasure#Standards
*/
SecureRandom sr = new SecureRandom();
for (int i = 0; i < password.length; i++)
password[i] = (char) sr.nextInt(Character.MAX_VALUE + 1);
//noinspection UnusedAssignment
password = null;
/*
* At some later point in the code, we might need the secret.
* Here's how to obtain it using Java 8+ lambdas.
*/
guardedString.access(chars -> {
for (char c : chars) {
System.out.print(c);
}
});
}
}
GuardedString可以从mavenIdentityConnectors: Framework获取。不过实际实现还需要IdentityConnectors: Framework Internal。
更准确地说,前一个包定义了Encryptor接口:
package org.identityconnectors.common.security;
/**
* Responsible for encrypting/decrypting bytes. Implementations
* are intended to be thread-safe.
*/
public interface Encryptor {
/**
* Decrypts the given byte array
* @param bytes The encrypted bytes
* @return The decrypted bytes
*/
public byte [] decrypt(byte [] bytes);
/**
* Encrypts the given byte array
* @param bytes The clear bytes
* @return The ecnrypted bytes
*/
public byte [] encrypt(byte [] bytes);
}
由EncryptorImpl 在第二个包中实现。 (抽象类EncryptorFactory也是如此,由EncryptorFactoryImpl扩展)。
EncryptorFactory 实际上修复了它的实现:
// At some point we might make this pluggable, but for now, hard-code
private static final String IMPL_NAME = "org.identityconnectors.common.security.impl.EncryptorFactoryImpl";
实现的一个不安全方面是它们使用带有硬编码 IV 和密钥的AES/CBC/PKCS5Padding。 EncryptorFactoryImpl 的构造函数将true 传递给EncryptorImpl:
public EncryptorFactoryImpl() {
_defaultEncryptor = new EncryptorImpl(true);
}
这会导致它使用默认密钥。无论如何,IV 始终是固定的:
public EncryptorImpl( boolean defaultKey ) {
if ( defaultKey ) {
_key = new SecretKeySpec(_defaultKeyBytes,ALGORITHM);
_iv = new IvParameterSpec(_defaultIvBytes);
}
else {
try {
_key = KeyGenerator.getInstance(ALGORITHM).generateKey();
_iv = new IvParameterSpec(_defaultIvBytes);
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这里有一些改进的空间:
- 使用 AES/CTR 或 AES/GCM 代替 AES/CBC。 (见block cipher mode of operation。)
- 始终使用随机 IV 和密钥。
-
GuardedString 使用内部方法 SecurityUtil.clear() 清除字节数组,这会将字节清零。如果有其他可能的data erasure 算法,那就太好了。