在Oracle函数中加密/解密密码

在Oracle函数中加密/解密密码,oracle,encryption,plsql,Oracle,Encryption,Plsql,由于以前设计的结构很差,我必须使用的当前数据库将用户的密码存储为文本 现在,我正在构建一个前端部分,它必须使用这些密码,我当然不想发送未加密的密码 我的想法是编写一个Oracle函数来加密和解密文本密码,并在将返回加密数据的存储过程中使用这些函数 Oracle中最好的方法是什么?看看DBMS\u CRYPTO 它内置了加密和解密数据的方法。比写自己的好 如果您想编写自己的函数来加密和解密数据,只需调用带有适当参数的加密和解密方法(即选择加密算法、密钥等) 当然,如果您编写自己的例程,假设您将密钥

由于以前设计的结构很差,我必须使用的当前数据库将用户的密码存储为文本

现在,我正在构建一个前端部分,它必须使用这些密码,我当然不想发送未加密的密码

我的想法是编写一个Oracle函数来加密和解密文本密码,并在将返回加密数据的存储过程中使用这些函数


Oracle中最好的方法是什么?

看看DBMS\u CRYPTO

它内置了加密和解密数据的方法。比写自己的好


如果您想编写自己的函数来加密和解密数据,只需调用带有适当参数的
加密
解密
方法(即选择加密算法、密钥等)

当然,如果您编写自己的例程,假设您将密钥存储在数据库中或数据库可以访问的某个地方,那么您在安全性方面做得并不多。通过网络发送未加密的密码是不好的,但通常在数据库中存储未加密的密码(如果数据库中有
decrypt
方法可以访问密钥来解密数据,则存储加密的密码)要糟糕得多。通常,从数据库窃取数据要比嗅探通过网络发送的数据以查找密码容易得多


当然,正确的答案是重新构建系统,这样就根本不需要存储密码。您应该存储不可逆的密码哈希(您也可以使用
DBMS\u CRYPTO
包生成密码哈希)。

这里有一个打包函数(成功实现),用于使用(
DBMS\u CRYPTO
)加密密码-

这使用了
encrypt\u aes256
-“高级加密标准。分组密码。使用256位密钥大小。”,
chain\u cbc
-“密码块链接。明文在加密之前与上一个密文块异或。”和
pad\u pkcs5
-“提供符合PKCS#5:基于密码的加密标准的填充”

除此之外,您还可以创建一个类似的函数来解密-

  FUNCTION decrypt_val( p_val IN RAW ) RETURN VARCHAR2  
  IS
    l_decrypted RAW(32);
    l_decrypted_string VARCHAR2(32);
    l_user  VARCHAR2(32);
  BEGIN
    SELECT user 
      INTO l_user
      FROM dual;

    if l_user = 'ADMIN' -- you can restrict usage of decrypt to certain db users only.
    then
        l_decrypted := dbms_crypto.decrypt
                ( src => p_val,
                  typ => G_ENCRYPTION_TYPE,
                  key => G_KEY );

        l_decrypted_string := utl_i18n.raw_to_char
                    ( data => l_decrypted,
                      src_charset => G_CHARACTER_SET );
        RETURN l_decrypted_string;
    else            
            RAISE_APPLICATION_ERROR(-20101, 'You are not authorized to use this function - decrypt_val()');
    end if;
    RETURN 'Unknown';
  END decrypt_val;

您还可以考虑在使用“代码>包装名为包装名=PACKAYNEX.PKB < /代码>之前编译包,然后编译生成的<代码> PLB < /代码> ./P>< P>或者您可以使用像这样的口令散列算法PBKDF2。 我用rfc验证了结果,效果很好

检查此链接以了解最小迭代次数和密钥大小: 请注意,结果是长度的两倍,因为它是十六进制编码的

我们将这些列存储在数据库中

PASSWORD_HASH                 VARCHAR2(512 BYTE),
PASSWORD_SALT                 VARCHAR2(256 BYTE),
PASSWORD_ITERATIONS           NUMBER(10),
PASSWORD_HASH_METHOD          VARCHAR2(30 BYTE),
PASSWORD_CHANGED_DT           DATE
散列、salt和迭代将为pbkdf2算法提供数据 hash_方法用于迁移目的
更改后的密码将使密码过期

用户此代码肯定有效

create or replace PACKAGE          "PKG_LOGI_PWD_REG" 
AS
  function ENCRYPT_VAL( P_VAL in varchar2 ) return varchar2;
  function DECRYPT_VAL( P_VAL in raw ) return varchar2;
end;
/

