sponsored links



No announcement yet.

Hashing the input string with SHA-1 and a RSA private Key.

  • Filter
  • Time
  • Show
Clear All
new posts

  • Hashing the input string with SHA-1 and a RSA private Key.


    I have a program to digitally sign records for tax porposes.

    The program uses Qshell's Openssl and although it works it is quite slow with many records to sign. On average I have about 70,000 records with peaks of 90,000.
    My CL:

    QSH CMD('openssl dgst -sha1 -sign "$_KeyPrivFile" +
    -out "$_Hash1" "$_Record" ; openssl enc +
    -base64 -in "$_Hash1" -out "$_Hashb64" -A')

    $_KeyPrivFile is my RSA Private File on IFS
    $_Hash1 is the Hash in Binary
    $_Record is my string that i want to Sign.
    $_Hashb64 is the Hash in base64 encoding

    I'm looking for some API or RPG/RPGLE program to replace OpenSSL.

    Can anyone indicate the right API or post an example (OS V7R3)?

  • #2
    If I understand correctly (and I'm only just getting into this myself) I think what you may be looking for is the Qc3CalculateHMAC API. If so IBM have a full RPG example here: https://www.ibm.com/support/pages/qc...ac-api-example


    • #3
      If you're using a SHA-1 shared key (which your openssl command suggests you are) then Qc3CalculateHMAC sounds right. If you want to sign with an actual RSA private key, then you need Qc3CalculateSignature.

      You'll also need a solution to convert the binary output to base64. One option is the BASE64ENCODE function in SYSTOOLS. If that doesn't work for you, Scott Klement also wrote a library.


      • #4
        First of all thank you very much for the answers.
        Looking at the IBM documentation (which is never very clear...) of the Qc3CalculateSignature api "API produces a digital signature by hashing the input data and encrypting the hash value using a public key algorithm (PKA)." I think this is not what I want since I have a string and the RSA private key and I want to get a hash (with SHA-1 algorithm) using both (the string and the RSA private key).

        This hash will be sent along with other invoice information to the tax people. Then they with the RSA public key (which I made available to them) and with the same invoice data (the string is made with some invoice data, so is the same string I use) will check if the Hash I produced for that invoice is correct or not.

        So, looking at the Calculate HMAC example (thanks JonBoy) I need to replace:

        d*SHA_256 c const (3)
        d SHA_1 c const (2)

        and all references to SHA_256 by SHA_1.

        So I think I need to do this Steps:

        - Convert the string to CCSID 819 (in my CL I add/replace the QIBM_CCSID environment variable to 819 before the QSH command).

        - Read my RSA private key (IFS file) into a String. <<< The CalculateHMAC API needs a String

        - Convert the string (with the private key) to CCSID 819. <<< not sure I need this

        - call the Calculate HMAC API.

        - Convert the binaryHMAC field to base64 (Thanks Martin for providing 2 solutions).

        - Convert Base64HMAC to EBCDIC (ccsid 37) <<< not sure I need this but once the Hash was generated with the string with ccsid 819 and I need to save the Hash at 37

        Please correct me in the necessary steps if something is missing or not correct ...

        Thanks in advance,


        • #5
          Thanks for adding more detail. I'm convinced now that you actually need Qc3CalculateSignature.

          Calculating an HMAC uses a hashing algorithm plus a symmetric key. If you want to use RSA for signing, then the signature is produced by combining the RSA algorithm with a separate hashing algorithm. I don't understand the distinction between all the different schemes for RSA signing (e.g. RSS-PSS) but this API was enough for our use case of signing JWTs with an RSA key plus SHA-256.

          If you take a look through the parameters for the API, you'll see that the Algorithm Description format ALGD0400 allows you to specify both the public key algorithm (50 = RSA) and the hash algorithm (2 = SHA-1).

          You would still use Key Description format KEYD0200, like in the example Jon linked, if you want to specify the RSA private key as a literal value. The biggest differences will be that myKey.Fmt would be '1' instead of '0', and myKey.Value would be the BER format (a.k.a. DER format) representation of your RSA private key. If your key file starts with the text "-----BEGIN RSA PRIVATE KEY-----") then it is in PEM format and you will need to convert it - the openssl command should be capable of doing that, though I don't have the exact options to hand.

          Here's roughly the code we wrote for our JWT implementation. Note that we put the RSA key pair into an IBM i Cryptographic Keystore File to protect it, so we use key description KEYD0400 to make a reference to the key. Still, this might be a useful starting point for you.

          // Stuff /included from a header member
          dcl-c HASH_ALGORITHM_SHA1 2;  
          dcl-c HASH_ALGORITHM_SHA256 3;
          dcl-c CRYPTO_ALGORITHM_RSA 50;
          dcl-ds t_ALGD0400 len(12) qualified template;
            publicKeyCipherAlgorithm int(10) pos(1);
            pkaBlockFormat char(1) pos(5);
            signingHashAlgorithm int(10) pos(9);
          dcl-ds t_KEYD0400 len(56) qualified template;
            keystoreFile char(10);
            keystoreLibrary char(10);
            recordLabel char(32);
          dcl-pr Qc3CalculateSignature extproc(*dclcase);
            inputData char(30000) options(*varsize) const;
            inputDataLength int(10) const;
            inputDataFormat char(8) const;
            algorithmDescription char(32000) options(*varsize) const;
            algorithmDescriptionFormat char(8) const;
            keyDescription char(30000) options(*varsize) const;
            keyDescriptionFormat char(8) const;
            cryptoServiceProvider char(1) const;
            cryptoDeviceName char(10) const;
            signature char(512) ccsid(*hex) options(*varsize);
            signatureLengthProvided int(10) const;
            signatureLengthReturned int(10);
            errorCode like(t_qusec);
          // Adapted from a service program procedure
          dcl-s payload varchar(10000) ccsid(*hex)
          dcl-ds keyDescription likeds(t_KEYD0400);
          dcl-ds algorithmDescription likeds(t_ALGD0400);
          dcl-ds qusec likeds(t_qusec) inz;
          dcl-s signature char(512) ccsid(*hex);
          dcl-s signatureLength int(10);
          keyDescription = *allx'00';
          keyDescription.keystoreFile = 'KEYSTORE'
          keyDescription.keystoreLibrary = '*LIBL';
          keyDescription.recordLabel = 'RSA_KEY_LABEL';
          algorithmDescription = *allx'00';
          algorithmDescription.publicKeyCipherAlgorithm = CRYPTO_ALGORITHM_RSA;
          algorithmDescription.pkaBlockFormat = '1';
          algorithmDescription.signingHashAlgorithm = HASH_ALGORITHM_SHA256;
            payload :
            %len(payload) :
            'DATA0100' :
            algorithmDescription :
            'ALGD0400' :
            keyDescription :
            'KEYD0400' :
            ANY_CSP :
            '' :
            signature :
            %size(signature) :
            signatureLength :


          • rmmcf@clix.pt
            rmmcf@clix.pt commented
            Editing a comment
            Thanks for the answer.
            Yes my key starts with "----- BEGIN RSA PRIVATE KEY -----" and ends with "-----END RSA PRIVATE KEY-----" and is in .PEM format

            However, I can transform the key into .DER with openssl:
            'openssl rsa -in "$ _KeyPrivFile" -outform DER -out "$ _KeyPrivDERFile"'

            I think .DER will be the same as .BER and is the key in Binary right?

            So, if I use Qc3CalculateSignature:
            KEYD0200 with:
            Key Type = 51 (RSA Private Key)
            Key Format = 1 (BER String)
            Key String = My .DER RSA Private Key

            and ALGD0400 with:
            Public key cipher algorithm = 50 (RSA)
            PKA block format <<< What value to put here?
            Signing hash algorithm = 2 (SHA-1)

            However, I'm confused by the API to use...
            Isn't Qc3EncryptData a better and simpler solution?

            In the meantime, I'm on vacation for 2 weeks, so I only answer at that time.
            Thank you

        • #6
          PKA block format <<< What value to put here?
          This influences the algorithm used for combining your hashing algorithm with your RSA key. There's a little more info here:


          In our own code we use block type 1, which corresponds to the RSASSA-PKCS1-v1_5 scheme. Block type 0 seems to refer to an older scheme that has since been dropped from the PKCS#1 standard.

          Isn't Qc3EncryptData a better and simpler solution?
          I am reading this as you suggesting that you encrypt the HMAC produced through Qc3CalculateHMAC? Obviously you could encrypt the tax data itself with your private key and then you wouldn't have any reason to create a signature.

          I think combining two API calls in this way is more complicated due to the additional code, but worse, I don't think it's equivalent to what your openssl command does. This page has a similar command string to yours, and it shows how it uses RSASSA-PKCS1-v1_5 including some padding operations. My opinion is that Qc3CalculateSignature seems like your best bet to recreate that functionality.


          • #7
            If you are on Release 7.3 or 7.4 on the latest TR you may have also a look whether the HASH_ROW SQL Scalar function will deliver what you need.