EldoS | Feel safer!

Software components for data protection, secure storage and transfer

Sign a string

Also by EldoS: RawDisk
Access locked and protected files in Windows, read and write disks and partitions and more.
#34198
Posted: 08/06/2015 06:35:58
by Eduardo Helminsky (Standard support level)
Joined: 08/20/2010
Posts: 102

I am trying to sign a string and I am using an example provided by governor.

The example is:
Code
static void Main(string[] args)
      {
         // To idendify the Smart Card CryptoGraphic Providers on your
         // computer, use the Microsoft Registry Editor (Regedit.exe).
         // The available Smart Card CryptoGraphic Providers are listed
         // in HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Defaults\Provider.
         // Create a new CspParameters object that identifies a
         // Smart Card CryptoGraphic Provider.
         // The 1st parameter comes from HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Defaults\Provider Types.
         // The 2nd parameter comes from HKEY_LOCAL_MACHINE\Software\Microsoft\Cryptography\Defaults\Provider.
         CspParameters csp = new CspParameters(1, "eToken Base Cryptographic Provider");

         csp.Flags = CspProviderFlags.UseDefaultKeyContainer;

         // Initialize an RSACryptoServiceProvider object using
         // the CspParameters object.

         RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);

         // Create some data to sign.
         byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };     // CNPJs vinculados

         Console.WriteLine("Data     : " + BitConverter.ToString(data));

         // Sign the data using the Smart Card CryptoGraphic Provider.
         byte[] sig = rsa.SignData(data, "SHA256");

         Console.WriteLine("Signature: " + BitConverter.ToString(sig));

         string base64sign = Convert.ToBase64String(sig);

         Console.WriteLine("Base64   : " + base64sign);
         Console.WriteLine("Tamanho  : " + base64sign.Length.ToString());

         // Verify the data using the Smart Card CryptoGraphic Provider.
         bool verified = rsa.VerifyData(data, "SHA256", sig);

         Console.WriteLine("Verified : " + verified);
      }


And the code I am using to sign it is the following:

Code
function AssinarString(cStr: String): String;
var KeyMaterial: TElPublicKeyMaterial;
    Crypto: TElRSAPublicKeyCrypto;
    sInput: TStringStream;
    sOutput: TStringStream;
begin
     if FCert = nil then begin
        raise Exception.Create('@Nenhum certificado digital foi informado para assinar');
     end;

     Result := '';

     KeyMaterial := TElRSAKeyMaterial.Create;
     try
        KeyMaterial.Assign(FCert.KeyMaterial);
        Crypto := TElRSAPublicKeyCrypto.Create;
        try
           Crypto.InputEncoding := pkeBase64;
           Crypto.OutputEncoding := pkeBase64;
           Crypto.KeyMaterial := KeyMaterial;
           Crypto.HashAlgorithm := SB_ALGORITHM_DGST_SHA256;
           sInput := TStringStream.Create(cStr,TEncoding.ASCII);
           try
              sOutput := TStringStream.Create;
              try
                 Crypto.SignDetached(sInput, sOutput);
                 Result := sOutput.DataString;
              finally
                 sOutput.Free;
              end;
           finally
              sInput.Free;
           end;
        finally
           Crypto.Free;
        end;
     finally
        KeyMaterial.Free;
     end;
end;


There is a software validator to check the signature and I have tried to sign using SHA1 too and I receive the information the validation is incorrect. I am using A3 certificate.

Could you help me ?
#34199
Posted: 08/06/2015 06:46:08
by Vsevolod Ievgiienko (EldoS Corp.)

Thank you for contacting us.

Your code is correct. Please try to use TMemoryStream instead of TStringStream as it can corrupt incoming data in some cases.

Also, please use CODE tag to mark the code in your messages.
#34218
Posted: 08/11/2015 14:48:57
by Eduardo Helminsky (Standard support level)
Joined: 08/20/2010
Posts: 102

Hi again. I have changed the code like you recommended by I am still having "invalid validation" from the governor site. What am I doing wrong ?
I am using Delphi XE (Unicode) and I "think" the conversion from string to memorystream is correct. What other things can I test (try/error method) to see if one of them solved my problem ?

Code
function AssinarString(cStr: String): String;
var KeyMaterial: TElPublicKeyMaterial;
    Crypto: TElRSAPublicKeyCrypto;
    sInput: TMemoryStream;
    sOutput: TMemoryStream;
begin
     if FCert = nil then begin
        raise Exception.Create('@Nenhum certificado digital foi informado para assinar');
     end;

     Result := '';

     KeyMaterial := TElRSAKeyMaterial.Create;
     try
        KeyMaterial.Assign(FCert.KeyMaterial);
        Crypto := TElRSAPublicKeyCrypto.Create;
        try
           Crypto.InputEncoding := pkeBase64;
           Crypto.OutputEncoding := pkeBase64;
           Crypto.KeyMaterial := KeyMaterial;
           Crypto.HashAlgorithm := SB_ALGORITHM_DGST_SHA256;

           sInput := TMemoryStream.Create;
           try
              sInput.Write(cStr[1], Length(cStr));
              sOutput := TStringStream.Create;
              try
                 Crypto.SignDetached(sInput, sOutput);
                 SetString(Result, PAnsiChar(sOutput.Memory), sOutput.Size);
              finally
                 sOutput.Free;
              end;
           finally
              sInput.Free;
           end;
        finally
           Crypto.Free;
        end;
     finally
        KeyMaterial.Free;
     end;
