The issue we're encountering is that we're able to use Jackcess Encrypt to decrypt most of our Access .accdb databases which we believe were created in 2013 and with encryption method set to legacy, but not others. And we're not sure exactly what was done differently to create the working ones vs. the failing ones. Possibly a mistaken encryption method selection, possibly a group policy or registry difference, or something else.
Here's the stack trace for the files that fail (Jackcess Encrypt 2.1.0):
java.lang.IllegalStateException: EncryptionHeader[_flags=12,_sizeExtra=0,_cryptoAlg=AES_256,_hashAlg=SHA1,_keySize=256,_providerType=24,_cspName=Microsoft Enhanced RSA and AES Cryptographic Provider] crypto algorithm must be one of [RC4]
at com.healthmarketscience.jackcess.impl.office.EncryptionHeader.read(EncryptionHeader.java:186)
at com.healthmarketscience.jackcess.impl.office.RC4CryptoAPIProvider.<init>(RC4CryptoAPIProvider.java:52)
at com.healthmarketscience.jackcess.impl.OfficeCryptCodecHandler.create(OfficeCryptCodecHandler.java:118)
at com.healthmarketscience.jackcess.CryptCodecProvider.createHandler(CryptCodecProvider.java:114)
at com.healthmarketscience.jackcess.impl.PageChannel.initialize(PageChannel.java:105)
at com.healthmarketscience.jackcess.impl.DatabaseImpl.<init>(DatabaseImpl.java:516)
at com.healthmarketscience.jackcess.impl.DatabaseImpl.open(DatabaseImpl.java:389)
at com.healthmarketscience.jackcess.DatabaseBuilder.open(DatabaseBuilder.java:248)
...</init></init>
We got as far as putting breakpoints in:
com.healthmarketscience.jackcess.impl.OfficeCryptCodecHandler.create(PasswordCallback callback, PageChannel channel, Charset charset).
For the file that failed, the flow gets to:
else if(((vMajor == 2) || (vMajor == 3) || (vMajor == 4)) && (vMinor == 2))
Then into:
if(EncryptionHeader.isFlagSet(flags, EncryptionHeader.FCRYPTO_API_FLAG)) {
Then into:
} else {
Where it gets to the following line, which we were surprised to see since the header says AES_256 rather than RC4:
// OC: 2.3.5.1 - RC4 CryptoAPI Encryption: (2,3,4),2
handler = new RC4CryptoAPIProvider(channel, encodingKey, encProvBuf, pwdBytes);
But even when forcing the flags to get the flow to the following line, it gets further along but eventually errors out anyway:
// OC: 2.3.4.5 - Standard Encryption: (3,4),2
handler = new ECMAStandardEncryptionProvider(channel, encodingKey, encProvBuf, pwdBytes);
MS Access 2013 can open the same database that fails.
Attaced is a newly created DB from Access 2013 using the "legacy" encryption method. It fails in the same way. The password of the file is 'password'.
At a first pass, i can't make heads or tails of it. The various encryption header values seem to violate what is allowed according to the crypto api docs ( https://msdn.microsoft.com/en-us/library/dd910529(v=office.12).aspx ). tweaking the code to force it down the ecma route seems to fail when verifying the password. likewise, forcing the algorithm to rc4 also fails.
Here's what we see regarding the seeming violation:
We read the first 4,096 bytes from the file, of which offset 0x299 marks the start of the cryptographic header, according to spec.
Some values to note from the test file:
• Length of encryptionHeader: 224
• vMajor: 4
• vMinor: 2
• Flags: 12
• AlgId: 26128 -> 0x00006610
• AlgId hash: 32772
• Key Size: 256
Per the spec, the value of “Flags” is used to determine if Flags.fCryptoAPI, Flags.fAES and Flags.fExternal are set.
According to the spec, the flag layout for AlgId: 0x00006610 must be the following:
Flags.fCryptoAPI Flags.fAES Flags.fExternal AlgID Algorithm
1 1 0 0x00006610 256-bit AES
But, the test file is displaying what appears to be an invalid header:
Flags.fCryptoAPI Flags.fAES Flags.fExternal AlgID Algorithm
1 0 0 0x00006610 256-bit AES
For comparison looking at a working access file shows the following header:
Flags.fCryptoAPI Flags.fAES Flags.fExternal AlgID Algorithm
1 0 0 0x00006801 RC4
We appreciate your help.
And, what's more troubling is that even if you force the code to use AES, the code can't decrypt the file successfully. Which seems to imply that the actual encryption used isn't following the spec either (even allowing for the confused flags).
Do you know what is causing the computer in question to generate the alternate encryption configuration?
We don't know. We have considered the following possibilities:
We've put a request in to Microsoft to see if they can tell us more about this too.
Last edit: JLPP 2016-11-30
yes, i can open the file just fine in ms access (2010 and 2013).
it would be excellent if you could get some details from microsoft, as i don't have any ideas at this point.
Do the systems that generate these files have any interesting values in the registry for:
HKCU\Software\Policies\Microsoft\Office\14.0\Common\Security
HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\Security\Crypto\CompatMode
I have the following CompatMode setting:
[HKEY_CURRENT_USER\Software\Policies\Microsoft\office\15.0\access\security\crypto]
"compatmode"=dword:00000000
By the way, we've given Microsoft a reproducible test case and spelled out the issue. Namely that the decrypt_fails.accdb fAES comes out as 0, the AlgID comes out as 0x6610, and the spec doesn't allow this combination (http://interoperability.blob.core.windows.net/files/MS-OFFCRYPTO/[MS-OFFCRYPTO].pdf pg 33).
We'll keep you updated with any news from the ticket.
In Access 2013, with encryption method set to legacy, we tried the following registry settings and got these results in Jackcess. Hopefully this will allow others to experiment to see how the accdb file header changes, whether in agreement or disagreement with Microsoft's spec.
CompatMode => HKEY_CURRENT_USER\ Software\ Policies\ Microsoft\ office\ 15.0\ access \security\ crypto\ compatmode
defaultencryption12 => HKEY_CURRENT_USER\ Software\ Policies\ Microsoft\ office\ 15.0\ common\ security\ defaultencryption12
AES 256 => Microsoft Enhanced RSA and AES Cryptographic Provider,AES 256,256
RC4 40 => Microsoft Enhanced Cryptographic Provider v1.0,RC4,40
Last edit: JLPP 2016-12-09
That's curious that jackess can handle the rc4 encryption in all cases. what headers end up in the access db in those cases?
The results of the RC4 40 CompatMode tests are below. Note that we copied Jackcess's header code and printed the results. For CompatMode 1 this code failed (see exception stack) but Jackcess was still able to decrypt the file and read the table. We probably didn't copy all of the necessary header read code so I think this is a false negative. Accdb files are attached.
Last edit: JLPP 2016-12-12
We've heard more from Microsoft support on this. Here are the highlights:
a. We should not be using defaultencryption12 of AES 256 with the legacy encryption method in Access (as was the case for decrypt_fails.accdb). This is not a supported configuration because the legacy encryption method must use a stream cipher such as RC4. Though a file can be generated using the legacy encryption method and a block cipher (e.g. AES – see decrypt_fails.accdb) and it may be readable by Access itself and Microsoft libraries, it is still not supported.
Documents that are saved in the older Office binary formats can only be encrypted by using RC4 to maintain compatibility with older versions of Office. AES, the default and recommended encryption algorithm, is used to encrypt Open XML Format files.
References:
Compatibility with previous versions of Office - https://technet.microsoft.com/en-us/library/cc179125.aspx
b. If we want to use the legacy encryption method in Access in a supported configuration, we must use the RC4 provider, not AES.
c. If we want to use RC4 40, we should remove the defaultencryption12 key, leave the compatmode set to 0 (or unset?), and continue to use the legacy encryption method in Access. Files will then be written as RC4 40.
d. If we want to use RC4 128, we should set defaultencryption12 to RC4 128, leave the compatmode to 0, and continue to use the legacy encryption method in Access. Files will then be written as RC4 128.
Microsoft did not comment on what is happening when the legacy encryption method is used with block cipher. For instance, is the legacy setting ignored and the cipher overrides? Or vice versa? They only warned that the results could be unpredictible.
So, it sounds like jackcess encrypt is handling the "valid" options. As for the "unpredictable" results that ms access clearly handles, i have no ideas. i tried a variety of different possibilities (both aes/rc4 and "stream" versions of aes) and couldn't find anything which successfully decrypted the file.
so, is this ticket essentially a "won't fix"? i mean, i'd certainly love to make it work even in this unsupported case, but i don't see a means to figuring it out...
Last edit: James Ahlborn 2016-12-19
James,
As requested I finally had few hours to look into the code and compatibility. Here is high level logic that would need to be implemented to support the attached decrypt_fails.accdb.
For this use case OfficeCryptCodecHandler needs to use ECMAStandardEncryptionProvider despite FAES_FLAG not being set.
In the mode used with the DB the 50K hashing iterations need to be skipped. The hacky code to illustrate is:
With above hacks I am able to open the DB.
Hope this will allow you to add support for this unsual mode of encryption
Vladimir
Last edit: Vladimir B. 2017-01-29
Good news, I think we now have a working algorithm! (just got a chance to test Vladimir's changes out) Hopefully i will be able to get it into the code base in the next few days.
Thanks again, Vladimir!
Last edit: James Ahlborn 2017-01-30
I've made changes to trunk, targeted at the 2.1.2 release, which work with the example database. It would be great if you could test out the changes on your real databases.
Wow! Thank you to you both, James and Vladimir. We will do what we can to test the latest changes with our sample Access databases over the next few days and let you know what we find. Thanks again.
All tests passed. Tested the following scenarios:
defaultencryption12 + aes 256 + compatmode 0
defaultencryption12 + aes 256 + compatmode 1
defaultencryption12 + aes 256 + no compatmode
defaultencryption12 + rc4 128 + compatmode 0
defaultencryption12 + rc4 128 + compatmode 1
defaultencryption12 + rc4 128 + no compatmode
defaultencryption12 + rc4 40 + compatmode 0
defaultencryption12 + rc4 40 + compatmode 1
defaultencryption12 + rc4 40 + no compatmode
Thank you again.
awesome!
fixed in trunk, will be in the 2.1.2 release.