65.9K
CodeProject 正在变化。 阅读更多。
Home

PL/SQL 中的 RSA 加密

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2020 年 8 月 12 日

CPOL

2分钟阅读

viewsIcon

9960

使用 RSA 公钥加密数据

引言

我最近需要使用提供的 RSA 公钥,从 Oracle PL/SQL 包中加密某些信息,以便能够与以这种方式进行身份验证的 Web 服务进行通信。
在互联网上进行了一些研究后,我找不到任何可用的解决方案(免费的)。 找到很多概念、想法和零散的内容,或者付费软件,但要获得一个可用的解决方案,我花费的时间和精力比平时多得多,而且我不得不自己完成大部分工作。

因此,我决定将其发布以供公众了解。

背景

首先,重要的是确保您的环境配置正确

  1. 您需要确保您的 ORACLE 数据库已安装 JAVA VM。 您可以通过以下方式检查
    select comp_name, version, status from dba_registry;

    如果它不在那里,则您需要以 SYS 身份登录并执行以下操作

    SQL> @$ORACLE_HOME/javavm/install/initjvm.sql;

    安装 JavaVM 后,一切就绪了。

  2. 为了本文的目的,我们将使用公钥加密密码,以便它可以被发送并在另一端使用相应的私钥解密。 但我们需要
    • 我们的密码(我们将使用“password”)
    • 我们的公钥文件(我将称它为“rsa.public”)

在我的例子中,我使用的是 Oracle (11g) 11.2.0.4。

Using the Code

我们需要理解的第一件事是,ORACLE(在 11g 版本中)不提供用于非对称密码学的库。 DBMS_CRYPTO 包只会提供对称密码学。

因此,我们需要创建自己的类来处理它。 我们需要在 Java 中完成它。
创建或替换并编译名为 rsa_crypto 的 Java 源代码,如下所示

import javax.crypto.*;
import java.security.spec.*; 
import java.security.*; //PublicKey

public class RSACrypto
{
  public static byte[] Encrypt(byte[] cert, byte[] data) 
         throws Throwable //NoSuchAlgorithmException, InvalidKeySpecException, 
                          //InvalidKeyException, IllegalBlockSizeException, 
                          //NoSuchPaddingException, BadPaddingException
  {
      byte[] encryptedData = null;
      try {

        //Generate Public Key with Cert
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(cert);
        PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
        
        //Encrypt
        Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");  
        cipher.init(Cipher.ENCRYPT_MODE, pubKey); 
        encryptedData = cipher.doFinal(data);

        return encryptedData;
     } catch (Exception e) {
        System.out.println(  "Unexpected error - " + e.getMessage());
        encryptedData = e.getMessage().getBytes();
        throw e;
     }
  }
}

我们的函数将接收 2 个字节数组(证书内容和要加密的数据),并将返回一个包含加密数据的字节数组。

我们将创建我们的包,并在其主体内部声明以下 private 函数

function rsa_encrypt(cert raw, data raw) return raw
     AS LANGUAGE JAVA
      NAME 'RSACrypto.Encrypt(byte[], byte[]) return byte[]';

然后我们将拥有我们的 login 函数,它将以以下方式使用我们的 JAVA 函数

c_certini              constant varchar2(50) := '-----BEGIN PUBLIC KEY-----';
c_certend              constant varchar2(50) := '-----END PUBLIC KEY-----';
c_dirname              constant varchar2(255) := 'DIRX';
c_key_filename         constant varchar2(255) := 'rsa.public';

function login return number is
     
      passpost  raw(1024);
      res    varchar2(1024);
      
      v_fexist  boolean;
      v_flen    number;
      v_bsize   binary_integer;
      v_key    utl_file.file_type;
      v_cert   raw(2048);
      v_buffer varchar2(2048);
     
  begin
            
      -- Check and Read Certificate
      utl_file.fgetattr(c_dirname, c_key_filename, v_fexist, v_flen, v_bsize);
      if not v_fexist then
         dbms_output.put_line(c_dirname || '/' || c_key_filename || ' Not Found.');
         return -3;
      end if;
      v_key := utl_file.fopen(c_dirname, c_key_filename,'R');
      utl_file.get_raw(v_key,v_cert,v_flen);
      utl_file.fclose(v_key);
      dbms_output.put_line('Cert Read: ' || utl_raw.cast_to_varchar2(v_cert));
      v_buffer := utl_raw.cast_to_varchar2(v_cert);      
      
      -- Strip beginning and end.
      v_buffer := replace(v_buffer,c_certini,'');
      v_buffer := replace(v_buffer,c_certend,'');
      v_cert := utl_encode.base64_decode(utl_raw.cast_to_raw(v_buffer));
      dbms_output.put_line('Cert Strip: [' || v_buffer || ']');
      
      --Call to JAVA function
      begin
             passpost:=rsa_encrypt(v_cert,utl_raw.cast_to_raw('Password'));
      exception when others then
             dbms_output.put_line('Cannot encrypt ' || SQLERRM);  
             return -4;
      end;
      if passpost is null then
         return -2;
      end if;
      res:= utl_raw.cast_to_varchar2(utl_encode.base64_encode(passpost));
      dbms_output.put_line('Encrypted Signature: ' || res);     
        
      return 0;
      
  end;

关注点

基本上我们:打开文件,读取其内容,并将其传递给 Java 函数 Base64Decoded
Java 函数将使用接收到的内容创建一个 public 密钥,并使用它加密数据并返回它。
我们将对返回的数据进行 base 64 编码,然后我们可以将其用于我们需要的任何其他用途。

历史

  • 2020 年 8 月 12 日:初始版本
© . All rights reserved.