序
本文主要研究一下Jasypt的StandardPBEByteEncryptor
Jasypt
Jasypt即Java Simplified Encryption,它主要是简化项目加解密的工作,内置提供了很多组件的集成,比如hibernate、spring、spring-security等
示例
示例1
StrongPasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();
String encryptedPassword = passwordEncryptor.encryptPassword(userPassword);
...
if (passwordEncryptor.checkPassword(inputPassword, encryptedPassword)) {// correct!
} else {// bad login!
}
示例2
AES256TextEncryptor textEncryptor = new AES256TextEncryptor();
textEncryptor.setPassword(myEncryptionPassword);
String myEncryptedText = textEncryptor.encrypt(myText);
...
String plainText = textEncryptor.decrypt(myEncryptedText);
StandardPBEByteEncryptor
org/jasypt/encryption/pbe/StandardPBEByteEncryptor.java
public final class StandardPBEByteEncryptor implements PBEByteCleanablePasswordEncryptor {/*** The default algorithm to be used if none specified: PBEWithMD5AndDES.*/public static final String DEFAULT_ALGORITHM = "PBEWithMD5AndDES";/*** The default number of hashing iterations applied for obtaining the encryption key from the specified password,* set to 1000.*/public static final int DEFAULT_KEY_OBTENTION_ITERATIONS = 1000;/*** The default salt size, only used if the chosen encryption algorithm is not a block algorithm and thus block size* cannot be used as salt size.*/public static final int DEFAULT_SALT_SIZE_BYTES = 8;/*** The default IV size*/public static final int IV_SIZE_IN_BITS = 128;// Algorithm (and provider-related info) for Password Based Encoding.private String algorithm = DEFAULT_ALGORITHM;private String providerName = null;private Provider provider = null;// Password to be applied. This will NOT have a default value. If none// is set during configuration, an exception will be thrown.private char[] password = null;// Number of hashing iterations to be applied for obtaining the encryption// key from the specified password.private int keyObtentionIterations = DEFAULT_KEY_OBTENTION_ITERATIONS;// SaltGenerator to be used. Initialization of a salt generator is costly,// and so default value will be applied only in initialize(), if it finally// becomes necessary.private SaltGenerator saltGenerator = null;// IVGenerator to initialise IVprivate IVGenerator ivGenerator = null;// Size in bytes of the IV to be usedprivate final int IVSizeBytes = IV_SIZE_IN_BITS;// Size in bytes of the salt to be used for obtaining the// encryption key. This size will depend on the PBE algorithm being used,// and it will be set to the size of the block for the specific// chosen algorithm (if the algorithm is not a block algorithm, the// default value will be used).private int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES;//......
}
StandardPBEByteEncryptor实现了PBEByteCleanablePasswordEncryptor接口,而该则集成了PBEByteEncryptor和CleanablePasswordBased接口,PBEByteEncryptor继承了ByteEncryptor、PasswordBased
ByteEncryptor
org/jasypt/encryption/ByteEncryptor.java
public interface ByteEncryptor {/*** Encrypt the input message* * @param message the message to be encrypted* @return the result of encryption*/public byte[] encrypt(byte[] message);/*** Decrypt an encrypted message* * @param encryptedMessage the encrypted message to be decrypted* @return the result of decryption*/public byte[] decrypt(byte[] encryptedMessage);}
ByteEncryptor定义了encrypt和decrypt方法
StandardPBEByteEncryptor.encrypt
public byte[] encrypt(final byte[] message) throws EncryptionOperationNotPossibleException {if (message == null) {return null;}// Check initializationif (!isInitialized()) {initialize();}try {final byte[] salt;byte[] iv = null;final byte[] encryptedMessage;if (usingFixedSalt) {salt = fixedSaltInUse;synchronized (encryptCipher) {encryptedMessage = encryptCipher.doFinal(message);}}else {// Create saltsalt = saltGenerator.generateSalt(saltSizeBytes);// Create the IViv = ivGenerator.generateIV(IVSizeBytes);IvParameterSpec ivParameterSpec = null;if (iv != null) {ivParameterSpec = new IvParameterSpec(iv);}/** Perform encryption using the Cipher*/final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations,ivParameterSpec);synchronized (encryptCipher) {encryptCipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);encryptedMessage = encryptCipher.doFinal(message);}}byte[] encryptedMessageWithIV = encryptedMessage;if (ivGenerator.includePlainIVInEncryptionResults()) {encryptedMessageWithIV = CommonUtils.appendArrays(iv, encryptedMessage);}// Finally we build an array containing both the unencrypted salt// and the result of the encryption. This is done only// if the salt generator we are using specifies to do so.if (saltGenerator.includePlainSaltInEncryptionResults()) {// Insert unhashed salt before the encryption resultfinal byte[] encryptedMessageWithIVAndSalt = CommonUtils.appendArrays(salt, encryptedMessageWithIV);return encryptedMessageWithIVAndSalt;}return encryptedMessageWithIV;}catch (final InvalidKeyException e) {// The problem could be not having the unlimited strength policies// installed, so better give a usefull error message.handleInvalidKeyException(e);throw new EncryptionOperationNotPossibleException();}catch (final Exception e) {// If encryption fails, it is more secure not to return any// information about the cause in nested exceptions. Simply fail.throw new EncryptionOperationNotPossibleException();}}
StandardPBEByteEncryptor的encrypt方法,该方法首先判断salt值是固定的和动态的,固定的则是初始化的时候就设置好的,直接从实例属性取,然后直接调用cipher加密
而动态的话,则通过saltGenerator和generateIV来生成salt和iv,之后根据salt、iv和keyObtentionIterations来创建PBEParameterSpec,然后初始化cipher再进行加密
最后通过ivGenerator判断是否需要把iv包含到加密结果中,是则append到前面进去,再通过saltGenerator判断是否应该把slat包含到加密结果中,是则append到前面进去,最后返回解密结果
StandardPBEByteEncryptor.decrypt
public byte[] decrypt(final byte[] encryptedMessage) throws EncryptionOperationNotPossibleException {if (encryptedMessage == null) {return null;}// Check initializationif (!isInitialized()) {initialize();}if (saltGenerator.includePlainSaltInEncryptionResults()) {// Check that the received message is bigger than the saltif (encryptedMessage.length <= saltSizeBytes) {throw new EncryptionOperationNotPossibleException();}}// if (this.ivGenerator.includePlainIVInEncryptionResults()) {// // Check that the received message is bigger than the IV// if (encryptedMessage.length <= this.IVSizeBytes) {// throw new EncryptionOperationNotPossibleException();// }// }try {// If we are using a salt generator which specifies the salt// to be included into the encrypted message itself, get it from// there. If not, the salt is supposed to be fixed and thus the// salt generator can be safely asked for it again.byte[] salt = null;byte[] encryptedMessageKernel = null;if (saltGenerator.includePlainSaltInEncryptionResults()) {final int saltStart = 0;final int saltSize = saltSizeBytes < encryptedMessage.length ? saltSizeBytes : encryptedMessage.length;final int encMesKernelStart = saltSizeBytes < encryptedMessage.length ? saltSizeBytes: encryptedMessage.length;final int encMesKernelSize = saltSizeBytes < encryptedMessage.length? encryptedMessage.length - saltSizeBytes: 0;salt = new byte[saltSize];encryptedMessageKernel = new byte[encMesKernelSize];System.arraycopy(encryptedMessage, saltStart, salt, 0, saltSize);System.arraycopy(encryptedMessage, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize);}else if (!usingFixedSalt) {salt = saltGenerator.generateSalt(saltSizeBytes);encryptedMessageKernel = encryptedMessage;}else {// this.usingFixedSalt == truesalt = fixedSaltInUse;encryptedMessageKernel = encryptedMessage;}// Logic for IVbyte[] finalEncryptedMessage;byte[] iv;if (ivGenerator.includePlainIVInEncryptionResults()) {// Extracting the IViv = Arrays.copyOfRange(encryptedMessageKernel, 0, IVSizeBytes / 8);finalEncryptedMessage = Arrays.copyOfRange(encryptedMessageKernel, iv.length,encryptedMessageKernel.length);}else {// Fixed IVfinalEncryptedMessage = encryptedMessageKernel;iv = ivGenerator.generateIV(IVSizeBytes);}final byte[] decryptedMessage;if (usingFixedSalt) {/** Fixed salt is being used, therefore no initialization supposedly needed*/synchronized (decryptCipher) {decryptedMessage = decryptCipher.doFinal(encryptedMessageKernel);}}else {/** Perform decryption using the Cipher*/IvParameterSpec ivParameterSpec = null;if (iv != null) {ivParameterSpec = new IvParameterSpec(iv);}final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, keyObtentionIterations,ivParameterSpec);synchronized (decryptCipher) {decryptCipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);decryptedMessage = decryptCipher.doFinal(finalEncryptedMessage);}}// Return the resultsreturn decryptedMessage;}catch (final InvalidKeyException e) {// The problem could be not having the unlimited strength policies// installed, so better give a usefull error message.handleInvalidKeyException(e);throw new EncryptionOperationNotPossibleException();}catch (final Exception e) {// If decryption fails, it is more secure not to return any// information about the cause in nested exceptions. Simply fail.throw new EncryptionOperationNotPossibleException();}}
StandardPBEByteEncryptor的decrypt方法先通过saltGenerator判断salt是否包含在密文中,是则根据配置的salt的长度从密文取出来salt,之后通过ivGenerator判断iv是否包含在密文中,是则从剩下的密文取出来iv,得到slat和iv之后,对剩下的密文进行解密
小结
StandardPBEByteEncryptor实现了ByteEncryptor的encrypt和decrypt方法,其主要思路就是判断slat、iv是否包含在密文,然后做对应的处理。
doc
- Jasypt