DBCoffer与Oracle字符集问题探讨

引言

数据库保险箱(简称DBCoffer) 是一款基于Oracle扩展机制实现的,数据高度安全、应用完全透明、密文高效访问的Oracle数据安全增强产品。DBCoffer可以防止绕过防火墙的外部数据攻击、来自于内部的高权限用户的数据窃取、以及由于磁盘、磁带失窃等引起的数据泄密。

       作为一款Oracle数据安全增强产品,其中不可避免的需要对Oracle内部数据进行操作,其中主要是对Oracle里需要保护的数据进行加密处理,但因为DBCoffer是Oracle外部实现对数据的保护处理后,然后再次导进数据库,其中涉及数据的出与进,就会有字符集兼容及字符集转换等相关问题产生,遇到相关问题时,如何才能从容应对,这就要求开发者及测试者具有一定的Oracle字符集知识基础,下面就以字符集的一些相关知识原理为切入点,然后从后面的问题中一步一步深入探讨。

 

1 Oracle字符集简介

Oracle字符集是一个字节数据的解释的符号集合,有大小之分,有相互的包容关系。ORACLE 支持国家语言的体系结构允许你使用本地化语言来存储,处理,检索数据。它使数据库工具,错误消息,排序次序,日期,时间,货币,数字,和日历自动适应本地化语言和平台。 
    影响Oracle数据库字符集最重要的参数是NLS_LANG参数。它的格式如下: 
                   NLS_LANG =language_territory.charset 

它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。其中: Language 指定服务器消息的语言,territory 指定服务器的日期和数字格式,charset 指定字符集。如:AMERICAN _ AMERICA. ZHS16GBK 
     从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。当用“select userenv('language') from dual” 语句进行查询时,数据库服务端返回“language_territory.charset” 的结构,则charset对应为当前连接数据库字符集,而此查询结果也可以作为配置客户端字符集的依据。

     在数据存储方面,不得不提两个概念:数据库字符集和国家字符集。在安装Oracle时,可以指定数据库字符集和国家字符集,其作用是用本国语言和格式来存储、处理和检索数据,如用来存储CHAR, VARCHAR2, CLOB, LONG等类型数据。国家字符集实质上是为Oracle选择的附加字符集,主要作用是为了增强Oracle的字符处理能力,因为NCHAR数据类型可以提供对亚洲使用定长多字节编码的支持,只能在unicode编码中的AF16UTF16和UTF8中选择,用以存储NCHAR, NVARCHAR2, NCLOB等类型数据,默认值是AF16UTF16。

由于oracle字符集种类多,且在存储、检索、迁移oracle数据时多个环节与字符集的设置密切相关,因此在实际的应用中,数据库开发和管理人员经常会遇到有关Oracle字符集方面的问题。

 

2 Oracle常用字符集原理剖析

    在最初的数据库统中,字符集只有一种ASCII,由于ASCII支持的字符很有限,因此随后又出现了很多的编码方案,这些编码方案大部分都是包括了ASCII,下面要谈到的Oracle字符集US7ASCII就是一个7位的ASCII字符集。当然要理清Oracle所有的字符集不是件容易的事,下面就从一些当前较常用的Oracle字符集编码进行简单说明。

2.1 单字节编码

    单字节7位字符集,可以定义128个字符,最常用的字符集为 US7ASCII.

    单字节8位字符集,可以定义256个字符,适合于欧洲大部分国家,例如:WE8ISO8859P1(西欧、8位、ISO标准8859P1编码 )

2.2 多字节编码

    变长多字节编码,某些字符用一个字节表示,其它字符用两个或多个字符表示,变长多字节编码常用于对亚洲语言的支持,例如日语、汉语、印地语等,例如:AL32UTF8(其中AL代表ALL,指适用于所有语言)、 ZHS16GBK231280。

    定长多字节编码,每一个字符都使用固定长度字节的编码方案,目前Oracle唯一支持的定长多字节编码是AF16UTF16,也是仅用于国家字符集。

2.3 Unicode编码

   Unicode是一个涵盖了目前全世界使用的所有已知字符的单一编码方案,也就是说Unicode为每一个字符提供唯一的编码。UTF-16是Unicode的16位编码方式,是一种定长多字节编码,用2个字节表示一个Unicode字符,AF16UTF16是UTF-16编码字符集。UTF-8 是Unicode的8位编码方式,是一种变长多字节编码,这种编码可以用1、2、3个字节表示一个Unicode字符,AL32UTF8,UTF8、UTFE是UTF-8编码字符集。

当一种字符集(字符集A)的编码数值包含另一种字符集(字符集B)的全部编码数值,并且两种字符集相同编码数值代表相同的字符时,则字符集A是字符集B的超级,或称字符集B是字符集A的子集。由于US7ASCII是最早的Oracle数据库编码格式,因此有许多字符集是US7ASCII的超集,例如WE8ISO8859P1、ZHS16CGB231280、ZHS16GBK,Oracle内部字符集的转换只保证是由子集到超集的转换正常。

 

3 DBCoffer与Oracle的通信架构分析

