序
本文次要钻研一下 jasypt 的 IVGenerator
IVGenerator
org/jasypt/salt/IVGenerator.java
/**
* <p>
* Common interface for all IV generators which can be applied in digest
* or encryption operations.
* </p>
* <p>
* <b>Every implementation of this interface must be thread-safe</b>.
* </p>
*
* @since 1.9.3
*
* @author Alex Scal
*
*/
public interface IVGenerator {
/**
* <p>
* This method will be called for requesting the generation of a new
* IV of the specified length.
* </p>
*
* @param length the requested length for the IV.
* @return the generated IV.
*/
byte[] generateIV(int length);
/**
* <p>
* Determines if the digests and encrypted messages created with a
* specific IV generator will include (prepended) the unencrypted
* IV itself, so that it can be used for matching and decryption
* operations.
* </p>
* <p>
* Generally, including the IV unencrypted in encryption results will
* be mandatory for randomly generated IVs, or for those generated in a
* non-predictable manner.
* Otherwise, digest matching and decryption operations will always fail.
* </p>
*
* @return whether the plain (unencrypted) IV has to be included in
* encryption results or not.
*/
public boolean includePlainIVInEncryptionResults();}
IVGenerator 定义了 generateIV 及 includePlainIVInEncryptionResults 办法,它有三个实现类,别离是 NoOpIVGenerator、StringFixedIVGenerator、RandomIVGenerator
NoOpIVGenerator
public class NoOpIVGenerator implements IVGenerator {
/**
* Return IV with the specified byte length.
*
* @param lengthBytes length in bytes.
* @return the generated salt.
*/
@Override
public byte[] generateIV(final int lengthBytes) {return null;}
/**
* As this salt generator provides a fixed IV, its inclusion
* unencrypted in encryption results
* is not necessary, and in fact not desirable (so that it remains hidden).
*
* @return false
*/
@Override
public boolean includePlainIVInEncryptionResults() {return false;}
}
次要用于解密旧版 (不应用 iv) 的明码
StringFixedIVGenerator
org/jasypt/salt/StringFixedIVGenerator.java
public class StringFixedIVGenerator implements IVGenerator {
private static final String DEFAULT_CHARSET = "UTF-8";
private final String iv;
private final String charset;
private final byte[] ivBytes;
/**
* Creates a new instance of <tt>FixedStringIVGenerator</tt> using
* the default charset.
*
* @param iv the specified salt.
*/
public StringFixedIVGenerator(final String iv) {this(iv, null);
}
/**
* Creates a new instance of <tt>FixedStringIVGenerator</tt>
*
* @param iv the specified salt.
* @param charset the specified charset
*/
public StringFixedIVGenerator(final String iv, final String charset) {super();
CommonUtils.validateNotNull(iv, "IV cannot be set null");
this.iv = iv;
this.charset = (charset != null? charset : DEFAULT_CHARSET);
try {this.ivBytes = this.iv.getBytes(this.charset);
} catch (UnsupportedEncodingException e) {
throw new EncryptionInitializationException("Invalid charset specified:" + this.charset);
}
}
/**
* Return IV with the specified byte length.
*
* @param lengthBytes length in bytes.
* @return the generated salt.
*/
public byte[] generateIV(final int lengthBytes) {if (this.ivBytes.length < lengthBytes) {
throw new EncryptionInitializationException("Requested IV larger than set");
}
final byte[] generatedIV = new byte[lengthBytes];
System.arraycopy(this.ivBytes, 0, generatedIV, 0, lengthBytes);
return generatedIV;
}
/**
* As this salt generator provides a fixed IV, its inclusion
* unencrypted in encryption results
* is not necessary, and in fact not desirable (so that it remains hidden).
*
* @return false
*/
@Override
public boolean includePlainIVInEncryptionResults() {return false;}
}
StringFixedIVGenerator 依据固定的值和长度来生成 iv,如果固定值的长度小于申请生成 iv 的长度则抛出 EncryptionInitializationException,否则从后面取指定长度返回
RandomIVGenerator
org/jasypt/salt/RandomIVGenerator.java
public class RandomIVGenerator implements IVGenerator {
/**
* The default algorithm to be used for secure random number
* generation: set to SHA1PRNG.
*/
private static final String GENERATOR_ALGORITHM = "SHA1PRNG";
private final SecureRandom random;
/**
* Creates a new instance of <tt>RandomIVGenerator</tt> using the
* default secure random number generation algorithm.
*/
public RandomIVGenerator() {this(GENERATOR_ALGORITHM);
}
/**
* Creates a new instance of <tt>RandomIVGenerator</tt> specifying a
* secure random number generation algorithm.
*
* @since 1.9.3
*
*/
public RandomIVGenerator(String secureRandomAlgorithm) {super();
try {this.random = SecureRandom.getInstance(secureRandomAlgorithm);
} catch (NoSuchAlgorithmException e) {throw new EncryptionInitializationException(e);
}
}
/**
* Generate a random IV of the specified length in bytes.
*
* @param length length in bytes.
* @return the generated IV.
*/
@Override
public byte[] generateIV(int length) {byte[] iv = new byte[length / 8];
random.nextBytes(iv);
return iv;
}
/**
* This IV generator needs the salt to be included unencrypted in
* encryption results, because of its being random. This method will always
* return true.
*
* @return true
*/
@Override
public boolean includePlainIVInEncryptionResults() {return true;}
}
RandomIVGenerator 能够依据传入的 secureRandomAlgorithm 来生成 iv,如果不传默认是
SHA1PRNG
PBES2Core
com/sun/crypto/provider/PBES2Core.java
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {if (key == null) {throw new InvalidKeyException("Null key");
}
byte[] passwdBytes = key.getEncoded();
char[] passwdChars = null;
PBEKeySpec pbeSpec;
try {if ((passwdBytes == null) ||
!(key.getAlgorithm().regionMatches(true, 0, "PBE", 0, 3))) {throw new InvalidKeyException("Missing password");
}
// TBD: consolidate the salt, ic and IV parameter checks below
// Extract salt and iteration count from the key, if present
if (key instanceof javax.crypto.interfaces.PBEKey) {salt = ((javax.crypto.interfaces.PBEKey)key).getSalt();
if (salt != null && salt.length < 8) {
throw new InvalidAlgorithmParameterException("Salt must be at least 8 bytes long");
}
iCount = ((javax.crypto.interfaces.PBEKey)key).getIterationCount();
if (iCount == 0) {iCount = DEFAULT_COUNT;} else if (iCount < 0) {
throw new InvalidAlgorithmParameterException("Iteration count must be a positive number");
}
}
// Extract salt, iteration count and IV from the params, if present
if (params == null) {if (salt == null) {
// generate random salt and use default iteration count
salt = new byte[DEFAULT_SALT_LENGTH];
random.nextBytes(salt);
iCount = DEFAULT_COUNT;
}
if ((opmode == Cipher.ENCRYPT_MODE) ||
(opmode == Cipher.WRAP_MODE)) {
// generate random IV
byte[] ivBytes = new byte[blkSize];
random.nextBytes(ivBytes);
ivSpec = new IvParameterSpec(ivBytes);
}
} else {if (!(params instanceof PBEParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Wrong parameter type: PBE expected");
}
// salt and iteration count from the params take precedence
byte[] specSalt = ((PBEParameterSpec) params).getSalt();
if (specSalt != null && specSalt.length < 8) {
throw new InvalidAlgorithmParameterException("Salt must be at least 8 bytes long");
}
salt = specSalt;
int specICount = ((PBEParameterSpec) params).getIterationCount();
if (specICount == 0) {specICount = DEFAULT_COUNT;} else if (specICount < 0) {
throw new InvalidAlgorithmParameterException("Iteration count must be a positive number");
}
iCount = specICount;
AlgorithmParameterSpec specParams =
((PBEParameterSpec) params).getParameterSpec();
if (specParams != null) {if (specParams instanceof IvParameterSpec) {ivSpec = (IvParameterSpec)specParams;
} else {
throw new InvalidAlgorithmParameterException("Wrong parameter type: IV expected");
}
} else if ((opmode == Cipher.ENCRYPT_MODE) ||
(opmode == Cipher.WRAP_MODE)) {
// generate random IV
byte[] ivBytes = new byte[blkSize];
random.nextBytes(ivBytes);
ivSpec = new IvParameterSpec(ivBytes);
} else {
throw new InvalidAlgorithmParameterException("Missing parameter type: IV expected");
}
}
passwdChars = new char[passwdBytes.length];
for (int i = 0; i < passwdChars.length; i++)
passwdChars[i] = (char) (passwdBytes[i] & 0x7f);
pbeSpec = new PBEKeySpec(passwdChars, salt, iCount, keyLength);
// password char[] was cloned in PBEKeySpec constructor,
// so we can zero it out here
} finally {if (passwdChars != null) Arrays.fill(passwdChars, '\0');
if (passwdBytes != null) Arrays.fill(passwdBytes, (byte)0x00);
}
SecretKey s = null;
try {s = kdf.engineGenerateSecret(pbeSpec);
} catch (InvalidKeySpecException ikse) {
InvalidKeyException ike =
new InvalidKeyException("Cannot construct PBE key");
ike.initCause(ikse);
throw ike;
}
byte[] derivedKey = s.getEncoded();
SecretKeySpec cipherKey = new SecretKeySpec(derivedKey, cipherAlgo);
// initialize the underlying cipher
cipher.init(opmode, cipherKey, ivSpec, random);
}
PBES2Core 的 engineInit 在 PBEParameterSpec 的 PBEParameterSpec 不为 null 但又不是 IvParameterSpec 类型时抛出 InvalidAlgorithmParameterException(“Wrong parameter type: IV expected”)异样;如果是 encrypt 或者 wrap 模式,如果不传 iv 则会主动生成,而不是 encrypt 或者 wrap 模式 (个别是 decrypt 模式),则在 iv 为 null 时抛出 InvalidAlgorithmParameterException(“Missing parameter type: IV expected”) 异样
小结
IVGenerator 定义了 generateIV 及 includePlainIVInEncryptionResults 办法,它有三个实现类,别离是 NoOpIVGenerator、StringFixedIVGenerator、RandomIVGenerator;对于 PBE 算法,如果 iv 不传,在 decrypt 模式会抛出 InvalidAlgorithmParameterException(“Missing parameter type: IV expected”)异样