Certificate signing produces different signature when on server










3















I am trying to sign some data using a certificate private key. The issue I'm finding is that the signature is different depending on if I'm executing it locally or on a server.



I'm using the following code as a test, running under the same user both locally and on the server:



using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace TestSignature

class Program

static void Main(string args)


var key = SigningKeyFromCertificate(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, "thumbprint");
var alg = CryptoConfig.MapNameToOID("SHA256");
var data = Encoding.UTF8.GetBytes("test");
var sig = key.SignData(data, alg);

Console.WriteLine(Convert.ToBase64String(sig));



private static RSACryptoServiceProvider SigningKeyFromCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, string findValue)

X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);

var certs = store.Certificates.Find(findType, findValue, false);
if (certs?.Count > 0)

var cert = certs[0];
if (cert.HasPrivateKey)

// Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
var key = cert.PrivateKey as RSACryptoServiceProvider;
var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;
var parameters = new CspParameters(enhanced.ProviderType, enhanced.ProviderName, key.CspKeyContainerInfo.UniqueKeyContainerName);
return new RSACryptoServiceProvider(parameters);

else

throw new Exception($"No private key access to cert 'findValue.'");


else

throw new Exception($"Cert 'findValue' not found!");






Locally, I get the following signature:



YUjspKhLl7v3u5VQkh1PfHytMTpEtbAftxOA5v4lmph3B4ssVlZp7KedO5NW9K5L222Kz9Ik9/55NirS0cNCz/cDhEFRtD4daJ9qLRuM8oD5hCj6Jt9Vc6WeS2he+Cqfoylnv4V9plfi1xw8y7EyAf4C77BGkXOdyP5wyz2Xubo=


On the server, I get this one instead:



u1RUDwbBlUpOgNNkAjXhYEWfVLGpMOa0vEfm6PUkB4y9PYBk1lDmCAp+488ta+ipbTdSDLM9btRqsQfZ7JlIn/dIBw9t5K63Y7dcDcc7gDLE1+umLJ7EincMcdwUv3YQ0zCvzc9RrP0jKJManV1ptQNnODpMktGYAq1KmJb9aTY=


Any idea of what could be different? I would think, with the same certificate, the same code, and the same data, the signature should be the same.



(The example is written in C# 4.5.2.)










share|improve this question






















  • Do they both decrypt to the same value, "test"?

    – Ryan Wilson
    Nov 14 '18 at 21:23







  • 1





    Does the signature verify with certs[0].PublicKey.Key? It's possible that one (or both) have a broken/mismatched private key reference. (You could also check if the certs are the same, Convert.ToBase64String(certs[0].RawData)))

    – bartonjs
    Nov 14 '18 at 22:13






  • 1





    @bartonjs: If I use cert.PublicKey.Key as RSACryptoServiceProvider (or rebuilt using CspParameters) to VerifyData, it fails both locally and on the server. Does that mean that both locations have mismatched private key references?

    – zimdanen
    Nov 15 '18 at 14:38






  • 2





    @zimdanen yep. And I think it’s because you used UniqueContainerName instead of ContainerName. You also want to set the ExistingOnly flag, because that would have told you that you were causing a key to be created the first time you ran it (instead of opened) — but now it’s saved so you are opening the same random key again. (And really the answer is to upgrade to a newer .NET and use GetRSAPrivateKey, and avoid the need to reopen it)

    – bartonjs
    Nov 16 '18 at 4:03






  • 1





    @zimdanen GetRSAPrivateKey should never need correcting. Largely because it avoids RSACryptoServiceProvider most of the time.

    – bartonjs
    Nov 16 '18 at 14:26















3















I am trying to sign some data using a certificate private key. The issue I'm finding is that the signature is different depending on if I'm executing it locally or on a server.



I'm using the following code as a test, running under the same user both locally and on the server:



using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace TestSignature

class Program

static void Main(string args)


var key = SigningKeyFromCertificate(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, "thumbprint");
var alg = CryptoConfig.MapNameToOID("SHA256");
var data = Encoding.UTF8.GetBytes("test");
var sig = key.SignData(data, alg);

Console.WriteLine(Convert.ToBase64String(sig));



private static RSACryptoServiceProvider SigningKeyFromCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, string findValue)

X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);

var certs = store.Certificates.Find(findType, findValue, false);
if (certs?.Count > 0)

var cert = certs[0];
if (cert.HasPrivateKey)

// Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
var key = cert.PrivateKey as RSACryptoServiceProvider;
var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;
var parameters = new CspParameters(enhanced.ProviderType, enhanced.ProviderName, key.CspKeyContainerInfo.UniqueKeyContainerName);
return new RSACryptoServiceProvider(parameters);

else

throw new Exception($"No private key access to cert 'findValue.'");


else

throw new Exception($"Cert 'findValue' not found!");






Locally, I get the following signature:



YUjspKhLl7v3u5VQkh1PfHytMTpEtbAftxOA5v4lmph3B4ssVlZp7KedO5NW9K5L222Kz9Ik9/55NirS0cNCz/cDhEFRtD4daJ9qLRuM8oD5hCj6Jt9Vc6WeS2he+Cqfoylnv4V9plfi1xw8y7EyAf4C77BGkXOdyP5wyz2Xubo=


On the server, I get this one instead:



u1RUDwbBlUpOgNNkAjXhYEWfVLGpMOa0vEfm6PUkB4y9PYBk1lDmCAp+488ta+ipbTdSDLM9btRqsQfZ7JlIn/dIBw9t5K63Y7dcDcc7gDLE1+umLJ7EincMcdwUv3YQ0zCvzc9RrP0jKJManV1ptQNnODpMktGYAq1KmJb9aTY=


Any idea of what could be different? I would think, with the same certificate, the same code, and the same data, the signature should be the same.



(The example is written in C# 4.5.2.)










share|improve this question






















  • Do they both decrypt to the same value, "test"?

    – Ryan Wilson
    Nov 14 '18 at 21:23







  • 1





    Does the signature verify with certs[0].PublicKey.Key? It's possible that one (or both) have a broken/mismatched private key reference. (You could also check if the certs are the same, Convert.ToBase64String(certs[0].RawData)))

    – bartonjs
    Nov 14 '18 at 22:13






  • 1





    @bartonjs: If I use cert.PublicKey.Key as RSACryptoServiceProvider (or rebuilt using CspParameters) to VerifyData, it fails both locally and on the server. Does that mean that both locations have mismatched private key references?

    – zimdanen
    Nov 15 '18 at 14:38






  • 2





    @zimdanen yep. And I think it’s because you used UniqueContainerName instead of ContainerName. You also want to set the ExistingOnly flag, because that would have told you that you were causing a key to be created the first time you ran it (instead of opened) — but now it’s saved so you are opening the same random key again. (And really the answer is to upgrade to a newer .NET and use GetRSAPrivateKey, and avoid the need to reopen it)

    – bartonjs
    Nov 16 '18 at 4:03






  • 1





    @zimdanen GetRSAPrivateKey should never need correcting. Largely because it avoids RSACryptoServiceProvider most of the time.

    – bartonjs
    Nov 16 '18 at 14:26













3












3








3








I am trying to sign some data using a certificate private key. The issue I'm finding is that the signature is different depending on if I'm executing it locally or on a server.



I'm using the following code as a test, running under the same user both locally and on the server:



using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace TestSignature

class Program

static void Main(string args)


var key = SigningKeyFromCertificate(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, "thumbprint");
var alg = CryptoConfig.MapNameToOID("SHA256");
var data = Encoding.UTF8.GetBytes("test");
var sig = key.SignData(data, alg);

Console.WriteLine(Convert.ToBase64String(sig));



private static RSACryptoServiceProvider SigningKeyFromCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, string findValue)

X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);

var certs = store.Certificates.Find(findType, findValue, false);
if (certs?.Count > 0)

var cert = certs[0];
if (cert.HasPrivateKey)

// Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
var key = cert.PrivateKey as RSACryptoServiceProvider;
var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;
var parameters = new CspParameters(enhanced.ProviderType, enhanced.ProviderName, key.CspKeyContainerInfo.UniqueKeyContainerName);
return new RSACryptoServiceProvider(parameters);

else

throw new Exception($"No private key access to cert 'findValue.'");


else

throw new Exception($"Cert 'findValue' not found!");






Locally, I get the following signature:



YUjspKhLl7v3u5VQkh1PfHytMTpEtbAftxOA5v4lmph3B4ssVlZp7KedO5NW9K5L222Kz9Ik9/55NirS0cNCz/cDhEFRtD4daJ9qLRuM8oD5hCj6Jt9Vc6WeS2he+Cqfoylnv4V9plfi1xw8y7EyAf4C77BGkXOdyP5wyz2Xubo=


