/* KeyringEditor Copyright 2004 Markus Griessnig Vienna University of Technology Institute of Computer Technology KeyringEditor is based on: Java Keyring v0.6 Copyright 2004 Frank Taylor These programs are distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ // Crypto.java // 29.10.2004 // 06.11.2004: removed entryId from decrypt() // 17.11.2004: setPassword uses char[] (security reason); enrypt rest=0 bug // 01.12.2004: Keyring database format 5 support added // 04.12.2004: AES support added import java.util.*; // Arrays.equals import javax.crypto.*; // SecretKey import javax.crypto.spec.*; // DESedeKeySpec import java.security.*; // MessageDigest /** * This class is used to encrypt and decrypt entries. */ public class Crypto { /** * Check for odd parity */ protected static final int odd_parity[]={ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254}; // Keyring database format 4 /** * Salt size in byte (Database format 4) */ protected static final int SALT_SIZE = 4; /** * Maximum size of salt + password in byte (Database format 4) */ protected static final int MD5_CBLOCK = 64; /** * MD5 hash size in byte (Database format 4) */ protected static final int MD5_DIGEST_LENGTH = 16; /** * Cipher block size in byte (Database format 4) */ protected static final int KDESBLOCKSIZE = 8; // Keyring database format 4 /** * Password information (Database format 4) */ protected byte[] recordZero; // password information /** * Databse version */ private int version; // Keyring database format 5 /** * Salt (Database format 5) */ protected byte[] salt = new byte[8]; /** * Hash (Database format 5) */ protected byte[] hash = new byte[8]; /** * Iterations (Database format 5) */ protected int iter; /** * Cipher type (Database format 5), * NO_CIPHER = 0, * DES3_EDE_CBC_CIPHER = 1, * AES_128_CBC_CIPHER = 2, * AES_256_CBC_CIPHER = 3 */ protected int type; // keyring: cipher protected SecretKey key = null; protected Cipher cipher = null; /** * Constructor for database format 4. * * @param recordZero Password information * @param version equals 4 */ public Crypto(byte[] recordZero, int version) { this.recordZero = recordZero; // null if Keyring database format 5 this.version = version; } /** * Constructor for database format 5. * * @param recordZero equals null * @param version equals 5 * @param salt Salt * @param hash Hash * @param iter Iterations * @param type Cipher type */ public Crypto(byte[] recordZero, int version, byte[] salt, byte[] hash, int iter, int type) { this.recordZero = recordZero; // null if Keyring database format 5 this.version = version; this.salt = salt; this.hash = hash; this.iter = iter; this.type = type; } // setPassword ------------------------------------------------ // // Source: keyring-link-0.1.1/keyring.c (gnukeyring.sourceforge.net) // // The master password is not stored in the database. Instead, // an MD5 hash of the password and a random 32-bit salt is stored and // checked against entered values. (Keyring crypto) // /** * This method calls the setPassword method according to database version. * * @param password Database password */ public void setPassword(char[] password) throws Exception { switch(version) { case 4: setPassword_4(password); break; case 5: setPassword_5(password); break; } } /** * This method checks the entered password and generates record encryption key for database format 5. * * @param password Database password */ public void setPassword_5(char[] password) throws Exception { int index; int[] cipherlen = {0, 24, 16, 32}; // keylength in byte byte[] pass = new byte[password.length]; for(int i=0;i 0) { if(keylen > SHA_DIGEST_LENGTH) blocklen = SHA_DIGEST_LENGTH; else blocklen = keylen; itmp[0] = (byte)(0xff & (i >> 24)); itmp[1] = (byte)(0xff & (i >> 16)); itmp[2] = (byte)(0xff & (i >> 8)); itmp[3] = (byte)(0xff & i); mac.reset(); mac.update(salt); digtmp = mac.doFinal(itmp); System.arraycopy(digtmp, 0, p, pos, blocklen); for(j = 1; j < iter; j++) { mac.reset(); digtmp = mac.doFinal(digtmp); for(k = 0; k < blocklen; k++) p[pos+k] ^= digtmp[k]; } keylen = keylen - blocklen; pos = pos + blocklen; i++; } return p; } /** * This method is an implementation of RFC 2104 (HMAC). Not used. */ public byte[] hmac(byte[] text, byte[] key, String hashfunction) throws Exception { // rfc 2104 int BLOCKSIZE = 64; // byte length byte[] ipad = new byte[BLOCKSIZE]; byte[] opad = new byte[BLOCKSIZE]; byte[] digest; Arrays.fill(ipad, (byte)0x00); Arrays.fill(opad, (byte)0x00); System.arraycopy(key, 0, ipad, 0, key.length); System.arraycopy(key, 0, opad, 0, key.length); for(int i=0;i 40) { throw new Exception("Password too long."); } // check password hash = checkPasswordHash_4(recordZero, pass); if(!Arrays.equals(hash, Model.sliceBytes(recordZero, SALT_SIZE, MD5_DIGEST_LENGTH))) { throw new Exception("Password incorrect."); } // -------------------------------------------------------- // generate record encryption key // -------------------------------------------------------- /* The master password is also used to generate a record encryption key. The 128-bit MD5 hash of the master password is split into two 64-bit keys, K1 and K2. (DES ignores the top bit of each byte, so the key has 112 effective unknown bits.) These are used to generate record data encrypted as Enc(K1, Dec(K2, Enc(K1, Data))). Each 8-byte data block is independently encrypted by the same key. (Keyring crypto) */ // calc_snib() MessageDigest md = MessageDigest.getInstance("MD5"); md.update(pass); snib = md.digest(); // 128 bit md5 hash //System.out.println("snib.length = " + snib.length); // generate the DES keypair (snib = A,B; desKeyData = A,B,A) for(i=0; i<16; i++) { desKeyData[i] = snib[i]; if(i < 8) { desKeyData[i + 16] = snib[i]; } } // setup SecretKey and Cipher key = SecretKeyFactory.getInstance("DESede").generateSecret(new DESedeKeySpec(desKeyData)); cipher = Cipher.getInstance("TripleDES/ECB/NoPadding"); // ECB Electronic Codebook Mode // for security reason set each element to zero for(i=0;i