Jackcess:MSAccess数据库错误的字符集

时间:2017-03-31 17:04:02

标签: java excel encryption jackcess

我有一个MS-Access数据库,其中包含#34;加密"字符串在里面。这些看起来像这样: encrypted strings

但是,我很快注意到这些字符串的长度与明文的长度完全匹配(我知道明文)。所以稍微尝试使用Excel,我发现如果你使用=CODE(<char>) - 函数(所以你得到默认字符集中的字符代码,而=CHAR(<number>)反之亦然)并且xor这个数字与符号应代表您的字母的字符代码始终获得相同的结果。这意味着我只需要在java和voila中创建一个包含这些值的数组。 Excel示例(右侧提到&#34;数组&#34;): excel example decoding 示例:&#34;&gt;&gt;&#34;索引为(dec)187,因此187xor253得到70 =&gt; &#34; F&#34;

现在,我使用jackcess来访问这些值和&#34;解密&#34;大多数情况下很好,但我有时会从字符串中得到错误的字符。在Excel中一切正常。代码效果最佳:

public static final int[] DECRYPT_KEY = { 253, 203, 204, 217, 226, 205, 128, 201, 222, 183, 58, 217, 230, 201, 183, 211, 158, 203, 167, 213, 35, 33, 201, 123, 186, 247 };

public static void main(String[] args) throws IOException
{
    System.out.println(System.getProperty("file.encoding"));


    Database db = DatabaseBuilder.open(new File("/home/***/TM.db"));                
    Table table = db.getTable("personal");

    for (Row row : table)
    {
        String vorname = row.getString("vorname");
        byte[] vornameArr = vorname.getBytes("cp1252");
        for (int i1 = 0; i1 < vornameArr.length; i1++)
        {               
            vornameArr[i1] = (byte) ((vornameArr[i1] & 0xff) ^ DECRYPT_KEY[i1]);
        } 

        System.out.println(new String(vornameArr, "cp1252"));
    }
}

但正如我所说,有些字符仍然是错误的,但在Excel中一切都很好。当我打印出getBytes("cp1272")给出的数字时,它与Excel的数字完全不同。

你有什么想法,我可能做错了什么以及为什么java有时会提供比Excel更不同的值?什么是更好的方法?我已经尝试了所有charsets的组合,有些在其他人失败的情况下工作,但后来有其他错误的结果。

2 个答案:

答案 0 :(得分:1)

我能够通过使用您问题中的字节值来破解数据库文件来重新创建您的问题。这条线

byte[] vornameArr = vorname.getBytes("cp1252");

尝试将vorname字符转换为cp1252字节,但没有与U + 008F(十进制143,SINGLE SHIFT THREE)对应的cp1252字符,因此Java将该字符转换为问号(0x3F) )。因此,您的解码步骤是解码0x3F而不是0x8F,这就是为什么你得到“FadiÝa”而不是“Fadima”。

通过用

替换上面的单行,我能够得到正确的结果
byte[] doubleBytes = vorname.getBytes("UTF-16LE");  // 187 0 170 0 168 0 ...
byte[] vornameArr = new byte[doubleBytes.length / 2];
for (int i = 0; i < vornameArr.length; i++) {
    vornameArr[i] = doubleBytes[i * 2];  // remove nulls
}

然后通过解码循环运行vornameArr个字节。 (如果您愿意,也可以在上面的循环中应用解码转换。)

答案 1 :(得分:1)

感谢@Gord Thompson和他建议的网站(fileformat.info)我终于找到了答案:有时字符看起来很相似,并且由于某种原因,在数据库中,“更高”的字符是首选(例如unicode字符402和131)。我的java代码期望所有内容都具有较低的值,因为excel提供了它。因此,如果代码高于255,则需要用较低的值代替。出于某种原因,getBytes("cp1252")将始终返回较低的值,但toCharArray()getBytes("UTF-16LE")将返回更高的正确值(比较:fileformat 192

所以我的代码就像现在一样完美无缺:

String vorname = row.getString("vorname");
char[] vornameArr = vorname.toCharArray();          
for (int i = 0; i < vornameArr.length; i++)
{
    if (vornameArr[i] > 255)
    {
        vornameArr[i] = (char) (String.valueOf(vornameArr[i]).getBytes("cp1252")[0] & 0xff);
    }

    vornameArr[i] = (char) (vornameArr[i] ^ DECRYPT_KEY[i]);
}

System.out.println(String.valueOf(vornameArr));

非常感谢你的帮助!