【openGauss】用plpgsql实现sha256算法(踩坑填坑实录)
前言
在开发中,经常会使用类似MD5/ SHA-1/ SHA-2这样的hash算法来对数据进行处理,以防原始信息泄露,比如密码的存储。
SHA-2中的SHA256,安全性比MD5更高,因此很多数据库增加了对SHA256计算的支持。
比如从oracle12c起,数据库内原生提供了DBMS_CRYPTO包和STANDARD_HASH函数,可以用来进行sha256计算。在postgresql中,可以通过安装pgcrypto扩展来支持,安装命令也很简单。
create extension pgcrypto;
但是,如果是基于postgresql的openGauss(2.1.0),事情就没这么简单了,需要搭建编译环境、下载源码、编译等一系列操作。
但好在,openGauss2.1.0支持了oracle风格的package对象,只要我们能找到有人为oracle写的plsql包,那么大概率是可以移植到openGauss里去的。
想到几年前我参与的某个项目,需要使用oracle11g数据库调用外部http接口,而这个接口需要使用sha256算法来计算签名,此版本的oracle是不支持sha256算法的,所以在网上找到了一个开源的plsql包
https://github.com/CruiserX/sha256_plsql
开始进行适配
二话不说,先直接往openGauss里干,注意数据库要为A兼容模式。(参考https://www.modb.pro/db/239429)
结果当然是报错了。
首先,这个包使用了utl_raw这个包,这在openGauss里是不会自带的,不过我在compat-tools里增加了这个包,因此需要先安装compat-tools,或者单独安装utl_raw这个包
compat-tools下载
utl_raw下载
1. type table 不支持
TYPE ta_number IS TABLE OF NUMBER INDEX BY binary_integer;
这是定义一列number类型的表作为类型,既然报错,那么去掉ta_number的定义,把下面的引用了ta_number的全部改成number[]
2. PL/pgSQL functions cannot accept type record
TYPE tr_ctx IS RECORD (
H TA_NUMBER, --//8
total TA_NUMBER, --//2
buflen NUMBER,
buffer32 TA_NUMBER --//32
);
去掉包内的tr_ctx定义,创建个单独的type(注意重名冲突问题)
CREATE TYPE tr_ctx AS (
H NUMBER[], --//8
total NUMBER[], --//2
buflen NUMBER,
buffer32 NUMBER[] --//32
);
3. 不支持参数的 NOCOPY属性
PROCEDURE sha256_init_ctx (ctx IN OUT NOCOPY TR_CTX);
直接把nocopy全部替换成空。
至此包头编译过去了,但编译包体提示
syntax error at end of input
而且不说是哪一行(在oracle中会提示错误的具体位置),只能逐段注释编译,最终发现了一个其实没有语法问题的位置
4. syntax error at end of input
add := CASE WHEN 128 - left_over > t_len THEN t_len ELSE 128 - left_over END;
这一段单独放在其他过程里面是不会有问题的,但是在这里报错了,原因不明,疑似bug。起初我以为是add关键词的原因,但改了后报相同的错。直接改成select into就好了
select CASE WHEN 128 - left_over > t_len THEN t_len ELSE 128 - left_over END into add;
然后包体也编译过去了。不过事情还没完,尝试调用这个包计算sha256,报错
5. ERROR: schema "ctx" does not exist
经翻阅相关文档,发现复合类型此处会存在一个歧义,文档中说是要用(ctx).col这种方式来避免歧义,但实测此方法此处不适用,因为这么改了后会提示 "("附近的语法有错误。然后我尝试把所有取数组指定位置元素的()改成了[],比如
ctx.buffer32(left_over_blk)
改成
ctx.buffer32[left_over_blk]
注意,要改动的地方多达五十多处,一个都不能漏
再尝试调用,就没报这个错了,但报了个新的错误
6. ERROR: invalid number format model
to_number(utl_raw.substr(t_buffer,idx,1),'0x')
这里是因为openGauss的to_number不支持 '0x'这个格式,其实这个和'xx'是一样的,因此把所有的'0x'直接替换成'xx' .
接着调用,报新的错误
7. ERROR: function "sha256_finish_ctx" with 1 parameters doesn't exist
PROCEDURE sha256_finish_ctx (ctx IN OUT TR_CTX,
resbuf OUT NUMBER[]);
sha256_finish_ctx(ctx, res);
这个目测和pg内核有关,这明明是个PROCEDURE,报错提示却是个function;而且调用这个过程时明明是传的两个参数,提示却说不存在1个参数的它,当我把resbuf这个参数也改成 in out,这个错就不报了,但又报了个新的错误
8. ERROR: array value must start with "{" or dimension information
我跟踪到,还是此处的问题
sha256_finish_ctx(ctx, res);
res这个参数,在此过程内部,最后得到了准确的值,但是一跳出这个过程,这个参数就变成了空。
此处应该也是pg内核的一个问题,对于过程中的数组类型参数,不会传出值,于是我把它改成了一个函数,用return返回res参数。
测试
至此,这个包终于可以调用成功了,检查输出结果
select sha256.ENCRYPT('aa') ;
--961b6dd3ede3cb8ecbaacbd68de040cd78eb2ed5889130cceb4c49268ea4d506
然后在oracle中查询
可以发现结果完全一致。
但是,我发现个很严重的问题,上述的这个sql,我在oracle中查询只需要0.277秒,但是在openGauss中查询则需要3秒。
猜测原因,一方面可能与我写的UTL_RAW包有关,毕竟不是用的原生c语言开发的;另一方面nocopy这个特性的确能提高性能;再者就是两个数据库运行的环境是存在差异的。
总结
虽然计算耗时比较严重,但是对一些速度要求不高的非频繁计算请求,有了总比没有好。上面列了8个点的问题,这也没有对这个包整体的逻辑进行大幅调整,即可"正常"使用,这也说明了openGauss对oracle语法强大的兼容性。
但是,如果对性能有强烈要求,建议还是安装相关扩展来使用。
最后附上我修改完的代码
https://gitee.com/darkathena/sha256-opengauss
后记
20220428更新:
基于最新的mogdb3.0.0测试版进行了下适配,发现只需要修改nocopy 和 to_number里的'0x'两处地方即可,并且查询时长小于0.01s!
- 本文作者: DarkAthena
- 本文链接: https://www.darkathena.top/archives/sha256-opengauss
- 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
posted on 2022-04-27 20:29 DarkAthena 阅读(283) 评论(3) 编辑 收藏 举报