From 644963efaa3f9c22e3a6b994e069ec6f6bf89a6c Mon Sep 17 00:00:00 2001 From: Andreas Buchen Date: Mon, 2 Oct 2017 20:40:55 +0200 Subject: [PATCH] Revert changes to ClientFactory to avoid BadPaddingException --- .../portfolio/model/ClientFactory.java | 159 +++++++++--------- 1 file changed, 77 insertions(+), 82 deletions(-) diff --git a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/ClientFactory.java b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/ClientFactory.java index ffaa33d510..492d9bb861 100644 --- a/name.abuchen.portfolio/src/name/abuchen/portfolio/model/ClientFactory.java +++ b/name.abuchen.portfolio/src/name/abuchen/portfolio/model/ClientFactory.java @@ -1,7 +1,5 @@ package name.abuchen.portfolio.model; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -29,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.zip.Deflater; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; @@ -151,29 +148,31 @@ private static class PlainWriterZIP implements ClientPersister public Client load(InputStream input) throws IOException { // wrap with zip input stream - try (ZipInputStream zipin = new ZipInputStream(input)) - { - ZipEntry entry = zipin.getNextEntry(); - - if (!ZIP_DATA_FILE.equals(entry.getName())) - throw new IOException(MessageFormat.format(Messages.MsgErrorUnexpectedZipEntry, ZIP_DATA_FILE, - entry.getName())); + ZipInputStream zipin = new ZipInputStream(input); + ZipEntry entry = zipin.getNextEntry(); + + if (!ZIP_DATA_FILE.equals(entry.getName())) + throw new IOException(MessageFormat.format(Messages.MsgErrorUnexpectedZipEntry, ZIP_DATA_FILE, entry.getName())); - return new XmlSerialization().load(new InputStreamReader(zipin, StandardCharsets.UTF_8)); - } + return new XmlSerialization().load(new InputStreamReader(zipin, StandardCharsets.UTF_8)); } @Override public void save(Client client, OutputStream output) throws IOException { - // wrap with zip output stream - try (ZipOutputStream zipout = new ZipOutputStream(output)) + try (Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) { - zipout.setLevel(Deflater.BEST_COMPRESSION); - + // wrap with zip output stream + ZipOutputStream zipout = new ZipOutputStream(output); zipout.putNextEntry(new ZipEntry(ZIP_DATA_FILE)); - new XmlSerialization().save(client, zipout); - zipout.closeEntry(); + + new XmlSerialization().save(client, zipout); + zipout.closeEntry(); + + zipout.flush(); + zipout.finish(); + output.flush(); + writer.flush(); } } } @@ -202,25 +201,12 @@ public Decryptor(String method, char[] password) this.password = password; this.keyLength = "AES256".equals(method) ? AES256_KEYLENGTH : AES128_KEYLENGTH; //$NON-NLS-1$ } - - /** - * Reads all data in the given {@link InputStream}. - * - * @param is - * {@link InputStream} - * @throws IOException - */ - private static void readStreamUntilEnd(InputStream is) throws IOException - { - // use an appropriate buffer (64 KB here) - byte[] data = new byte[65536]; - while (is.read(data) != -1) - {} - } @Override public Client load(final InputStream input) throws IOException { + InputStream decrypted = null; + try { // check signature @@ -244,40 +230,29 @@ public Client load(final InputStream input) throws IOException byte[] iv = new byte[IV_LENGTH]; input.read(iv); - Client client; // build cipher and stream Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv)); - try (InputStream decrypted = new CipherInputStream(input, cipher)) - { - // read version information - byte[] bytes = new byte[4]; - decrypted.read(bytes); // major version number - int majorVersion = ByteBuffer.wrap(bytes).getInt(); - decrypted.read(bytes); // version number - int version = ByteBuffer.wrap(bytes).getInt(); - - // sanity check if the file was properly decrypted - if (majorVersion < 1 || majorVersion > 10 || version < 1 || version > 100) - throw new IOException(Messages.MsgIncorrectPassword); - if (majorVersion > Client.MAJOR_VERSION || version > Client.CURRENT_VERSION) - throw new IOException(MessageFormat.format(Messages.MsgUnsupportedVersionClientFiled, version)); - - // wrap with zip input stream - try (ZipInputStream zipin = new ZipInputStream(decrypted)) - { - zipin.getNextEntry(); + decrypted = new CipherInputStream(input, cipher); - client = new XmlSerialization().load(new InputStreamReader(zipin, StandardCharsets.UTF_8)); + // read version information + byte[] bytes = new byte[4]; + decrypted.read(bytes); // major version number + int majorVersion = ByteBuffer.wrap(bytes).getInt(); + decrypted.read(bytes); // version number + int version = ByteBuffer.wrap(bytes).getInt(); - // starting with a later jdk 1.8.0 (for example - // 1.8.0_25), a - // javax.crypto.BadPaddingException - // "Given final block not properly padded" is thrown if - // we do not read the complete stream - readStreamUntilEnd(decrypted); - } - } + // sanity check if the file was properly decrypted + if (majorVersion < 1 || majorVersion > 10 || version < 1 || version > 100) + throw new IOException(Messages.MsgIncorrectPassword); + if (majorVersion > Client.MAJOR_VERSION || version > Client.CURRENT_VERSION) + throw new IOException(MessageFormat.format(Messages.MsgUnsupportedVersionClientFiled, version)); + + // wrap with zip input stream + ZipInputStream zipin = new ZipInputStream(decrypted); + zipin.getNextEntry(); + + Client client = new XmlSerialization().load(new InputStreamReader(zipin, StandardCharsets.UTF_8)); // save secret key for next save client.setSecret(secret); @@ -288,6 +263,21 @@ public Client load(final InputStream input) throws IOException { throw new IOException(MessageFormat.format(Messages.MsgErrorDecrypting, e.getMessage()), e); } + finally + { + try + { + if (decrypted != null) + decrypted.close(); + } + catch (IOException ignore) + { + // starting with a later jdk 1.8.0 (for example 1.8.0_25), a + // javax.crypto.BadPaddingException + // "Given final block not properly padded" is thrown if the + // we do not read the complete stream + } + } } @Override @@ -325,22 +315,22 @@ public void save(Client client, final OutputStream output) throws IOException output.write(iv); // encrypted stream - try (OutputStream encrypted = new CipherOutputStream(output, cipher)) - { - // write version information - encrypted.write(ByteBuffer.allocate(4).putInt(Client.MAJOR_VERSION).array()); - encrypted.write(ByteBuffer.allocate(4).putInt(client.getVersion()).array()); + OutputStream encrpyted = new CipherOutputStream(output, cipher); - // wrap with zip output stream - try (ZipOutputStream zipout = new ZipOutputStream(encrypted)) - { - zipout.setLevel(Deflater.BEST_COMPRESSION); - zipout.putNextEntry(new ZipEntry(ZIP_DATA_FILE)); + // write version information + encrpyted.write(ByteBuffer.allocate(4).putInt(Client.MAJOR_VERSION).array()); + encrpyted.write(ByteBuffer.allocate(4).putInt(client.getVersion()).array()); - new XmlSerialization().save(client, zipout); - zipout.closeEntry(); - } - } + // wrap with zip output stream + ZipOutputStream zipout = new ZipOutputStream(encrpyted); + zipout.putNextEntry(new ZipEntry(ZIP_DATA_FILE)); + + new XmlSerialization().save(client, zipout); + + zipout.closeEntry(); + zipout.flush(); + zipout.finish(); + output.flush(); } catch (GeneralSecurityException e) { @@ -388,17 +378,17 @@ public static Client load(File file, char[] password, IProgressMonitor monitor) if (isEncrypted(file) && password == null) throw new IOException(Messages.MsgPasswordMissing); + InputStream input = null; + try { // progress monitor long bytesTotal = file.length(); int increment = (int) Math.min(bytesTotal / 20L, Integer.MAX_VALUE); monitor.beginTask(MessageFormat.format(Messages.MsgReadingFile, file.getName()), 20); - try (InputStream input = new ProgressMonitorInputStream(new BufferedInputStream(new FileInputStream(file)), - increment, monitor)) - { - return buildPersister(file, null, password).load(input); - } + input = new ProgressMonitorInputStream(new FileInputStream(file), increment, monitor); + + return buildPersister(file, null, password).load(input); } catch (FileNotFoundException e) { @@ -407,6 +397,11 @@ public static Client load(File file, char[] password, IProgressMonitor monitor) fnf.initCause(e); throw fnf; } + finally + { + if (input != null) + input.close(); + } } public static Client load(Reader input) throws IOException @@ -432,7 +427,7 @@ public static void save(final Client client, final File file, String method, cha if (isEncrypted(file) && password == null && client.getSecret() == null) throw new IOException(Messages.MsgPasswordMissing); - try (OutputStream output = new BufferedOutputStream(new FileOutputStream(file))) + try (OutputStream output = new FileOutputStream(file)) { buildPersister(file, method, password).save(client, output); }