在GBase 8s上使用java udr实现Oracle兼容的加密解密功能

  最近碰到客户从Oracle迁移到GBase 8s使用到字段加密解密功能,使用到了Utl_raw.cast_to_varchar2、Utl_raw.cast_to_raw、Utl_encode.base64_decode、dbms_obfuscation_toolkit.desdecrypt等oracle特有函数,这些与GBase 8s自带的加密函数DECRYPT_CHAR并不兼容。客户暂时只能考虑使用程序加密的方式实现。
  对于GBase 8s数据库来说,其实也可以使用自定义例程(UDR,可以是基于C,也可能是基于Java)的方式来扩展函数使用。以下我们就以这个加密解密功能来说明Java UDR的创建与使用。

使用的操作系统:CentOS7.6 64bit
数据库版本:GBase8sV8.7_2.0.1a2_2

GBase 8s自带的JRE版本是1.6,故创建java程序时,特性需要与java 1.6版本兼容。在java 1.6中,我们使用到了commons-codec-1.9.jar。

0, 创建并配置sbspace空间

onspaces -c -S sbspace01 -p /data/gbase/sbspace01 -o 0 -s 1024000

更新ONCONFIG配置文件中SBSPACENAME为sbspace01

SBSPACENAME sbspace01

1,创建java类JUDREncrypt,实现encrypt_base64out及decrypt_base64in功能,测试通过

/**
 * Base on: Java 1.6 & apache commons-codec-1.9
 */

import java.nio.charset.Charset;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

import org.apache.commons.codec.binary.Base64;

public class JUDREncrypt {
    
    public static String charset = "GBK";

    public static void main(String[] args) {
        // encrypt_base64: text = 1, key = GBase8sv8.7_2.0.1a2_2
        String key = "GBase8sv8.7_2.0.1a2_2";
        String encrypttext = encrypt_base64out("中文输入", key);
        System.out.println("Encrypt text: " + encrypttext);
        System.out.println("Decrypt text: " + decrypt_base64in("ERPHYYO4/BQ=", "GBase8sv8.7_2.0.1a2_2"));
        
    }
    
    // 加密
    public static String encrypt_base64out(String p_text, String p_key) {
        try {            
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            DESKeySpec keySpec = new DESKeySpec(p_key.getBytes(Charset.forName(charset)));
            SecretKey myDesKey = keyFactory.generateSecret(keySpec);
            Cipher desCipher = Cipher.getInstance("DES/ECB/NoPadding");
            desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
            
            byte[] textEncrypted = desCipher.doFinal(padding(p_text).getBytes());

            Base64 encoder = new Base64();
            byte[] byte_text = encoder.encode(textEncrypted);
            
            return new String(byte_text,charset);            
        } catch (Exception e) {
            e.printStackTrace();
        }    
        return null;
    }
    
    public static String decrypt_base64in(String p_text, String p_key) {
        try {
            Base64 decoder = new Base64();
            byte[] byte_text = decoder.decode(p_text.getBytes(Charset.forName(charset)));
            
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            DESKeySpec keySpec = new DESKeySpec(p_key.getBytes(Charset.forName(charset)));
            SecretKey myDesKey = keyFactory.generateSecret(keySpec);
            Cipher desCipher = Cipher.getInstance("DES/ECB/NoPadding");
            desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
            
            byte[] textDecrypted = desCipher.doFinal(byte_text);
            return rtrim(new String(textDecrypted,charset));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public static String rtrim(String str,String del) {
        if(del.equals(null)) {
            del = "`";
        }
        int strlen = str.length();
        for(int i = strlen - 1; i > -1; i--) {
            if(! str.substring(i, i+1).equals(del)) {
                return str.substring(0,i+1);
            }
        }
        return str;
    }
    
    public static String rtrim(String str) {
        String del = "`";
        int strlen = str.length();
        for(int i = strlen - 1; i > -1; i--) {
            if(! str.substring(i, i+1).equals(del)) {
                return str.substring(0,i+1);
            }
        }
        return str;
    }
    
    public static String padding(String str,String pad) {
        if (str.getBytes().length % 8 == 0) {
            return str;
        }
        if(pad.equals(null)) {
            pad = "`";
        }
        int padlen = 8 - str.getBytes().length % 8;
        String padstr = "";
        if(padlen >  0 & padlen < 8) {
            for(int i = 0; i < padlen; i++) {
                padstr = padstr + pad;
            }
        }
        return str + padstr;
    }
    
    public static String padding(String str) {
        if (str.getBytes().length % 8 == 0) {
            return str;
        }
        String pad = "`";
        int padlen = 8 - str.getBytes().length % 8;
        String padstr = "";
        if(padlen >  0 & padlen < 8) {
            for(int i = 0; i < padlen; i++) {
                padstr = padstr + pad;
            }
        }
        return str + padstr;
    }

}

2,打包成jar(使用jdk 1.6版本)

javac JUDREncrypt.java

jar -cf judrencrypt-1.4.jar JUDREncrypt.class

3,将jar包放置到数据库服务器上($GBASEDBTDIR=/opt/gbase)

judrencrypt-1.4.jar 放置到$GBASEDBTDIR/java_udr目录下
commons-codec-1.9.jar 放置到$GBASEDBTDIR/extend/krakatoa目录下。

4,设置数据库的JVP环境

ONCONFIG配置文件中修改

1)VPCLASS开启jvp,用于处理Java UDR

VPCLASS jvp,num=1

2)JVPARGS增加-Dfile.encoding=GBK