On the server, I get this one instead:



u1RUDwbBlUpOgNNkAjXhYEWfVLGpMOa0vEfm6PUkB4y9PYBk1lDmCAp+488ta+ipbTdSDLM9btRqsQfZ7JlIn/dIBw9t5K63Y7dcDcc7gDLE1+umLJ7EincMcdwUv3YQ0zCvzc9RrP0jKJManV1ptQNnODpMktGYAq1KmJb9aTY=


Any idea of what could be different? I would think, with the same certificate, the same code, and the same data, the signature should be the same.



(The example is written in C# 4.5.2.)










share|improve this question














I am trying to sign some data using a certificate private key. The issue I'm finding is that the signature is different depending on if I'm executing it locally or on a server.



I'm using the following code as a test, running under the same user both locally and on the server:



using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace TestSignature

class Program

static void Main(string args)


var key = SigningKeyFromCertificate(StoreName.My, StoreLocation.LocalMachine, X509FindType.FindByThumbprint, "thumbprint");
var alg = CryptoConfig.MapNameToOID("SHA256");
var data = Encoding.UTF8.GetBytes("test");
var sig = key.SignData(data, alg);

Console.WriteLine(Convert.ToBase64String(sig));



private static RSACryptoServiceProvider SigningKeyFromCertificate(StoreName storeName, StoreLocation storeLocation, X509FindType findType, string findValue)

X509Store store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadOnly);

var certs = store.Certificates.Find(findType, findValue, false);
if (certs?.Count > 0)

var cert = certs[0];
if (cert.HasPrivateKey)

// Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
var key = cert.PrivateKey as RSACryptoServiceProvider;
var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;
var parameters = new CspParameters(enhanced.ProviderType, enhanced.ProviderName, key.CspKeyContainerInfo.UniqueKeyContainerName);
return new RSACryptoServiceProvider(parameters);

else

throw new Exception($"No private key access to cert 'findValue.'");


else

throw new Exception($"Cert 'findValue' not found!");






Locally, I get the following signature:



YUjspKhLl7v3u5VQkh1PfHytMTpEtbAftxOA5v4lmph3B4ssVlZp7KedO5NW9K5L222Kz9Ik9/55NirS0cNCz/cDhEFRtD4daJ9qLRuM8oD5hCj6Jt9Vc6WeS2he+Cqfoylnv4V9plfi1xw8y7EyAf4C77BGkXOdyP5wyz2Xubo=


On the server, I get this one instead:



u1RUDwbBlUpOgNNkAjXhYEWfVLGpMOa0vEfm6PUkB4y9PYBk1lDmCAp+488ta+ipbTdSDLM9btRqsQfZ7JlIn/dIBw9t5K63Y7dcDcc7gDLE1+umLJ7EincMcdwUv3YQ0zCvzc9RrP0jKJManV1ptQNnODpMktGYAq1KmJb9aTY=


Any idea of what could be different? I would think, with the same certificate, the same code, and the same data, the signature should be the same.



