CryptoSys Home > PKI > An improvement in CryptoSys PKI Pro v11.0 we forgot to mention

An improvement in CryptoSys PKI Pro v11.0 we forgot to mention


4 June 2016: Here is a change we forgot to mention when we released CryptoSys PKI Pro v11.0 in March 2016 that improves the SIG_SignData and SIG_SignFile functions and the corresponding .NET methods Sig.SignData, Sig.SignFile and Sig.SignDigest (the "SIG functions").

There's also some C# code that compares the old and new ways together with performance measuring code.

Pass the internal keystring instead of the filename and password

The SIG_SignData() function and its companions (the "SIG functions") were designed for a simplified interface to create a signature value in base64 format directly from the message, private key file and the keyfile's password. In pseudo-code:

    sig = Sign(message, keyfile, password)

This is fine for a one-off operation, but can be improved if you want to call the function repeatedly with the same private key.

As of CryptoSys PKI Pro v11.0 and above you can pass an internal key string to the "SIG functions" instead of the filename and password, so removing the need to repeatedly access and decrypt the key file.

    keystr = ReadPrivateKey(keyfile, password)
    foreach msg in Messages:
        sig = Sign(message, keystr)

This improves security as well as giving speed improvements (we typically get a 20% improvement, YMMV). You can create the internal key string once and then wipe the password. The internal key is encrypted using an internal key and is only valid for the duration of the session. You can wipe this internal key when you are are finished with it.

    keystr = ReadPrivateKey(keyfile, password)
    Wipe(password)
    foreach msg in Messages:
        sig = Sign(message, keystr)
    Wipe(keystr)

[Go to top]

Some C# code

The following C# code shows an example to demonstrate the old way and the new way to sign an arbitrary string, and a way to measure any improvement when done repeatedly. With thanks to Pavel Vladov for his technique to Measure C# Code Performance

The private key used in the example is here and the corresponding certificate is here. Note that the arbitrary strings are just random bytes encoded in base58.

namespace RsaTestSig
{
    class Program
    {
        // Original way to sign a string using Sig.SignData() with a key file and password
        public static string SignStringUsingRSAKeyFile(string Msg, string PrivateKeyFilePath, string PrivateKeyPW)
        {
            string sig;
            byte[] arrData;
            arrData = System.Text.UTF8Encoding.UTF8.GetBytes(Msg);
            sig = Sig.SignData(arrData, PrivateKeyFilePath, PrivateKeyPW, SigAlgorithm.Default);
            return sig;
        }

        // New way - pass the internal key string
        // -- works for Sig.SignData() in CryptoSys PKI v11.0 and above
        public static string SignStringUsingRSAIntKeyString(string Msg, string PrivateKeyString)
        {
            string sig;
            byte[] arrData;
            arrData = System.Text.UTF8Encoding.UTF8.GetBytes(Msg);
            sig = Sig.SignData(arrData, PrivateKeyString, null, SigAlgorithm.Default);
            return sig;
        }

        static void quick_demo()
        {
            string keyFile = @"AlicePrivRSASign.epk";
            string msg;
            string sig;

            // 1. Original method using key file
            // -- key file is read each time, convenient for a one-off.
            msg = "abc";
            Console.WriteLine("MSG={0}", msg);
            sig = SignStringUsingRSAKeyFile(msg, keyFile, "password");
            Console.WriteLine("SIG={0}", sig);
            Debug.Assert(sig.Length > 0);

            // 2. Read in private key to an encrypted internal string
            // -- valid until process ends. 

            // Read in the private key once
            StringBuilder sbKeyStr;
            sbKeyStr = Rsa.ReadPrivateKey(keyFile, "password");
            Console.WriteLine("Key size={0}", Rsa.KeyBits(sbKeyStr.ToString()));
            for (int i = 0; i < 3; i++) {
                msg = Cnv.ToBase58(Rng.Bytes(48));
                Console.WriteLine("MSG{0}={1}", i+1, msg);
                sig = SignStringUsingRSAIntKeyString(msg, sbKeyStr.ToString());
                Console.WriteLine("SIG{0}={1}", i+1, sig);
                Debug.Assert(sig.Length > 0);
            }
            // Clean up
            Wipe.String(sbKeyStr);
        }


        // Code performance technique from Pavel Vladov
        // http://www.pvladov.com/2012/06/measure-code-performance.html

        static double computeAverageTime(int repetitions, long[] times)
        {
            // Sort the elapsed times for all test runs
            Array.Sort(times);
            // Calculate the total times discarding
            // the 5% min and 5% max test times
            long totalTime = 0;
            int discardCount = (int)Math.Round(repetitions * 0.05);
            int count = repetitions - discardCount;
            for (int i = discardCount; i < count; i++) {
                totalTime += times[i];
            }
            double averageTime = ((double)totalTime) / (count - discardCount);
            return averageTime;
        }


