RSA Signature Example

Parent Previous Next

RSA Signature Example.

Here we will exemplify, how to sign some sensitive data in .NET and verify the signature in bytecode. This technique can be used to protect license files and other types of sensitive data from being modified by others.


This example contains the necessary C# and SCRAMBLECODE source code to fully demonstrate:


DISCLAIMER: The source code is provided "AS IS" with no guarantees what so ever. You may copy, modify and use it at your own risk.


The Byte Order Issue.

SCRAMBLECODE uses the native Microsoft Cryptographic API (also known as CryptoAPI or CAPI) for the security functionality.


A well known general problem exists when trying to mix native code with the .NET (and Java) world:

For any further details of the differences between these two Microsoft libraries, please consult the MSDN documentation.


However, due to the byte order difference it is necessary to include some extra code as demonstrated next.


The C# Source Code.

A three-phase setup is used for the C# source code:



//C# code:

       public void SignAndVerifyData()

       {

           //-------- PHASE 1: CREATE DATA, KEYS AND SIGNATURE IN .NET ------

           string textForSigning = "This is the original text data to sign";

           byte[] originalData = Encoding.ASCII.GetBytes(textForSigning);


           //Create a new pair of keys (4096 bit) and the crypto service provider

           RSACryptoServiceProvider cspRSA = new RSACryptoServiceProvider(4096);


           //Sign the data using a SHA1, SHA256, SHA384 or SHA512 hash and verify the signature

           //SHA1CryptoServiceProvider   sha = new SHA1CryptoServiceProvider();

           //SHA256CryptoServiceProvider sha = new SHA256CryptoServiceProvider();

           //SHA384CryptoServiceProvider sha = new SHA384CryptoServiceProvider();

           SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider();

           byte[] signature = cspRSA.SignData(originalData, sha);

           Boolean verified = cspRSA.VerifyData(originalData, sha, signature);

           if (!verified) throw new Exception("Signature could not be verified");



           //-------- PHASE 2: PREPARE FOR CryptoAPI AUTHENTICATION ------

           byte[] publKeyBlob = cspRSA.ExportCspBlob(false);

           Array.Reverse(signature); //<- Important: Reverse byte order.



           //-------- PHASE 3: VERIFY SIGNATURE IN BYTECODE ------

           //Create the VM (with bytecode from a CUF)

           string cufPath = @"C:\ScrambleCode\Projects\PublKeyAuth\Authenticate.CUF";

           string byteCode = "FILE=" + cufPath;

           Int32 vm = DLL.VMCreate(byteCode);

           

           //Pass the blobs as cell values

           DLL.VMCellSetBytes(vm, 0, 0, originalData);

           DLL.VMCellSetBytes(vm, 0, 1, signature);

           DLL.VMCellSetBytes(vm, 0, 2, publKeyBlob);


           //Execute and read the returned message

           Int32 result = DLL.VMExecute(vm, 0, 0, 0);

           string authMsg = DLL.VMCellGetString(vm, 1, 0);


           DLL.VMFree(vm);

       }




The SCRAMBLECODE Source.

Here we have the source code which is executed as bytecode in the VM above. It reads the cells containing the blobs and performs the public key authentication to verify the signature matches the original data. The result is returned as a message string in cell(1,0).


Please notice that @BinaryAuthenticate may throw an error in case you forget to reverse the signature, making it particular relevant to use a try-catch structure.



Class Main

{

 Public Function Main(Integer x, y, z) : Integer

 {

   String authMsg;

   Try {

     authMsg := VerifySignature();

   }

   Catch {

     authMsg := @ErrorMessage();

   }

   @CellSetString(1, 0, authMsg);

   Return 0;

 }


 Private Function VerifySignature() : String

 {

   Blob originalData, signature, publicKey;


   originalData := @CellGetBytes(0,0);

   signature := @CellGetBytes(0,1);

   publicKey := @CellGetBytes(0,2);


   If @BinaryAuthenticate(originalData, signature, publicKey, "SHA512") Then {

     Return "Result OK: Original data is untampered and matches the signature.";

   } Else {

     Return "ERROR: Original data failed authentication.";

   }

 }

}



TIP: Please notice you can include a public key permanently in your bytecode as a variable with a prefixed constant value. Just expand this example and use Blob.ToString and save it to a file for later use in a source file.