create or replace PACKAGE BODY          "PKG_LOGI_PWD_REG" 
as
  FUNCTION decrypt_val( p_val IN RAW ) RETURN VARCHAR2  
  IS
    l_decrypted RAW(32);
    l_decrypted_string VARCHAR2(32);
    L_USER  varchar2(32);
    L_CHARACTER_SET varchar2(10);
    L_STRING varchar2(32);
    L_KEY raw(250);
    L_ENCRYPTION_TYPE PLS_INTEGER;
  BEGIN
        L_KEY := UTL_I18N.STRING_TO_RAW
                            ( data => '98345678901234567890123456789012',
                              DST_CHARSET => 'AL32UTF8' );
        L_ENCRYPTION_TYPE := dbms_crypto.encrypt_aes256 
                                        + DBMS_CRYPTO.CHAIN_CBC 
                                        + DBMS_CRYPTO.PAD_PKCS5;

        l_decrypted := dbms_crypto.decrypt
                ( SRC => P_VAL,
                  TYP => L_ENCRYPTION_TYPE,
                  key => L_KEY );

        l_decrypted_string := utl_i18n.raw_to_char
                    ( data => l_decrypted ,
                      src_charset => 'AL32UTF8' );
        RETURN l_decrypted_string;

  end DECRYPT_VAL;

  FUNCTION encrypt_val( p_val IN VARCHAR2 ) RETURN VARCHAR2
  is
    L_VAL RAW(32);
    L_ENCRYPTED raw(32);
    L_CHARACTER_SET varchar2(10);
    L_STRING varchar2(32);
    L_KEY RAW(250);
    L_ENCRYPTION_TYPE PLS_INTEGER;
  begin


    L_KEY := UTL_I18N.STRING_TO_RAW
                        ( data => '98345678901234567890123456789012',
                          DST_CHARSET => 'AL32UTF8' );
    L_ENCRYPTION_TYPE := dbms_crypto.encrypt_aes256 
                                    + DBMS_CRYPTO.CHAIN_CBC 
                                    + DBMS_CRYPTO.PAD_PKCS5;
    L_VAL := utl_i18n.string_to_raw
              ( data => p_val,
                dst_charset => 'AL32UTF8' );

    L_ENCRYPTED := dbms_crypto.encrypt
                   ( SRC => L_VAL,
                     TYP => L_ENCRYPTION_TYPE,
                     key => L_KEY );


    return L_ENCRYPTED;
  EXCEPTION when OTHERS then
    RETURN SQLCODE||'-'||SQLERRM;
  end ENCRYPT_VAL;
end PKG_LOGI_PWD_REG;
/

我当然同意你的观点,但在这一点上,数据库似乎不会被重新构建。答案中没有人提到过(现在),但你永远不应该解密密码。相反,你应该加密用户输入的内容,并将其与加密密码进行比较。大多数(好)密码方案使用单向加密散列,因此无法解密。
create or replace PACKAGE          "PKG_LOGI_PWD_REG" 
AS
  function ENCRYPT_VAL( P_VAL in varchar2 ) return varchar2;
  function DECRYPT_VAL( P_VAL in raw ) return varchar2;
end;
/

create or replace PACKAGE BODY          "PKG_LOGI_PWD_REG" 
as
  FUNCTION decrypt_val( p_val IN RAW ) RETURN VARCHAR2  
  IS
    l_decrypted RAW(32);
    l_decrypted_string VARCHAR2(32);
    L_USER  varchar2(32);
    L_CHARACTER_SET varchar2(10);
    L_STRING varchar2(32);
    L_KEY raw(250);
    L_ENCRYPTION_TYPE PLS_INTEGER;
  BEGIN
        L_KEY := UTL_I18N.STRING_TO_RAW
                            ( data => '98345678901234567890123456789012',
                              DST_CHARSET => 'AL32UTF8' );
        L_ENCRYPTION_TYPE := dbms_crypto.encrypt_aes256 
                                        + DBMS_CRYPTO.CHAIN_CBC 
                                        + DBMS_CRYPTO.PAD_PKCS5;

        l_decrypted := dbms_crypto.decrypt
                ( SRC => P_VAL,
                  TYP => L_ENCRYPTION_TYPE,
                  key => L_KEY );

        l_decrypted_string := utl_i18n.raw_to_char
                    ( data => l_decrypted ,
                      src_charset => 'AL32UTF8' );
        RETURN l_decrypted_string;

  end DECRYPT_VAL;

  FUNCTION encrypt_val( p_val IN VARCHAR2 ) RETURN VARCHAR2
  is
    L_VAL RAW(32);
    L_ENCRYPTED raw(32);
    L_CHARACTER_SET varchar2(10);
    L_STRING varchar2(32);
    L_KEY RAW(250);
    L_ENCRYPTION_TYPE PLS_INTEGER;
  begin


    L_KEY := UTL_I18N.STRING_TO_RAW
                        ( data => '98345678901234567890123456789012',
                          DST_CHARSET => 'AL32UTF8' );
    L_ENCRYPTION_TYPE := dbms_crypto.encrypt_aes256 
                                    + DBMS_CRYPTO.CHAIN_CBC 
                                    + DBMS_CRYPTO.PAD_PKCS5;
    L_VAL := utl_i18n.string_to_raw
              ( data => p_val,
                dst_charset => 'AL32UTF8' );

    L_ENCRYPTED := dbms_crypto.encrypt
                   ( SRC => L_VAL,
                     TYP => L_ENCRYPTION_TYPE,
                     key => L_KEY );


    return L_ENCRYPTED;
  EXCEPTION when OTHERS then
    RETURN SQLCODE||'-'||SQLERRM;
  end ENCRYPT_VAL;
end PKG_LOGI_PWD_REG;
/