Skip to content

Commit

Permalink
This pull request fixes the issue #162 that the encryption code is ob…
Browse files Browse the repository at this point in the history
…solete and supports only Blowfish/SHA1.

It is now possible to save files with the old Blowfish encryption as well as with the AES256-CBC encryption.
By default, the data is stored with AES256 CBC encryption. Only files that still have the old encryption are still saved with this encryption.

resolves #162
  • Loading branch information
lawern authored and mistmist committed Nov 28, 2024
1 parent 3456269 commit 36ef92c
Showing 1 changed file with 49 additions and 42 deletions.
91 changes: 49 additions & 42 deletions odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfPackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ public class OdfPackage implements Closeable {
private OdfManifestDom mManifestDom;
private String mOldPwd;
private String mNewPwd;
private boolean isAES = true;

/* Commonly used files within the ODF Package */
public enum OdfFile {
Expand Down Expand Up @@ -1913,17 +1914,18 @@ private byte[] encryptData(byte[] data, OdfFileEntry fileEntry) {

// 3. The start key is generated: the byte sequence
// representing the password in UTF-8 is used to
// generate a 20-byte SHA1 digest.
byte[] passBytes = mNewPwd.getBytes("UTF-8");
MessageDigest md = MessageDigest.getInstance("SHA1");
// generate a 20-byte SHA-1 or a 32-byte SHA-256 digest.
byte[] passBytes = mNewPwd.getBytes(StandardCharsets.UTF_8);
MessageDigest md = MessageDigest.getInstance(isAES ? "SHA-256" : "SHA-1");
passBytes = md.digest(passBytes);

// 4. Checksum specifies a digest in BASE64 encoding
// that can be used to detect password correctness. The
// digest is build from the compressed unencrypted file.
md.reset();
md.update(compressedData, 0, (compressedDataLength > 1024 ? 1024 : compressedDataLength));
byte[] checksumBytes = new byte[20];
md.digest(checksumBytes, 0, 20);
md.update(compressedData, 0, (Math.min(compressedDataLength, 1024)));
byte[] checksumBytes = new byte[isAES ? 32 : 20];
md.digest(checksumBytes, 0, checksumBytes.length);

// 5. For each file, a 16-byte salt is generated by a random
// generator.
Expand All @@ -1932,39 +1934,36 @@ private byte[] encryptData(byte[] data, OdfFileEntry fileEntry) {
byte[] salt = new byte[16];
secureRandom.nextBytes(salt);

// char passChars[] = new String(passBytes, "UTF-8").toCharArray();
/*
* char passChars[] = new char[20]; for (int i = 0; i <
* passBytes.length; i++) { passChars[i] = (char)
* ((passBytes[i]+256)%256);
* //System.out.println("passChars[i]:"+passChars
* [i]+", passBytes[i]"+passBytes[i]); } //char passChars[] =
* getChars(passBytes); // 6. The PBKDF2 algorithm based on the
* HMAC-SHA-1 function is used for the key derivation.
* SecretKeyFactory factory =
* SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); // 7. The
* salt is used together with the start key to derive a unique
* 128-bit key for each file. // The default iteration count for the
* algorithm is 1024. KeySpec spec = new PBEKeySpec(passChars, salt,
* 1024, 128); SecretKey skey = factory.generateSecret(spec); byte[]
* raw = skey.getEncoded(); // algorithm-name="Blowfish CFB"
* SecretKeySpec skeySpec = new SecretKeySpec(raw, "Blowfish");
*/
byte[] dk = derivePBKDF2Key(passBytes, salt, 1024, 16);
SecretKeySpec key = new SecretKeySpec(dk, "Blowfish");
// 8.The files are encrypted: The random number
// generator is used to generate the 8-byte initialization vector
// for the
// algorithm. The derived key is used together with the
// initialization
// vector to encrypt the file using the Blowfish algorithm in cipher
// feedback
// CFB mode.
Cipher cipher = Cipher.getInstance("Blowfish/CFB/NoPadding");
// 6-7. Key derivation:
// The PBKDF2 algorithm based on the HMAC-SHA-1 function is used for the key derivation.
SecretKeySpec key;
if (isAES) {
// The salt is used together with the start key to derive a unique 2048-bit key for each
// file.
// The default iteration count for the algorithm is 100000.
PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(new SHA1Digest());
generator.init(passBytes, salt, 100000);
KeyParameter keyParam = (KeyParameter) generator.generateDerivedParameters(256);
key = new SecretKeySpec(keyParam.getKey(), "AES");
} else {
// The salt is used together with the start key to derive a unique 128-bit key for each
// file.
// The default iteration count for the algorithm is 1024.
byte[] dk = derivePBKDF2Key(passBytes, salt, 1024, 16);
key = new SecretKeySpec(dk, "Blowfish");
}

// 8. The files are encrypted:
// The random number generator is used to generate the 8-byte initialization vector
// for the algorithm. The derived key is used together with the initialization
// vector to encrypt the file using the Blowfish algorithm in cipher feedback
// CFB/CBC mode.
Cipher cipher =
Cipher.getInstance(isAES ? "AES/CBC/ISO10126Padding" : "Blowfish/CFB/NoPadding");
// initialisation-vector specifies the byte-sequence used
// as an initialization vector to a encryption algorithm. The
// initialization vector is a BASE64 encoded binary sequence.
byte[] iv = new byte[8];
byte[] iv = new byte[isAES ? 16 : 8];
secureRandom.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
Expand All @@ -1978,28 +1977,36 @@ private byte[] encryptData(byte[] data, OdfFileEntry fileEntry) {
if (encryptionDataElement != null) {
fileEntryElement.removeChild(encryptionDataElement);
}
encryptionDataElement = fileEntryElement.newEncryptionDataElement(checksum, "SHA1/1K");
encryptionDataElement =
fileEntryElement.newEncryptionDataElement(
checksum,
isAES ? "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha256-1k" : "SHA1/1K");
String initialisationVector = new Base64Binary(iv).toString();
AlgorithmElement algorithmElement =
OdfElement.findFirstChildNode(AlgorithmElement.class, encryptionDataElement);
if (algorithmElement != null) {
encryptionDataElement.removeChild(algorithmElement);
}
algorithmElement =
encryptionDataElement.newAlgorithmElement("Blowfish CFB", initialisationVector);
encryptionDataElement.newAlgorithmElement(
isAES ? "http://www.w3.org/2001/04/xmlenc#aes256-cbc" : "Blowfish CFB",
initialisationVector);
String saltStr = new Base64Binary(salt).toString();
KeyDerivationElement keyDerivationElement =
OdfElement.findFirstChildNode(KeyDerivationElement.class, encryptionDataElement);
if (keyDerivationElement != null) {
encryptionDataElement.removeChild(keyDerivationElement);
}
keyDerivationElement = encryptionDataElement.newKeyDerivationElement(1024, "PBKDF2", saltStr);
keyDerivationElement =
encryptionDataElement.newKeyDerivationElement(isAES ? 100000 : 1024, "PBKDF2", saltStr);
StartKeyGenerationElement startKeyGenerationElement =
OdfElement.findFirstChildNode(StartKeyGenerationElement.class, encryptionDataElement);
if (startKeyGenerationElement != null) {
encryptionDataElement.removeChild(startKeyGenerationElement);
}
encryptionDataElement.newStartKeyGenerationElement("SHA1").setKeySizeAttribute(20);
encryptionDataElement
.newStartKeyGenerationElement(isAES ? "http://www.w3.org/2000/09/xmldsig#sha256" : "SHA1")
.setKeySizeAttribute(isAES ? 32 : 20);

// System.out.println("full-path=\""+ path +"\"");
// System.out.println("size=\""+ data.length +"\"");
Expand Down Expand Up @@ -2033,7 +2040,7 @@ private byte[] decryptData(
byte[] passBytes = mOldPwd.getBytes(StandardCharsets.UTF_8);

String algorithm = algorithmElement.getAlgorithmNameAttribute();
boolean isAES = algorithm.contains("aes256-cbc") || algorithm.contains("AES-256-CBC");
isAES = algorithm.contains("aes256-cbc") || algorithm.contains("AES-256-CBC");

SecretKeySpec key;
Cipher cipher;
Expand All @@ -2047,7 +2054,7 @@ private byte[] decryptData(
generator.init(passBytes, salt, 100000);
KeyParameter keyParam = (KeyParameter) generator.generateDerivedParameters(256);
key = new SecretKeySpec(keyParam.getKey(), "AES");
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher = Cipher.getInstance("AES/CBC/ISO10126Padding");
md.reset();
} else {
// Blowfish
Expand Down

0 comments on commit 36ef92c

Please sign in to comment.