📄 jcecipherfactory.java
字号:
/* Derby - Class org.apache.derby.impl.services.jce.JCECipherFactory Copyright 2000, 2004 The Apache Software Foundation or its licensors, as applicable. 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 org.apache.derby.impl.services.jce;import org.apache.derby.iapi.services.crypto.CipherFactory;import org.apache.derby.iapi.services.crypto.CipherProvider;import org.apache.derby.iapi.services.monitor.ModuleControl;import org.apache.derby.iapi.services.monitor.Monitor;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.error.StandardException;import org.apache.derby.iapi.services.info.JVMInfo;import org.apache.derby.iapi.util.StringUtil;import org.apache.derby.iapi.reference.SQLState;import org.apache.derby.iapi.reference.Attribute;import org.apache.derby.iapi.util.StringUtil;import java.util.Properties;import java.security.Key;import java.security.Provider;import java.security.SecureRandom;import java.security.Security;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.MessageDigest;import java.security.spec.KeySpec;import java.security.spec.InvalidKeySpecException;import java.io.FileNotFoundException;import java.io.IOException;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.DESKeySpec;import javax.crypto.spec.SecretKeySpec;import org.apache.derby.iapi.store.raw.RawStoreFactory;import org.apache.derby.io.StorageFactory;import org.apache.derby.io.WritableStorageFactory;import org.apache.derby.io.StorageFile;import org.apache.derby.io.StorageRandomAccessFile;/** This CipherFactory creates new JCECipherProvider. @see CipherFactory */public final class JCECipherFactory implements CipherFactory, ModuleControl, java.security.PrivilegedExceptionAction{ private final static String MESSAGE_DIGEST = "MD5"; private final static String DEFAULT_PROVIDER = "com.sun.crypto.provider.SunJCE"; private final static String DEFAULT_ALGORITHM = "DES/CBC/NoPadding"; private final static String DES = "DES"; private final static String DESede = "DESede"; private final static String TripleDES = "TripleDES"; private final static String AES = "AES"; // minimum boot password length in bytes private final static int BLOCK_LENGTH = 8; /** AES encryption takes in an default Initialization vector length (IV) length of 16 bytes This is needed to generate an IV to use for encryption and decryption process @see CipherProvider */ private final static int AES_IV_LENGTH = 16; // key length in bytes private int keyLengthBits; private int encodedKeyLength; private String cryptoAlgorithm; private String cryptoAlgorithmShort; private String cryptoProvider; private String cryptoProviderShort; private MessageDigest messageDigest; private SecretKey mainSecretKey; private byte[] mainIV; /** Amount of data that is used for verification of external encryption key This does not include the MD5 checksum bytes */ private final static int VERIFYKEY_DATALEN = 4096; private StorageFile activeFile; private int action; private String activePerms; static String providerErrorName(String cps) { return cps == null ? "default" : cps; } private byte[] generateUniqueBytes() throws StandardException { try { String provider = cryptoProviderShort; KeyGenerator keyGen; if (provider == null) { keyGen = KeyGenerator.getInstance(cryptoAlgorithmShort); } else { if( provider.equals("BouncyCastleProvider")) provider = "BC"; keyGen = KeyGenerator.getInstance(cryptoAlgorithmShort, provider); } keyGen.init(keyLengthBits); SecretKey key = keyGen.generateKey(); return key.getEncoded(); } catch (java.security.NoSuchAlgorithmException nsae) { throw StandardException.newException(SQLState.ENCRYPTION_NOSUCH_ALGORITHM, cryptoAlgorithm, JCECipherFactory.providerErrorName(cryptoProviderShort)); } catch (java.security.NoSuchProviderException nspe) { throw StandardException.newException(SQLState.ENCRYPTION_BAD_PROVIDER, JCECipherFactory.providerErrorName(cryptoProviderShort)); } } /** Encrypt the secretKey with the boot password. This includes the following steps, getting muck from the boot password and then using this to generate a key, generating an appropriate IV using the muck using the key and IV thus generated to create the appropriate cipher provider and encrypting the secretKey @return hexadecimal string of the encrypted secretKey @exception StandardException Standard Cloudscape error policy */ private String encryptKey(byte[] secretKey, byte[] bootPassword) throws StandardException { // In case of AES, care needs to be taken to allow for 16 bytes muck as well // as to have the secretKey that needs encryption to be a aligned appropriately // AES supports 16 bytes block size int muckLength = secretKey.length; if(cryptoAlgorithmShort.equals(AES)) muckLength = AES_IV_LENGTH; byte[] muck = getMuckFromBootPassword(bootPassword, muckLength); SecretKey key = generateKey(muck); byte[] IV = generateIV(muck); CipherProvider tmpCipherProvider = createNewCipher(ENCRYPT,key,IV); // store the actual secretKey.length before any possible padding encodedKeyLength = secretKey.length; // for the secretKey to be encrypted, first ensure that it is aligned to the block size of the // encryption algorithm by padding bytes appropriately if needed secretKey = padKey(secretKey,tmpCipherProvider.getEncryptionBlockSize()); byte[] result = new byte[secretKey.length]; // encrypt the secretKey using the key generated of muck from boot password and the generated IV tmpCipherProvider.encrypt(secretKey, 0, secretKey.length, result, 0); return org.apache.derby.iapi.util.StringUtil.toHexString(result, 0, result.length); } /** For block ciphers, and algorithms using the NoPadding scheme, the data that has to be encrypted needs to be a multiple of the expected block size for the cipher Pad the key with appropriate padding to make it blockSize align @param secretKey the data that needs blocksize alignment @param blockSizeAlign secretKey needs to be blocksize aligned @return a byte array with the contents of secretKey along with padded bytes in the end to make it blockSize aligned */ private byte[] padKey(byte[] secretKey,int blockSizeAlign) { byte [] result = secretKey; if(secretKey.length % blockSizeAlign != 0 ) { int encryptedLength = secretKey.length + blockSizeAlign - (secretKey.length % blockSizeAlign); result = new byte[encryptedLength]; System.arraycopy(secretKey,0,result,0,secretKey.length); } return result; } /** Decrypt the secretKey with the user key . This includes the following steps, retrieve the encryptedKey, generate the muck from the boot password and generate an appropriate IV using the muck,and using the key and IV decrypt the encryptedKey @return decrypted key @exception StandardException Standard Cloudscape error policy */ private byte[] decryptKey(String encryptedKey, int encodedKeyCharLength, byte[] bootPassword) throws StandardException { byte[] secretKey = org.apache.derby.iapi.util.StringUtil.fromHexString(encryptedKey, 0, encodedKeyCharLength); // In case of AES, care needs to be taken to allow for 16 bytes muck as well // as to have the secretKey that needs encryption to be a aligned appropriately // AES supports 16 bytes block size int muckLength; if(cryptoAlgorithmShort.equals(AES)) muckLength = AES_IV_LENGTH; else muckLength = secretKey.length; byte[] muck = getMuckFromBootPassword(bootPassword, muckLength); // decrypt the encryptedKey with the mucked up boot password to recover // the secretKey SecretKey key = generateKey(muck); byte[] IV = generateIV(muck); createNewCipher(DECRYPT, key, IV). decrypt(secretKey, 0, secretKey.length, secretKey, 0); return secretKey; } private byte[] getMuckFromBootPassword(byte[] bootPassword, int encodedKeyByteLength) { int ulength = bootPassword.length; byte[] muck = new byte[encodedKeyByteLength]; int rotation = 0; for (int i = 0; i < bootPassword.length; i++) rotation += bootPassword[i]; for (int i = 0; i < encodedKeyByteLength; i++) muck[i] = (byte)(bootPassword[(i+rotation)%ulength] ^ (bootPassword[i%ulength] << 4)); return muck; } /** Generate a Key object using the input secretKey that can be used by JCECipherProvider to encrypt or decrypt. @exception StandardException Standard Cloudscape Error Policy */ private SecretKey generateKey(byte[] secretKey) throws StandardException { int length = secretKey.length; if (length < CipherFactory.MIN_BOOTPASS_LENGTH) throw StandardException.newException(SQLState.ILLEGAL_BP_LENGTH, new Integer(MIN_BOOTPASS_LENGTH)); try { if (cryptoAlgorithmShort.equals(DES)) { // single DES if (DESKeySpec.isWeak(secretKey, 0)) { // OK, it is weak, spice it up byte[] spice = StringUtil.getAsciiBytes("louDScap"); for (int i = 0; i < 7; i++) secretKey[i] = (byte)((spice[i] << 3) ^ secretKey[i]); } } return new SecretKeySpec(secretKey, cryptoAlgorithmShort); } catch (InvalidKeyException ike) { throw StandardException.newException(SQLState.CRYPTO_EXCEPTION, ike); } } /** Generate an IV using the input secretKey that can be used by JCECipherProvider to encrypt or decrypt. */ private byte[] generateIV(byte[] secretKey) { // do a little simple minded muddling to make the IV not // strictly alphanumeric and the number of total possible keys a little // bigger. int IVlen = BLOCK_LENGTH; byte[] iv = null; if(cryptoAlgorithmShort.equals(AES)) { IVlen = AES_IV_LENGTH;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -