Question Stefan Cronje · Jul 10, 2017

SHA256 Signing with RSA PSS padding

Hi everyone,

I have a project which requires the sending of JSON messages to an external service provider using REST. The service provider requires the message contents to be signed.

Their instructions:

  1. Add a header called "Date" with the the date and time in a specific format - done
  2. Add the client's certificate password in a field in the header - done
  3. Create a string which consist of the {Date}{newline}{Password}{newline}{etc}{Message Body}.
    1. Convert to a UTF8 byte array
    2. SHA256 sign the value with the certificate and private key and use RSA PSS padding
    3. Base 64 Encode the value and place it in a Signature field in the header.

I've done the following:

  1. Set up X.509 credentials using the certificate and private key files
  2. Created the string to sign as per their instruction
  3. Performed a $zconvert, 'O', 'UTF8' on the string
  4. Used %SYSTEM.Encryption -> RSASHASign() and Base64Encode()

This does not seem to be correct, as the service provider keeps rejecting the messages.

Is there a way to specify the RSA padding to be PSS?
Am I using the wrong method?
Does this method actually use PSS padding and I should look for the problem somewhere else?
Are these methods endian-ness aware?

Thank you in advance.

Comments

Rubens Silva · Jul 11, 2017

Since you didn't specified which service you're using it's hard to simulate your doubt.

However I did a quick research and noticed that 'padding' actually refers to OAEP, this could be the method you want to use to encrypt. I'm not sure though.
/// This method performs RSA encryption as specified in
/// PKCS #1 v2.1: RSA Cryptography Specifications, section 7 Encryption Schemes.
/// <br><br>
/// Input parameters:
/// <br><br>
/// Plaintext - Data to be encrypted.
/// <br><br>
/// Certificate - An X.509 certificate containing the RSA public key to be used for encryption,
/// in PEM encoded or binary DER format.
/// Note that the length of the plaintext can not be greater than the length of the modulus of
/// the RSA public key contained in the certificate minus 42 bytes.
/// <br><br>
/// CAfile - The name of a file containing trusted Certificate Authority X.509 Certificates in PEM-encoded format, one of which was
/// used to sign the Certificate (optional).
/// <br><br>
/// CRLfile - The name of a file containing X.509 Certificate Revocation Lists in PEM-encoded format that should be checked
/// to verify the status of the Certificate (optional).
/// <br><br>
/// Encoding - PKCS #1 v2.1 encoding method (optional):<br>
///     1 = OAEP (default)<br>
///     2 = PKCS1-v1_5<br>
/// <br><br>
/// Return value: Ciphertext.
ClassMethod RSAEncrypt(
Plaintext As %String,
Certificate As %String,
CAfile As %String,
CRLfile As %String,
Encoding As %Integer) As %String
{
}

0
Stefan Cronje  Jul 11, 2017 to Vitaliy Serdtsev

Thank you. If it was possible to up-mark the answer more than once, I would have done it.

There is a difference in the values created by Cache and by OpenSSL

0
Eduard Lebedyuk  Jul 11, 2017 to Vitaliy Serdtsev

You can convert binary openssl output to base64 and write that to file:

cmd=$$$FormatText("openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -sign %1 %2 | base64 > %3",fileKey,fileMsg,file64)

base64 is available in most linux flavors, and on Windows in various GNU CoreUtils builds and in Git (usually under C:\Program Files\Git\usr\bin\).

Also in an a business operation filenames should be generated randomly (probably via ##clss(%File).TempFilename(ext)) to avoid conflicts.

0
Stefan Cronje  Jul 11, 2017 to Vitaliy Serdtsev

I've created a config file from some random example and placed it in the bin directory. I can run it from there in the command prompt, but the $zf does not execute it.

Would you mind sharing your openssl config file and where it should reside? Which paths should be configured on Windows?

Will the user require the %CallOut service to be available to do this on a locked down install(production environment)

0
Vitaliy Serdtsev · Jul 11, 2017

You can try to do it using the OpenSSL libraries, which comes complete with Caché/Ensemble/etc. To google: "openssl rsa-pss sign", "openssl SHA256 with RSA PSS padding"

Here is a small example on Windows, where it is assumed that

  • cert.pem is your certificate:
    -----BEGIN CERTIFICATE-----
    <...>
    -----END CERTIFICATE-----
  • key.pem is your private key:
    -----BEGIN RSA PRIVATE KEY-----
    <...>
    -----END RSA PRIVATE KEY-----
So (test.bat):
@echo off
 
echo Delete all temporary files
del /Q /F test.txt test.sig pubkey.pem test.b64
 
echo Extract the public key from certificate (only be done once)
openssl x509 -pubkey-in cert.pem -noout> pubkey.pem
 
echo Create testfile(test.txt)echo bla-bla-bla test123 {Date}{newline}{Password}{newline}{etc}{Message Body}> test.txt
 
echo Create signature (test.sig)
openssl dgst -sha256-sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1-sign key.pem -out test.sig test.txt
 
echo This step is only for information/verification.
echo Verify signature (The result should be: "Verified OK")
openssl dgst -sha256-sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1-signature test.sig -verify pubkey.pem test.txt
 
echo Convert signature to Base64 (test.b64)echo You can this step be make on COS.
openssl base64 -in test.sig -out test.b64 -nopad

Or on COS:

<FONT COLOR="#0000ff">#include </FONT><FONT COLOR="#000000">%systemInclude
</FONT><FONT COLOR="#0000ff">#include </FONT><FONT COLOR="#000000">%occErrors
</FONT><FONT COLOR="#ff0000">main</FONT><FONT COLOR="#000000">() </FONT><FONT COLOR="#0000ff">public </FONT><FONT COLOR="#800080">{
  
  </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">fileMsg</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"test.txt"</FONT><FONT COLOR="#000000">,
    </FONT><FONT COLOR="#800000">fileSig</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"test.sig"</FONT><FONT COLOR="#000000">,
    </FONT><FONT COLOR="#800000">file64</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"test.b64"</FONT><FONT COLOR="#000000">,
    </FONT><FONT COLOR="#800000">filePubKey</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"pubkey.pem"</FONT><FONT COLOR="#000000">,
    </FONT><FONT COLOR="#800000">fileCert</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"C:\SSL\cert.pem"</FONT><FONT COLOR="#000000">,
    </FONT><FONT COLOR="#800000">fileKey</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"C:\SSL\key.pem"

  </FONT><FONT COLOR="#0000ff">try </FONT><FONT COLOR="#800080">{     </FONT><FONT COLOR="#0000ff">$$$AddAllRoleTemporaryInTry     n $namespace

    if </FONT><FONT COLOR="#000000">'</FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%File</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">Exists</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">filePubKey</FONT><FONT COLOR="#000000">) </FONT><FONT COLOR="#800080">{       </FONT><FONT COLOR="#008000">; Only be done once       ; Extract the public key from certificate       </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#0000ff">$$$FormatText</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"openssl x509 -pubkey -in %1 -noout > %2"</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileCert</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">filePubKey</FONT><FONT COLOR="#000000">)       </FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">,!!       </FONT><FONT COLOR="#0000ff">d $zf</FONT><FONT COLOR="#000000">(-1,</FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">)     </FONT><FONT COLOR="#800080">}          </FONT><FONT COLOR="#0000ff">f </FONT><FONT COLOR="#800000">i</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#800000">fileMsg</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileSig</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">file64 </FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%File</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">Delete</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">i</FONT><FONT COLOR="#000000">)          </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">file</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%Stream.FileCharacter</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">%New</FONT><FONT COLOR="#000000">()     </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">file</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">Filename</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#800000">fileMsg     </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">file</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">TranslateTable</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#008000">"UTF8"     </FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#800000">file</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">WriteLine</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"{Date}"</FONT><FONT COLOR="#000000">)     </FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#800000">file</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">WriteLine</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"{Password}"</FONT><FONT COLOR="#000000">)     </FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#800000">file</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">Write</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"{etc}{Message Body}"</FONT><FONT COLOR="#000000">)     </FONT><FONT COLOR="#0000ff">$$$ThrowOnError</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">file</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">%Save</FONT><FONT COLOR="#000000">())          </FONT><FONT COLOR="#0000ff">w $$$FormatText</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"Create signature (%1)"</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileSig</FONT><FONT COLOR="#000000">),!     </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#0000ff">$$$FormatText</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -sign %1 -out %2 %3"</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileKey</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileSig</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileMsg</FONT><FONT COLOR="#000000">)     </FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">,!!     </FONT><FONT COLOR="#0000ff">d $zf</FONT><FONT COLOR="#000000">(-1,</FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">)          </FONT><FONT COLOR="#0000ff">w $$$FormatText</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"Convert signature to Base64 (%1)"</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">file64</FONT><FONT COLOR="#000000">),!     </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#0000ff">$$$FormatText</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"openssl base64 -in %1 -out %2 -nopad"</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileSig</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">file64</FONT><FONT COLOR="#000000">)     </FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">,!!     </FONT><FONT COLOR="#0000ff">d $zf</FONT><FONT COLOR="#000000">(-1,</FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">)          </FONT><FONT COLOR="#008000">;here we read our file test.b64 (file64) and place it in a Signature field in the header        </FONT><FONT COLOR="#800080">}</FONT><FONT COLOR="#0000ff">catch</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">ex</FONT><FONT COLOR="#000000">) </FONT><FONT COLOR="#800080">{     </FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#008000">"Error "</FONT><FONT COLOR="#000000">, </FONT><FONT COLOR="#800000">ex</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">DisplayString</FONT><FONT COLOR="#000000">(),!   </FONT><FONT COLOR="#800080">}

  </FONT><FONT COLOR="#0000ff">f </FONT><FONT COLOR="#800000">i</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#800000">fileMsg</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileSig </FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%File</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">Delete</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">i</FONT><FONT COLOR="#000000">) </FONT><FONT COLOR="#800080">}</FONT>

0
Vitaliy Serdtsev  Jul 13, 2017 to Eduard Lebedyuk

Personally, I prefer to use all out of the box, so as not to produce zoo libraries/technologies/languages, etc.

Really, both operations possible to execute at a time, for instance so:

<FONT COLOR="#0000ff">w $$$FormatText</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"Create a signature and convert it to base64 (%1)"</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">file64</FONT><FONT COLOR="#000000">),!
</FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#0000ff">$$$FormatText</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -sign %1 %2 | openssl base64 -out %3 -nopad"</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileKey</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">fileMsg</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">file64</FONT><FONT COLOR="#000000">)
</FONT><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">,!!
</FONT><FONT COLOR="#0000ff">d $zf</FONT><FONT COLOR="#000000">(-1,</FONT><FONT COLOR="#800000">cmd</FONT><FONT COLOR="#000000">)</FONT>
0
Eduard Lebedyuk  Jul 11, 2017 to Stefan Cronje

Ensemble runs under other user, so:

1. Compare environment variables from terminal and Ensemble (path is especially important):

do $zf(-1,"set > vars.txt")

2. Check that ensemble can access all required files.

3. Check working directory.

4. (Optional) Provide full paths to all files and binaries.

0