        static void test_performance()
        {
            string keyFile = @"AlicePrivRSASign.epk";
            string msg;
            string sig;
            string keyStr;
            int nbits;

            const int repetitions = 100;
            long[] times = new long[repetitions];
            double avgTime1, avgTime2;

            msg = Cnv.ToBase58(Rng.Bytes(48));
            Console.WriteLine("MSG={0}", msg);

            Console.WriteLine("Doing {0} tests using SignStringUsingRSAKeyFile()...", repetitions);

            for (int i = 0; i < repetitions; i++) {
                Stopwatch stopwatch = Stopwatch.StartNew();

                sig = SignStringUsingRSAKeyFile(msg, keyFile, "password");
                Debug.Assert(sig.Length > 0);

                stopwatch.Stop();
                times[i] = stopwatch.ElapsedMilliseconds;
            }

            avgTime1 = computeAverageTime(repetitions, times);
            Console.WriteLine("Average time: {0:N2} ms", avgTime1);


            keyStr = Rsa.ReadPrivateKey(keyFile, "password").ToString();
            nbits = Rsa.KeyBits(keyStr);
            Console.WriteLine("Key size={0}", nbits);
            Debug.Assert(nbits > 0);
            
            Console.WriteLine("Doing {0} tests using SignStringUsingRSAIntKeyString()...", repetitions);

            for (int i = 0; i < repetitions; i++) {
                Stopwatch stopwatch = Stopwatch.StartNew();

                sig = SignStringUsingRSAIntKeyString(msg, keyStr);
                Debug.Assert(sig.Length > 0);

                stopwatch.Stop();
                times[i] = stopwatch.ElapsedMilliseconds;
            }

            avgTime2 = computeAverageTime(repetitions, times);
            Console.WriteLine("Average time: {0:N2} ms", avgTime2);
            Console.WriteLine("Improvement = {0:N2}%", (avgTime1 - avgTime2) * 100 / avgTime1);
        }


        static void Main(string[] args)
        {
            quick_demo();
            //test_performance();
        }

    }
}

The output for quick_demo() should look like this:

MSG=abc
SIG=YK1aePtKQDDsVCyJdM0V9VOE6DZVTO3ZoyLV9BNcYmep0glwxU5mUQcLAUTUOETImTIN2Pp4Gffr
xqdxUoczLshnXBNhg7P4ofge+WlBgmcTCnVv27LHHZpmdEbjTg6tnPMb+2b4FvMZ0LfkMKXyiRVTmG4A
NyAmHH6QIsDZ8R8=
Key size=1024
MSG1=48Q8BvG4GjwxCTZPEJseWYpxGHbEHtY9RbmJdFFDp91u5KELoZWdNLZabW57JaN9fP
SIG1=s1Pv47e/G/tvK74jBLFEqDAWTFCRAvbwvSfWF6W5fPOK0JheotkFrn2bJTwzc2Hlg1PVUdhKEmv
wZ9Z5KL2Wz7BuhVpbxnZ3LidyiYSKEWw88hSv0EZ72/Xjm9mBzA50mhYlVozYkjlM/28a6JXbbXSgNQT
6irbEJAXsowGmr3Y=
MSG2=4QsRTC8GwEsc6WedRJQagrXtd341cAEQm4bo9Y2XnGmAtPAJWtuj1fE2fcDrXpxUFK
SIG2=kj4tqg7cjLjSG+40Lxn+wDO2CKa7yegzdGr0NhhmyiuiwzWgA5KTtaiT+En8XmTl20H1EYSsTFL
Z22TBiOF/wM8i7feoBgrT+wnaUrDqUyMcZ4kzi91GiyL0N0rSojMP5+PLVhPghpLJlI1sr4x/DIxa27x
WAQb0C+wQq7HG6PU=
MSG3=26b4zsubrdosZscLEG4ZGXntB1GKZTFYq9g34Qj2cQ7AAL4TKYLTWrmxzrQxnMBR3N
SIG3=vMBAsq45AnaK/dYGWkBqGm++0LbqBjvmo9DTI+NSQyiY9hDn9YtDAtpB1Vm19Aa63SwD8JFwTrI
ACxB4zoJ1QzZq/lsX5jB3frp+1Q3uQrddaeVoY5P/NBcAqLJWdJlmO9CBDhlmDQP/uX9IYWVi8/6/P7s
wIhhsbTgfDudO4As=

[Go to top]

Contact

For more information, or to comment on this page, please send us a message.

This page last updated 15 August 2025

[Go to top]