有了上面字符集的基础知识后,再来谈DBCoffer的与Oracle的通信架构图,相信能够很快让你看懂图中有哪些地方涉及字符集的转换,如下图所示:应用程序与Oracle客户端、外部库与Oracle服务端、Oracle客户端与Oracle服务端、Oracle服务端与Exp导出及Imp导入与Oracle服务端,这里提到的点都是Oracle服务端直接通信且可能发生字符集。在DBCoffer下,主要是通过调用外部库,直接与DBCSecureService进行通信,从而对数据进行处理后再送回数据库,当然这仅仅是整个DBCoffer的冰山一角,下面我们就从通信两端字符一致与不一致情况来分别讨论可能发生的情况。

图 1

 

3.1 字符集一致时情况分析

相信大家都知道,在Oracle客户端与服务端之间,如果两端字符集一致是不会有字符集转换的,即在客户端输进去的是什么,那么数据库存储是就是什么,这就是为什么7位US7ASCII、8位WE8ISO8859P1及UTF8字符集类型时,对于中文数据支持也是相当的不错,但是用这些字符集来处理中文时,相应的客户端程序开发与维护难度也会增加不少,且乱码产生的可能性也比ZHS16GBK要高出不少。下面来看一个例子:JAVA THIN连接字符集为WE8ISO8859P1的Oracle数据库。

    首先对于JAVA THIN连接,也可以看成是Oracle的一种客户端,至于这个“客户端”所采用的字符集,通常是以JAVA默认字符集为参考。经过实验,当用JAVA THIN方式连接Oracle时,当服务端字符集为ZHS16GBK和UTF8时,对于DML语句的执行和结果集的显示,是不用进行相关字符集的转换操作,而当Oracle数据库字符集为WE8ISO8859P1时,问题就来了,对于需要执行的SQL语句,尤其是包含中文数据的SQL语句必须要先进行一个转码过程,转码函数参考如下:

public String changeCharset(String str, String newCharset, String oldCharset) throws UnsupportedEncodingException { 

        if(str != null) { 

            //将目标字符集转换成想要的字符集

            byte[] bs = str.getBytes(oldCharset); 

            return new String(bs, newCharset); 

        } 

        return null

    } 

表 1
 
其中DML语句进行一个由“GBK”编码到“ISO8859-1”编码的转换,而取出来的结果集则正好相反,需要由“ISO8859-1”编码到“GBK”编码的转换,方能正常显示中文。这里有人也许会心生疑问,为什么当服务端为UTF8字符集,就不需要进行转换呢?解释此问题需要用到上面提到的子集与超集的知识,utf-8 是UNICODE的一种变长字节编码,其实简单来说  utf-8几乎可以存放世界所有的文字。即UTF8是ZHS16GBK的超集,此过程中是不会有数据丢失的或改变的,而JAVA字符串默认的字符串编码是“GBK”,所以这就是为什么不论数据库字符集为ZHS16GBK还是UTF8时,JAVA是不需要对SQL语句及结果集进行相关转码处理的。这就是在DBCStudio中操作数据库字符集为WE8ISO8859P1时,有些提示信息为乱码的原因也在这里,需要进行一个转码处理再进行信息显示。
 

3.2 字符集不一致时情况分析

这里的不一致主要是指外部库调用进程extproc所依赖字符集与Oracle数据库字符集不一致,对于Oracle客户端和服务端要是在一台机器上时,可以通过IPC协议进行工作的,IPC是Inter-process Communication的简称,从而提高数据的访问速度。和Oracle客户端一样,外部过程所依赖的字符集也是由环变量NLS_LNAG决定,如果与其相关的应用系统设置的字符集与数据库服务端不同时,无疑给后期应用程序的开发与维护增加难度,且一旦出现需要字符集转换时,将面临程序运行异常和数据丢失等风险。这里结合DBC系统具体看看会有什么情况发生。

先修改下本地listener.ora,让Oracle启用外部过程调用功能,此处主要是修改Oracle的监听配置,相关配置参考如下表所示。

SID_LIST_LISTENER =

  (SID_LIST =

    (SID_DESC =

      (SID_NAME = ljb)

      (ORACLE_HOME = E:\oracle\product\10.2.0\db_1)

    )

       (SID_DESC =

      (SID_NAME = PLSExtProc)

      (ORACLE_HOME = E:\oracle\product\10.2.0\db_1)

      (ENVS = EXTPROC_DLLS=ANY)

      (PROGRAM = extproc)

    )

  )

LISTENER =

  (DESCRIPTION_LIST =

    (DESCRIPTION =

      (ADDRESS = (PROTOCOL = IPC)(KEY = extproc))

      (ADDRESS = (PROTOCOL = TCP)(HOST = Jiabo)(PORT = 1521))

    )

  )

 

表 2

 