end;
#34219
Posted: 08/11/2015 15:42:34
by Eugene Mayevski (EldoS Corp.)

You need to insert "sInput.Position := 0" to line 26. The components don't rewind the stream when processing it so when you pass the stream to the component, the latter processes the stream from its current position, not from the beginning.


Sincerely yours
Eugene Mayevski
#34220
Posted: 08/11/2015 15:43:51
by Ken Ivanov (EldoS Corp.)

Hi Eduardo,

1) Your code is full of Unicode strings, and some operations involving them are incorrect (e.g. the sInput.Write(cStr[1], Length(cStr) will only write a half of the string to the memory stream, as each character in the string is represented by 2 bytes). Please try to move all the operations to byte arrays to eliminate all hidden conversion pitfalls.

2) I've just noticed that you set Crypto.InputEncoding to pkeBase64. Is the input you pass in sInput actually base64-encoded?

Ken
#34221
Posted: 08/11/2015 17:01:00
by Eduardo Helminsky (Standard support level)
Joined: 08/20/2010
Posts: 102

First of all, thanks to you Ken and Eugene.

I have fixed the code with "position of stream" (always I forgot about it) and I have replaced the code to move a string to stream.

Instead of
Code
sInput.Write(cStr[1], Length(cStr));

Now I use
sInput.Write(Pointer(cStr)^, Length(cStr) * SizeOf(Char));


The input string is just a sequence of numbers: 0144727800017407977033000135 and it is a plain text.

I tried to remove the line to inform base64 from input and it did not work too.
#34222
Posted: 08/11/2015 17:26:25
by Ken Ivanov (EldoS Corp.)

Eduardo,

Still, there is a pitfall that the verifying code might expect data to be provided in form of ANSI or UTF8 strings and not UTF16, as it is in your code.

I suggest that you start with reproducing the exact logic of the sample C# code in your Delphi application. Please try to sign the 8 byte array given in the sample code (byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };) and check if the signature validates as expected. I'm 99% sure that the problem has something to do with string normalization, so your code and the verifying tool basically operate with different inputs.

Ken
#34225
Posted: 08/12/2015 07:36:06
by Eduardo Helminsky (Standard support level)
Joined: 08/20/2010
Posts: 102

Ken

I have stopped and think again, the problem was with string provided in plain text instead of Base64. After I convert it to Base64 everything works fine. Thank again for your contribution.

The final code:

Code
function AssinarString(cStr: String): String;
var KeyMaterial: TElPublicKeyMaterial;
    Crypto: TElRSAPublicKeyCrypto;
    sInput: TMemoryStream;
    sOutput: TMemoryStream;
begin
     if FCert = nil then begin
        raise Exception.Create('@Nenhum certificado digital foi informado para assinar');
     end;

     Result := '';

     cStr := Base64EncodeString(cStr,False);

     KeyMaterial := TElRSAKeyMaterial.Create;
     try
        KeyMaterial.Assign(FCert.KeyMaterial);

        Crypto := TElRSAPublicKeyCrypto.Create;
        try
           Crypto.InputEncoding := pkeBase64;
           Crypto.OutputEncoding := pkeBase64;
           Crypto.KeyMaterial := KeyMaterial;
           Crypto.HashAlgorithm := SB_ALGORITHM_DGST_SHA256;

           sInput := TMemoryStream.Create;
           try
              sInput.Write(Pointer(cStr)^, Length(cStr) * SizeOf(Char));
              sInput.Position := 0;
              sOutput := TStringStream.Create;
              try
                 Crypto.SignDetached(sInput, sOutput);
                 SetString(Result, PAnsiChar(sOutput.Memory), sOutput.Size);
              finally
                 sOutput.Free;
              end;
           finally
              sInput.Free;
           end;
        finally
           Crypto.Free;
        end;
     finally
        KeyMaterial.Free;
     end;
end;
#34226
Posted: 08/12/2015 07:38:44
by Vsevolod Ievgiienko (EldoS Corp.)

Code
Crypto.InputEncoding := pkeBase64;

Indeed we've missed this line. You can change InputEncoding property value to eliminate Base64 for input data.
Also by EldoS: Solid File System
A virtual file system that offers a feature-rich storage for application documents and data with built-in compression and encryption.

Reply

Statistics

Topic viewed 4602 times

Number of guests: 1, registered members: 0, in total hidden: 0




|

Back to top

As of July 15, 2016 EldoS Corporation will operate as a division of /n software inc. For more information, please read the announcement.

Got it!