(The example is written in C# 4.5.2.)







c# certificate signature sha256






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 14 '18 at 21:20









zimdanenzimdanen

3,48063378




3,48063378












  • Do they both decrypt to the same value, "test"?

    – Ryan Wilson
    Nov 14 '18 at 21:23







  • 1





    Does the signature verify with certs[0].PublicKey.Key? It's possible that one (or both) have a broken/mismatched private key reference. (You could also check if the certs are the same, Convert.ToBase64String(certs[0].RawData)))

    – bartonjs
    Nov 14 '18 at 22:13






  • 1





    @bartonjs: If I use cert.PublicKey.Key as RSACryptoServiceProvider (or rebuilt using CspParameters) to VerifyData, it fails both locally and on the server. Does that mean that both locations have mismatched private key references?

    – zimdanen
    Nov 15 '18 at 14:38






  • 2





    @zimdanen yep. And I think it’s because you used UniqueContainerName instead of ContainerName. You also want to set the ExistingOnly flag, because that would have told you that you were causing a key to be created the first time you ran it (instead of opened) — but now it’s saved so you are opening the same random key again. (And really the answer is to upgrade to a newer .NET and use GetRSAPrivateKey, and avoid the need to reopen it)

    – bartonjs
    Nov 16 '18 at 4:03






  • 1





    @zimdanen GetRSAPrivateKey should never need correcting. Largely because it avoids RSACryptoServiceProvider most of the time.

    – bartonjs
    Nov 16 '18 at 14:26

















  • Do they both decrypt to the same value, "test"?

    – Ryan Wilson
    Nov 14 '18 at 21:23







  • 1





    Does the signature verify with certs[0].PublicKey.Key? It's possible that one (or both) have a broken/mismatched private key reference. (You could also check if the certs are the same, Convert.ToBase64String(certs[0].RawData)))

    – bartonjs
    Nov 14 '18 at 22:13






  • 1





    @bartonjs: If I use cert.PublicKey.Key as RSACryptoServiceProvider (or rebuilt using CspParameters) to VerifyData, it fails both locally and on the server. Does that mean that both locations have mismatched private key references?

    – zimdanen
    Nov 15 '18 at 14:38






  • 2





    @zimdanen yep. And I think it’s because you used UniqueContainerName instead of ContainerName. You also want to set the ExistingOnly flag, because that would have told you that you were causing a key to be created the first time you ran it (instead of opened) — but now it’s saved so you are opening the same random key again. (And really the answer is to upgrade to a newer .NET and use GetRSAPrivateKey, and avoid the need to reopen it)

    – bartonjs
    Nov 16 '18 at 4:03






  • 1





    @zimdanen GetRSAPrivateKey should never need correcting. Largely because it avoids RSACryptoServiceProvider most of the time.

    – bartonjs
    Nov 16 '18 at 14:26
















Do they both decrypt to the same value, "test"?

– Ryan Wilson
Nov 14 '18 at 21:23






Do they both decrypt to the same value, "test"?

– Ryan Wilson
Nov 14 '18 at 21:23





1




1





Does the signature verify with certs[0].PublicKey.Key? It's possible that one (or both) have a broken/mismatched private key reference. (You could also check if the certs are the same, Convert.ToBase64String(certs[0].RawData)))

– bartonjs
Nov 14 '18 at 22:13





Does the signature verify with certs[0].PublicKey.Key? It's possible that one (or both) have a broken/mismatched private key reference. (You could also check if the certs are the same, Convert.ToBase64String(certs[0].RawData)))

– bartonjs
Nov 14 '18 at 22:13




1




1





@bartonjs: If I use cert.PublicKey.Key as RSACryptoServiceProvider (or rebuilt using CspParameters) to VerifyData, it fails both locally and on the server. Does that mean that both locations have mismatched private key references?

– zimdanen
Nov 15 '18 at 14:38





@bartonjs: If I use cert.PublicKey.Key as RSACryptoServiceProvider (or rebuilt using CspParameters) to VerifyData, it fails both locally and on the server. Does that mean that both locations have mismatched private key references?

– zimdanen
Nov 15 '18 at 14:38




2




2





@zimdanen yep. And I think it’s because you used UniqueContainerName instead of ContainerName. You also want to set the ExistingOnly flag, because that would have told you that you were causing a key to be created the first time you ran it (instead of opened) — but now it’s saved so you are opening the same random key again. (And really the answer is to upgrade to a newer .NET and use GetRSAPrivateKey, and avoid the need to reopen it)

– bartonjs
Nov 16 '18 at 4:03





@zimdanen yep. And I think it’s because you used UniqueContainerName instead of ContainerName. You also want to set the ExistingOnly flag, because that would have told you that you were causing a key to be created the first time you ran it (instead of opened) — but now it’s saved so you are opening the same random key again. (And really the answer is to upgrade to a newer .NET and use GetRSAPrivateKey, and avoid the need to reopen it)

– bartonjs
Nov 16 '18 at 4:03




1




1





@zimdanen GetRSAPrivateKey should never need correcting. Largely because it avoids RSACryptoServiceProvider most of the time.

– bartonjs
Nov 16 '18 at 14:26





@zimdanen GetRSAPrivateKey should never need correcting. Largely because it avoids RSACryptoServiceProvider most of the time.

– bartonjs
Nov 16 '18 at 14:26












1 Answer
1






active

oldest

votes


















2














You have some code to reopen the CAPI key handle under PROV_RSA_AES:



// Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
var key = cert.PrivateKey as RSACryptoServiceProvider;
var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;

var parameters = new CspParameters(
enhanced.ProviderType,
enhanced.ProviderName,
key.CspKeyContainerInfo.UniqueKeyContainerName);

return new RSACryptoServiceProvider(parameters);


But key.CspKeyContainerInfo.UniqueKeyContainerName isn't the name of the key (it's the name of the file on disk where the key lives), so you're opening a brand new key (you're also generating a new ephemeral key just to ask what the default provider is). Since it's a named key it persists, and subsequent application executions resolve to the same key -- but a different "same" key on each computer.



