Forum Stats

  • 3,851,626 Users
  • 2,264,005 Discussions
  • 7,904,793 Comments

Discussions

Function for RSA SHA-256 digital signature

13

Answers

  • User_O7GVP
    User_O7GVP Member Posts: 4 Green Ribbon

    Ascheffe,

    I am trying to implement your solution but I am getting the below error while calling as_crypto.sign function from github (https://github.com/antonscheffer/as_crypto) library. In the github library the function as_crypto.sign only accepts integers to sign.

    I tried to run in both Oracle 12 and 19c but no luck. Any help would be highly appreciated.

    ORA-06502: PL/SQL: numeric or value error

    ORA-06512: at "SYS.OWA_UTIL", line 359

    ORA-06512: at "SYS.HTP", line 1418

    ORA-06512: at "SYS.HTP", line 1494

    ORA-06512: at "SYS.HTP", line 1809

    ORA-06512: at "AS_CRYPTO", line 726

    ORA-06502: PL/SQL: numeric or value error

    ORA-06512: at "AS_CRYPTO", line 653

    ORA-06512: at "AS_CRYPTO", line 670

    ORA-06512: at "AS_CRYPTO", line 715

    ORA-06512: at "AS_CRYPTO", line 3210

  • Anton Scheffer
    Anton Scheffer Member Posts: 1,950 Gold Trophy

    My guess is that you got the parameters mixed up.

  • User_O7GVP
    User_O7GVP Member Posts: 4 Green Ribbon

    When I replace my key in your function, I get the error.

       l_sign := base64URL_encode( p_raw => as_crypto.sign( utl_raw.cast_to_raw( l_header || '.' || l_payload ), utl_raw.cast_to_raw('MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+CXUDT6S44Nni

    Q+PGO+Q+8zUdVavAqdz+hGS2Zewjbwwu/Jgxfu32ipE7szK6tadGZVaqLPFdZMFu

    XDkG7dM4Skl5wYoaD/M0MgRbT336yyLEc/0wMjvuTAaRmJBR2FP/qJ/rbK9XUGut

    sPr0qpIJcW+pKnf4MEHMourskvSieUTfPtUiKlQFHJS/amYQMaNRYBIGOk0XTTGS

    7xmQa6jBJ/n+s8dL6+jqKbzpsJ3lYmHdHdQRTaePU4c6FbCF3JD/a0r7czSI+TgY

    p4SavSQqZfxkXWEzQR2KgY7HqHkP5qecdHhISN6h0LBJjaVVN0xBkTHpayQnVgie

    Johp8HlJAgMBAAECggEAC03vtn812sORYnrMYV3tsWrxRWnzuhXArik8/naoqeDw

    thjTito0mJ5cjlMWGPI2XaMomR3sS7RzJzf4MnfSQT52CXagWvWI75lsFexwH4uW

    6V3G+Wd7lizSlTWgnCXEtWgX/z6rXYPMq/vjqcs9va8QR91dXpesSlqEpAcH42Q9

    7TdsZN1MD+bEN7QRcf5eI4M4gJTcg00/N7alsiVLXnN45HZw8mX6vpzZ7dGxfe3E

    nsQTNuuzG6FVwOPaaWFgUx80MCwt0+5OTxhJltdCrGMGZhppA4RYygHfHWLPyb7v

    kfu/dJN1HKVmFY/tYXKObkU0xl7P9NWEBut4vxf5UwKBgQD4k9R0FPS7wo21jjch

    /TSjKD+BVh1fsxaWVeXFEPvByAWL9Io1s/C2SFIVRVhpAWHvAyVMOYvXMpIpWts6

    NLp96FDs8aC0/H7eEjAGbUHxI0RfukrHly4Awx5sKhfiHyMPjCduiMVvtt1B/brZ

    PA6TnZ58YoOoZDL4YSAPMJj7nwKBgQDDtiISwixOzJmRUF97fhHGew4cqyoFg1ra

    WKhyB0S5x1ZInTuiBVIINKp5RTgK45c3F8hmBmLClWVxCCGnQXxRxNI+0nlsAXyJ

    Jbez0Y7uXTTukxocdFsQBC8FYIhnGUxbKdtoFtr0PxGW4WkUCz3wgD/dLzOA6E5Q

    VTkwl8FiFwKBgGfuHxDdxilOwcMcD+8VF9HzvQGNiMB/dz0p8uR/IlO2FqOafcLQ

    Vh9Qg4lDMiDNJC4ziX+iO9ag3T2PMEPY0fRckqFOsVuaiky9G/lD6yMwFKFVmXhz

    vBczfV/KBaxUSmtwtPfhFXOM6Ft0A5BaLNoJN79XzSY1pp30tdvD/OKPAoGBAL+X

    6BdxG6swxQ3rHu5oZsJl6VTLg+HNy/2BWbX3w6ITsTZ5Js3gY4RA+rqptsmCMKDw

    mXC3w6s2Ug6rpTZ191ydUC3MIq5HbkFdOU/CUSis+oqsW52LkzJExjt/W0mDDKsa

    eP6XkVYXAAMzPIkw0DxHcT35XXRO94vRQqF9jwWVAoGAJRVMzxhpPseQv791mwq0

    b0qhtaimHLvvVgXrNnnB8vaKKC2oViyuqJygzU6U+iPoED/lrcTvO0FMOgtx4tPo

    9OFGPjNa8MtbZoCHMOHAc8HUt5ZlFA19Tl/8cyuMCmCjhelG00BTSHk7Hd65WoK6

    bZNs0GvR3n6KBqKqG8G3Ba8='

    ), as_crypto.KEY_TYPE_RSA, as_crypto.SIGN_SHA256_RSA ) );

  • User_O7GVP
    User_O7GVP Member Posts: 4 Green Ribbon

    When I use the key provided in your sample code it works fine.

  • Anton Scheffer
    Anton Scheffer Member Posts: 1,950 Gold Trophy

    The key used in that example is in PKCS#1 format, the key in your case is in PKCS#8 format. I've no idea if the Oracle dbms_crypto supports PKCS#8 too, but I will change my code to support both formats. Might take some time :)

  • Anton Scheffer
    Anton Scheffer Member Posts: 1,950 Gold Trophy
    edited Sep 24, 2021 8:56PM

    If you change the function parse_DER_RSA_PRIV_KEY in as_crypto to the following it might work.

    Update: I just compared the results of this new function to the Oracle 21.0.0.0.0 version: both support PKCS#1 and PKCS#8 format and produce the same "sign". And still no support for a key with the headers.

      function parse_DER_RSA_PRIV_key
        ( p_key raw
        , p_key_parameters out tp_key_parameters
        )
      return boolean
      is
        l_dummy raw(3999);
        l_ind pls_integer;
        l_len pls_integer;
        l_tmp pls_integer;
      begin
        p_key_parameters.delete;
        check_starting_sequence( p_key, l_ind );
        l_dummy := get_integer( p_key, l_ind );  -- version
        if utl_raw.substr( p_key, l_ind, 1 ) = c_SEQUENCE
        then -- PKCS#8
          l_tmp := l_ind;
          l_len := get_len( p_key, l_ind );
          if get_oid( p_key, l_ind ) != '2A864886F70D010101' -- 1.2.840.113549.1.1.1 rsaEncryption
          then
            raise value_error;
          end if;
          l_ind := l_tmp + l_len + 2; -- skip optional stuff of AlgorithmIdentifier
          if utl_raw.substr( p_key, l_ind, 1 ) not in ( c_OCTECT, c_BIT_STRING )
          then
            raise value_error;
          end if;
          l_len := get_len( p_key, l_ind );
          check_starting_sequence( p_key, l_ind );
          l_dummy := get_integer( p_key, l_ind );  -- version
        end if;
        -- process PKCS#1
        p_key_parameters(1) := get_integer( p_key, l_ind ); -- n modulus
        p_key_parameters(2) := get_integer( p_key, l_ind ); -- e public
        p_key_parameters(3) := get_integer( p_key, l_ind ); -- d private
        l_dummy := get_integer( p_key, l_ind ); -- p prime1
        l_dummy := get_integer( p_key, l_ind ); -- q prime2
        l_dummy := get_integer( p_key, l_ind ); -- d mod (p-1) exponent1
        l_dummy := get_integer( p_key, l_ind ); -- d mod (q-1) exponent2
        l_dummy := get_integer( p_key, l_ind ); -- (inverse of q) mod p coefficient
        return true;
      exception when value_error
        then
          p_key_parameters.delete;
          return false;
      end;
    
    
    
  • User_O7GVP
    User_O7GVP Member Posts: 4 Green Ribbon

    Thank you ascheffer for all your help. It worked for us after you changed the code. Thanks again.

  • Anton Scheffer
    Anton Scheffer Member Posts: 1,950 Gold Trophy

    You're welcome :)

  • MonkeyThink
    MonkeyThink Member Posts: 8 Red Ribbon
    edited Jul 28, 2022 4:02PM

    Hi Anton, firstly, thanks for the package. Very useful on a 19 db where we want to use JWTs for authentication to an APEX app.

    I've used the examples and discussion above to be able to prove that using the as_crypto.sign function produces exactly the same JWT as the jwt.io tool for an RSA256 encrypted JWT.

    However I can't seem to get the verification working.

    Code sample to produce the JWT which matched jwt.io is this:

    declare

     l_header varchar2(1000);

     l_payload varchar2(1000);

     l_sign varchar2(1000);

     l_token varchar2(32767);

     function base64URL_encode( p_raw in raw )

     return varchar2

     is

      l_rv varchar2(32767);

     begin

      l_rv := utl_raw.cast_to_varchar2( utl_encode.base64_encode( p_raw ) );

      l_rv := replace( l_rv, '+', '-' );

      l_rv := replace( l_rv, '/', '_' );

      l_rv := replace( l_rv, chr(10), '' );

      l_rv := replace( l_rv, chr(13), '' );

      return rtrim( l_rv, '=' );

     end;

     function base64URL_encode( p_txt in varchar2 )

     return varchar2

     is

     begin

      return base64URL_encode( p_raw => utl_raw.cast_to_raw( p_txt ) );

     end;

    begin

     l_header := base64URL_encode( p_txt => '{"alg":"RS256","typ":"JWT"}' );

     l_payload := base64URL_encode( p_txt => '{"sub":"1234567890","name":"John Doe","admin":true,"iat":1516239022}' );

     l_sign := base64URL_encode( p_raw => as_crypto.sign( utl_raw.cast_to_raw( l_header || '.' || l_payload )

                               , utl_raw.cast_to_raw( '-----BEGIN PRIVATE KEY-----

    MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj

    MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu

    NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ

    qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg

    p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR

    ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi

    VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV

    laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8

    sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H

    mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY

    dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw

    ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ

    DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T

    N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t

    0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv

    t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU

    AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk

    48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL

    DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK

    xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA

    mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh

    2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz

    et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr

    VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD

    TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc

    dn/RsYEONbwQSjIfMPkvxF+8HQ==

    -----END PRIVATE KEY-----' ), as_crypto.KEY_TYPE_RSA, as_crypto.SIGN_SHA256_RSA ) );

     l_token := l_header || '.' || l_payload || '.' || l_sign;

     dbms_output.put_line( l_token );

    end;


    and produces this JWT:

    eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ


    To verify I assumed that we need to take the first two elements of the JWT which represent the base64url encoded header and payload and use those in the src parameter of the verify function. The sign parameter is the 3rd element of the JWT, along with public key parameter and the pubkey_alg and sign_alg parameters being the same as in the call to the sign function, which gives me this code:


    begin

     if as_crypto.verify ( src   => utl_raw.cast_to_raw ( 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0')

               , sign   => utl_raw.cast_to_raw ( 'NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ')

               , pub_key => utl_raw.cast_to_raw ( '-----BEGIN PUBLIC KEY-----

    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo

    4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u

    +qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh

    kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ

    0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg

    cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc

    mwIDAQAB

    -----END PUBLIC KEY-----' )

              , pubkey_alg => as_crypto.KEY_TYPE_RSA

              , sign_alg  => as_crypto.SIGN_SHA256_RSA )

     then

      dbms_output.put_line ('Verified');

     else

      dbms_output.put_line ('Failed verification');

     end if ;

    end ;

    But it always comes back as 'Failed verification' :(


    Any thoughts on where I've gone wrong?

    Thanks

    Adam

  • MonkeyThink
    MonkeyThink Member Posts: 8 Red Ribbon

    Ok, I've got a bit further with this. It's something to do with the datatypes/casting of the src and/or sign parameters. Working to track it down now.