JVPARGS -Dcom.ibm.tools.attach.enable=no;-Dfile.encoding=GBK

3)JVPCLASSPATH增加commons-codec-1.9.jar的路径

$GBASEDBTDIR/extend/krakatoa/commons-codec-1.9.jar
使之变成为

JVPCLASSPATH  $GBASEDBTDIR/extend/krakatoa/krakatoa.jar:$GBASEDBTDIR/extend/krakatoa/jdbc.jar:$GBASEDBTDIR/extend/krakatoa/commons-codec-1.9.jar

重启数据库生效,onstat -g glo中应有jvp虚拟处理器。

5,在需要的数据库中注册jar

execute procedure install_jar("file:/opt/gbase/java_udr/judrencrypt-1.4.jar", "JUDREncrypt");

install_jar的参数:jar所在的绝对路径,安装后的名称。

6,创建相应的例程

-- 解密
drop function if exists decrypt_base64in;
create function decrypt_base64in(varchar(254),varchar(254))
returns varchar(254) with (not variant)
external name 'JUDREncrypt:JUDREncrypt.decrypt_base64in(java.lang.String,java.lang.String)'
language java;
  
--加密
drop function if exists encrypt_base64out;
create function encrypt_base64out(varchar(254),varchar(254))
returns varchar(254) with (not variant)
external name 'JUDREncrypt:JUDREncrypt.encrypt_base64out(java.lang.String,java.lang.String)'
language java;

其中function中的decrypt_base64in(lvarchar,lvarchar)应与Java中的定义相对应;
external name 为java中的名称,格式为:安装后的名称:java类名.方法

完成即可正常使用新创建的encrypt_base64in(lvarchar,lvarchar)及decrypt_base64in(lvarchar,lvarchar)

> execute function encrypt_base64out("1","GBase8sv8.7_2.0.1a2_2");

(expression)  wfjaoFa+Fng=

1 row(s) retrieved.


> execute function decrypt_base64in("wfjaoFa+Fng=","GBase8sv8.7_2.0.1a2_2");

(expression)  1

1 row(s) retrieved.

7, 修改Java UDR例程的操作

修改指定注册名称的对应的jar新路径或者名称
如:JUDREncrypt使用新的jar包替换掉之前的

execute procedure replace_jar("file:/opt/gbase/java_udr/judrencrypt-1.4.jar", "JUDREncrypt");

8,删除Java UDR例程的操作

删除对应的函数

drop function decrypt_base64in;
drop function encrypt_base64out;

移除对应的注册的jar

execute procedure remove_jar("JUDREncrypt");

只需要注册的名称。

编译好的jar(JDK1.6)

judrencrypt_jdk1.6.zip


注:本文已在2020-05-15更新

标签: GBase 8s java udr, udr, 加密解密, encrypt, decrypt

已有 2 条评论

  1. 鲁鲁 鲁鲁
    问一下,你是南大通用的员工吗?网站做得不错
  2.  如果只是在java程序中使用,且java的版本高于或者等于1.8时:Base64可直接使用java.util.Base64,而不需要commons-codec-1.9.jar(org.apache.commons.codec.binary.Base64)中的Base64。

添加新评论