下面来举个例子,当数据库字符集为ZHS16GBK,注册表NLS_LANG的值为SIMPLIFIED CHINESE_CHINA.ZHS16GBK,启动DBCoffer,并对一个普通Schema下的表进行加密处理,此表的记录数为830,选择一个NUMBER类型字段数据进行加密,此时的加密动作正常完成。然后添加系统环境变量NLS_LANG= AMERICAN_AMERICA.UTF8,注意一定要重启机器,让环境变量同步到注册表。重新启动后,当用PL/SQL Developer连接Oracle时,会弹出客户端与服务端字符集不一致的警告对话框,然后再删除系统环境变量NLS_LANG,再次用PL/SQLDeveloper连接Oracle时,则没有字符集不一致警告,说明客户端字符集已经是ZHS16GBK。

但当授权进行查询时,问题来了,错误信息为:ORA-01722:无效数字。当定位低层表进行查询时,发现底层表对应的密文字段出现了长度仍是一致的,结果如下表所示:

SQL> select count(*) from odct#orders where lengthb(customerid)=18 and lengthb(e

mployeeid)=18;

 

  COUNT(*)

----------

        830

表 3

 

查询时就已经出现了数据解密不正确。删除系统环境变量NLS_LANG,重启监听和Oracle实例,情况依旧,而此时注册表中的系统级环境变量NLS_LANG为AMERICAN_AMERICA.UTF8仍然存在,再次重启机器后,数据查询变得正常,所以可以肯定extproc进程参考字符集是取自注册表中的环境变量NLS_LANG。

这里为了配置的灵活以及及时生效,这里推荐将NLS_LANG配置到监听器中,将表2中的红色部分替换成

(ENVS ="EXTPROC_DLLS=ANY,NLS_LANG=AMERICAN_AMERICA.UTF8");

然后重启监听即可生效。

 

4ZHS16GBK导出导入问题探讨

    在此问题讨论之前,需要明确一下GBK的编码原理,其内码空间为0x8140 - 0xFEFE,即一个汉字包含两个字节,下面是GBK编码的ASCII值表述:
第一个字节                 第二个字节
GBK      |  x81-0xFE(129-254)    |   0x40-0xFE(64-254)
表 5

而Oracle中ZHS16GBK的字符集转换也是同样道理,当Oracle服务端读到第一个字节内容为129-254时,会去找下一个字节,从而组成一个完整的汉字编码。

当IPC协议的程序两端字符集都一致时,我们知道当数据传输到数据库端时是不会发生字符集转换的,即数据直接入库。问题来了,当加密后的一个密文字段为一个奇数值长度时,由于数据的多样性,加密后的密文数据很难保证最后一个字节的ASCII值不在129-254,先看下表:

字节编号

字节1

字节2

字节3

……

字节2n-1

字节2n

字节2n+1

ASCII值

129-254

表 4

但由于没有发生字符集的转换,所以存进数据库时也不会有数据的丢失,但是一旦有字符集的转换时,则有可能出现完成前2n个字节的字符匹配后,剩下字节2n+1则会去找其下一个字节,从而组成一个GBK字符编码,显然已经没有地方去找,此时必然出现异常或是数据丢失。

下面举个例子,来具体说明。利用老版本的DBCoffer系统加密一个字段,然后将此字段所在密文表用EXP命令导出数据库,重命名底层密文表,再用IMP命令导回数据库,此时再次比较两个密文字段长度,如下图所示:

图 2

发现新导入的表的部分密文数据的有一个字节数据丢失。现再次将刚才导进和表用EXP命令再导出来,看看数据丢失发生在是第一次导出还是导入环节,用十六进制比较工具比较相应两个导出二进制数据文件,如下图所示:

图 3

图片左侧为第一次的导出的数据文件,图片右侧为第二次的导出数据文件,很显然,数据的丢失发生在数据导入时,且丢失的字节在81-FE之间,且对应的设备控制信号11变成了数据传送换码信号10。此点正好对应了前面的理论,前2n个字节已经完成了相应字符解析,最后的第2n+1个字节需要再去找下一个字节,但此时已经到末尾,Oracle则认为这是一个无效字节,从而丢弃,但对于DBCoffer系统来说,这可就破坏了数据的完整性,从而导致DBCoffer的运行异常。

    通过以上分析,相应问题出现的前因后果都有所了解了,现在主要是如何解决问题,看懂此文的读者相信到此也已经有自己的解决方案了,但BDCoffer做得更优,为了更具兼容性,DBCoffer采用了两套方案解决此问题,且可以灵活配置,关于具体解决方案有兴趣的朋友可以发邮件至:liaojiabo@schina.cn

 

5 DBCoffer支持Oracle字符集探索

按照理论,DBCoffer对于Oracle的常用字符集应该都是支持的,但这并不是简单的几个小小测试就一蹴而就的,需要对Oracle各种字符集编码有相当深厚的了解,而且Oracle字符集相关知识涉及面较广,需要静下心来仔细思考其中蕴含的原理。

由于开发团队不懈的努力和严谨的工作态度,现在DBCoffer对ZHS16GBK,US7ASCII,WE8ISO8859P1字符集支持较好,UTF8正在测试当中。相信不久后,DBCoffer将能够兼容更多的Oracle常用字符集。
    本文通过理论加实验给大家展示了有关DBCoffer下与Oracle的相关内容,供参考,希望对大家有帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值