A more stable way of reopening the key is



var cspParameters = new CspParameters

KeyContainerName = foo.CspKeyContainerInfo.KeyContainerName,
Flags = CspProviderFlags.UseExistingKey,
;


(since the provider type and name aren't specified they will use the defaults, and by saying UseExistingKey you get an exception if you reference a key that doesn't exist).




That said, the easiest fix is to stop using RSACryptoServiceProvider. .NET Framework 4.6 (and .NET Core 1.0) have a(n extension) method on X509Certificate2, GetRSAPrivateKey(), it returns an RSA (which you should avoid casting) which is usually RSACng (on Windows), but may be RSACryptoServiceProvider if only CAPI had a driver required for a HSM, and may be some other RSA in the future. Since RSACng handles SHA-2 better there's almost never a need to "reopen" the return object (even if it's RSACryptoServiceProvider, and even if the type isn't PROV_RSA_AES (24), that doesn't mean the HSM will fail to do SHA-2).






share|improve this answer






















    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53308897%2fcertificate-signing-produces-different-signature-when-on-server%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    You have some code to reopen the CAPI key handle under PROV_RSA_AES:



    // Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
    var key = cert.PrivateKey as RSACryptoServiceProvider;
    var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;

    var parameters = new CspParameters(
    enhanced.ProviderType,
    enhanced.ProviderName,
    key.CspKeyContainerInfo.UniqueKeyContainerName);

    return new RSACryptoServiceProvider(parameters);


    But key.CspKeyContainerInfo.UniqueKeyContainerName isn't the name of the key (it's the name of the file on disk where the key lives), so you're opening a brand new key (you're also generating a new ephemeral key just to ask what the default provider is). Since it's a named key it persists, and subsequent application executions resolve to the same key -- but a different "same" key on each computer.



    A more stable way of reopening the key is



    var cspParameters = new CspParameters

    KeyContainerName = foo.CspKeyContainerInfo.KeyContainerName,
    Flags = CspProviderFlags.UseExistingKey,
    ;


    (since the provider type and name aren't specified they will use the defaults, and by saying UseExistingKey you get an exception if you reference a key that doesn't exist).




    That said, the easiest fix is to stop using RSACryptoServiceProvider. .NET Framework 4.6 (and .NET Core 1.0) have a(n extension) method on X509Certificate2, GetRSAPrivateKey(), it returns an RSA (which you should avoid casting) which is usually RSACng (on Windows), but may be RSACryptoServiceProvider if only CAPI had a driver required for a HSM, and may be some other RSA in the future. Since RSACng handles SHA-2 better there's almost never a need to "reopen" the return object (even if it's RSACryptoServiceProvider, and even if the type isn't PROV_RSA_AES (24), that doesn't mean the HSM will fail to do SHA-2).






    share|improve this answer



























      2














      You have some code to reopen the CAPI key handle under PROV_RSA_AES:



      // Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
      var key = cert.PrivateKey as RSACryptoServiceProvider;
      var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;

      var parameters = new CspParameters(
      enhanced.ProviderType,
      enhanced.ProviderName,
      key.CspKeyContainerInfo.UniqueKeyContainerName);

      return new RSACryptoServiceProvider(parameters);


      But key.CspKeyContainerInfo.UniqueKeyContainerName isn't the name of the key (it's the name of the file on disk where the key lives), so you're opening a brand new key (you're also generating a new ephemeral key just to ask what the default provider is). Since it's a named key it persists, and subsequent application executions resolve to the same key -- but a different "same" key on each computer.



      A more stable way of reopening the key is



      var cspParameters = new CspParameters

      KeyContainerName = foo.CspKeyContainerInfo.KeyContainerName,
      Flags = CspProviderFlags.UseExistingKey,
      ;


      (since the provider type and name aren't specified they will use the defaults, and by saying UseExistingKey you get an exception if you reference a key that doesn't exist).




      That said, the easiest fix is to stop using RSACryptoServiceProvider. .NET Framework 4.6 (and .NET Core 1.0) have a(n extension) method on X509Certificate2, GetRSAPrivateKey(), it returns an RSA (which you should avoid casting) which is usually RSACng (on Windows), but may be RSACryptoServiceProvider if only CAPI had a driver required for a HSM, and may be some other RSA in the future. Since RSACng handles SHA-2 better there's almost never a need to "reopen" the return object (even if it's RSACryptoServiceProvider, and even if the type isn't PROV_RSA_AES (24), that doesn't mean the HSM will fail to do SHA-2).






      share|improve this answer

























        2












        2








        2







        You have some code to reopen the CAPI key handle under PROV_RSA_AES:



        // Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
        var key = cert.PrivateKey as RSACryptoServiceProvider;
        var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;

        var parameters = new CspParameters(
        enhanced.ProviderType,
        enhanced.ProviderName,
        key.CspKeyContainerInfo.UniqueKeyContainerName);

        return new RSACryptoServiceProvider(parameters);


        But key.CspKeyContainerInfo.UniqueKeyContainerName isn't the name of the key (it's the name of the file on disk where the key lives), so you're opening a brand new key (you're also generating a new ephemeral key just to ask what the default provider is). Since it's a named key it persists, and subsequent application executions resolve to the same key -- but a different "same" key on each computer.



        A more stable way of reopening the key is



        var cspParameters = new CspParameters

        KeyContainerName = foo.CspKeyContainerInfo.KeyContainerName,
        Flags = CspProviderFlags.UseExistingKey,
        ;


        (since the provider type and name aren't specified they will use the defaults, and by saying UseExistingKey you get an exception if you reference a key that doesn't exist).




        That said, the easiest fix is to stop using RSACryptoServiceProvider. .NET Framework 4.6 (and .NET Core 1.0) have a(n extension) method on X509Certificate2, GetRSAPrivateKey(), it returns an RSA (which you should avoid casting) which is usually RSACng (on Windows), but may be RSACryptoServiceProvider if only CAPI had a driver required for a HSM, and may be some other RSA in the future. Since RSACng handles SHA-2 better there's almost never a need to "reopen" the return object (even if it's RSACryptoServiceProvider, and even if the type isn't PROV_RSA_AES (24), that doesn't mean the HSM will fail to do SHA-2).






        share|improve this answer













        You have some code to reopen the CAPI key handle under PROV_RSA_AES:



        // Force use of Enhanced RSA and AES Cryptographic Provider to allow use of SHA256.
        var key = cert.PrivateKey as RSACryptoServiceProvider;
        var enhanced = new RSACryptoServiceProvider().CspKeyContainerInfo;

        var parameters = new CspParameters(
        enhanced.ProviderType,
        enhanced.ProviderName,
        key.CspKeyContainerInfo.UniqueKeyContainerName);

        return new RSACryptoServiceProvider(parameters);


        But key.CspKeyContainerInfo.UniqueKeyContainerName isn't the name of the key (it's the name of the file on disk where the key lives), so you're opening a brand new key (you're also generating a new ephemeral key just to ask what the default provider is). Since it's a named key it persists, and subsequent application executions resolve to the same key -- but a different "same" key on each computer.



        A more stable way of reopening the key is



        var cspParameters = new CspParameters

        KeyContainerName = foo.CspKeyContainerInfo.KeyContainerName,
        Flags = CspProviderFlags.UseExistingKey,
        ;


        (since the provider type and name aren't specified they will use the defaults, and by saying UseExistingKey you get an exception if you reference a key that doesn't exist).




        That said, the easiest fix is to stop using RSACryptoServiceProvider. .NET Framework 4.6 (and .NET Core 1.0) have a(n extension) method on X509Certificate2, GetRSAPrivateKey(), it returns an RSA (which you should avoid casting) which is usually RSACng (on Windows), but may be RSACryptoServiceProvider if only CAPI had a driver required for a HSM, and may be some other RSA in the future. Since RSACng handles SHA-2 better there's almost never a need to "reopen" the return object (even if it's RSACryptoServiceProvider, and even if the type isn't PROV_RSA_AES (24), that doesn't mean the HSM will fail to do SHA-2).







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 16 '18 at 16:06









        bartonjsbartonjs

        13.5k12254




        13.5k12254





























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53308897%2fcertificate-signing-produces-different-signature-when-on-server%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            27

            Top Tejano songwriter Luis Silva dead of heart attack at 64

            Category:Rhetoric