📄 importerv3.java
字号:
/*KeePass for J2MECopyright 2007 Naomaru Itoi <nao@phoneid.org>This file was derived from Java clone of KeePass - A KeePass file viewer for JavaCopyright 2006 Bill Zwicky <billzwicky@users.sourceforge.net>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; version 2This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA*/package org.phoneid.keepassj2me;// PhoneIDimport org.phoneid.*;// Javaimport java.io.InputStream;import java.io.IOException;import java.util.Vector;import java.util.Enumeration;import java.lang.RuntimeException;import java.io.UnsupportedEncodingException;import javax.microedition.io.*;import javax.microedition.lcdui.*;// Bouncy Castleimport org.bouncycastle1.util.encoders.Hex;import org.bouncycastle1.crypto.*;import org.bouncycastle1.crypto.generators.*;import org.bouncycastle1.crypto.digests.*;import org.bouncycastle1.crypto.params.*;import org.bouncycastle1.crypto.paddings.*;import org.bouncycastle1.crypto.modes.*;import org.bouncycastle1.crypto.engines.*;import org.bouncycastle1.util.*;import org.bouncycastle1.util.encoders.*;/** * Load a v3 database file. * * @author Naomaru Itoi <nao@phoneid.org> * @author Bill Zwicky <wrzwicky@pobox.com> */public class ImporterV3 { // Platform platform = new JavaPlatform(); Form mForm; public ImporterV3() { super(); mForm = null; } /** * Constructor which takes form for debugging */ public ImporterV3(Form form) { super(); mForm = form; } /** * Load a v3 database file, return contents in a new PwManager. * * @param infile Existing file to load. * @param password Pass phrase for infile. * @param pRepair (unused) * @return new PwManager container. * * @throws IOException on any file error. * @throws InvalidKeyException on a decryption error, or possible internal bug. * @throws IllegalBlockSizeException on a decryption error, or possible internal bug. * @throws BadPaddingException on a decryption error, or possible internal bug. * @throws NoSuchAlgorithmException on a decryption error, or possible internal bug. * @throws NoSuchPaddingException on a decryption error, or possible internal bug. * @throws InvalidAlgorithmParameterException if error decrypting main file body. * @throws ShortBufferException if error decrypting main file body. */ public PwManager openDatabase( InputStream inStream, String password ) throws IOException, PhoneIDException, InvalidCipherTextException { PwManager newManager; SHA256Digest md; /** Master key encrypted several times */ byte[] transformedMasterKey; byte[] finalKey; if (mForm != null) mForm.append("openDatabase()\r\n"); // Load entire file, most of it's encrypted. // InputStream in = new FileInputStream( infile ); byte[] filebuf = new byte[(int)inStream.available()]; inStream.read( filebuf, 0, (int)inStream.available()); inStream.close(); // Parse header (unencrypted) if( filebuf.length < PwDbHeader.BUF_SIZE ) throw new IOException( "File too short for header" ); PwDbHeader hdr = new PwDbHeader( filebuf, 0 ); if( (hdr.signature1 != PwManager.PWM_DBSIG_1) || (hdr.signature2 != PwManager.PWM_DBSIG_2) ) { KeePassMIDlet.logS ( "Bad database file signature" ); throw new IOException( "Bad database file signature" ); } if( hdr.version != PwManager.PWM_DBVER_DW ) { KeePassMIDlet.logS ( "Bad database file version"); throw new IOException( "Bad database file version" ); } newManager = new PwManager(); newManager.setMasterKey( password ); // Select algorithm if( (hdr.flags & PwManager.PWM_FLAG_RIJNDAEL) != 0 ) { KeePassMIDlet.logS ( "Algorithm AES"); newManager.algorithm = PwManager.ALGO_AES; } else if( (hdr.flags & PwManager.PWM_FLAG_TWOFISH) != 0 ) { KeePassMIDlet.logS ( "Algorithm TWOFISH"); newManager.algorithm = PwManager.ALGO_TWOFISH; } else { throw new IOException( "Unknown algorithm." ); } if( newManager.algorithm == PwManager.ALGO_TWOFISH ) throw new IOException( "TwoFish algorithm is not supported" ); newManager.numKeyEncRounds = hdr.numKeyEncRounds; if (mForm != null) mForm.append("rounds = " + newManager.numKeyEncRounds + "\r\n"); // testRijndael_JCE(); newManager.name = "KeePass Password Manager"; // Generate transformedMasterKey from masterKey //KeePassMIDlet.logS ("masterSeed2: " + new String(Hex.encode(hdr.masterSeed2))); transformedMasterKey = transformMasterKey( hdr.masterSeed2, newManager.masterKey, newManager.numKeyEncRounds ); // Hash the master password with the salt in the file md = new SHA256Digest(); md.update( hdr.masterSeed, 0, hdr.masterSeed.length ); md.update( transformedMasterKey, 0, transformedMasterKey.length ); finalKey = new byte[md.getDigestSize()]; md.doFinal ( finalKey, 0); // NI //KeePassMIDlet.logS ("finalKey: " + new String(Hex.encode(finalKey))); // Initialize Rijndael algorithm // Cipher cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" ); //PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine())); BufferedBlockCipher cipher = new BufferedBlockCipher(new CBCBlockCipher(new AESEngine())); //cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( finalKey, "AES" ), new IvParameterSpec( hdr.encryptionIV ) ); cipher.init(false, new ParametersWithIV(new KeyParameter(finalKey), hdr.encryptionIV)); // Decrypt! The first bytes aren't encrypted (that's the header) //int encryptedPartSize = cipher.doFinal( filebuf, PwDbHeader.BUF_SIZE, filebuf.length - PwDbHeader.BUF_SIZE, filebuf, PwDbHeader.BUF_SIZE ); //int encryptedPartSize int paddedEncryptedPartSize = cipher.processBytes(filebuf, PwDbHeader.BUF_SIZE, filebuf.length - PwDbHeader.BUF_SIZE, filebuf, PwDbHeader.BUF_SIZE ); int encryptedPartSize = 0; //try { PKCS7Padding padding = new PKCS7Padding(); encryptedPartSize = paddedEncryptedPartSize - padding.padCount(filebuf); //} catch (Exception e) { //} // NI byte[] plainContent = new byte[encryptedPartSize]; System.arraycopy(filebuf, PwDbHeader.BUF_SIZE, plainContent, 0, encryptedPartSize); if (mForm != null) mForm.append("filebuf length: " + filebuf.length + "\r\n"); //System.out.println ("file length: " + filebuf.length); //System.out.println ("plaintext contents length: " + encryptedPartSize); //System.out.println ("plaintext contents:\n" + new String(Hex.encode(plainContent))); //if( pRepair == null ) { //md = MessageDigest.getInstance( "SHA-256" ); md = new SHA256Digest(); md.update( filebuf, PwDbHeader.BUF_SIZE, encryptedPartSize ); // md.update( makePad(filebuf) ); md.doFinal (finalKey, 0); if( PhoneIDUtil.compare( finalKey, hdr.contentsHash ) == false) { //KeePassMIDlet.logS ( "Database file did not decrypt correctly. (checksum code is broken)" ); System.out.println ("Database file did not decrypt correctly. (checksum code is broken)"); if (mForm != null) mForm.append("Database file did not decrypt correctly. (checksum code is broken)\r\n"); throw new PhoneIDException("Wrong Password, or Database File Corrupted (database file did not decrypt correctly)"); } // } // Import all groups if (mForm != null) mForm.append("Import all groups\r\n"); int pos = PwDbHeader.BUF_SIZE; PwGroup newGrp = new PwGroup(); for( int i = 0; i < hdr.numGroups; ) { int fieldType = Types.readShort( filebuf, pos ); pos += 2; int fieldSize = Types.readInt( filebuf, pos ); pos += 4; if( fieldType == 0xFFFF ) { KeePassMIDlet.logS ( newGrp.level + " " + newGrp.name ); // End-Group record. Save group and count it. //newManager.groups.add( newGrp ); newManager.addGroup( newGrp ); newGrp = new PwGroup(); i++; } else { readGroupField( newGrp, fieldType, filebuf, pos ); } pos += fieldSize; } // fixGroups( groups ); // Import all entries if (mForm != null) mForm.append("Import all entries\r\n"); PwEntry newEnt = new PwEntry(); for( int i = 0; i < hdr.numEntries; ) { int fieldType = Types.readShort( filebuf, pos ); int fieldSize = Types.readInt( filebuf, pos + 2 ); if( fieldType == 0xFFFF ) { // End-Group record. Save group and count it. newManager.addEntry( newEnt ); KeePassMIDlet.logS( newEnt.title ); newEnt = new PwEntry(); i++; } else { readEntryField( newEnt, filebuf, pos ); } pos += 2 + 4 + fieldSize; } // Keep the Meta-Info entry separate if (mForm != null) mForm.append("Keep the Meta-Info entry separate\r\n"); for( int i=0; i<newManager.entries.size(); i++) { PwEntry ent = (PwEntry)newManager.entries.elementAt(i); if( ent.title.equals( "Meta-Info" ) && ent.url.equals( "$" ) && ent.username.equals( "SYSTEM" ) ) { newManager.metaInfo = ent; newManager.entries.removeElementAt(i); } } if (mForm != null) mForm.append("Return newManager: " + newManager + "\r\n"); return newManager; } /** * KeePass's custom pad style. * * @param data buffer to pad. * @return addtional bytes to append to data[] to make * a properly padded array. */ public static byte[] makePad( byte[] data ) { //custom pad method //TODO //WRZ doesn't work (yet) // append 0x80 plus zeros to a multiple of 4 bytes int thisblk = 32 - data.length % 32; // bytes needed to finish blk int nextblk = 0; // 32 if we need another block // need 9 bytes; add new block if no room if( thisblk < 9 ) { nextblk = 32; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -