Certificate signing produces different signature when on server
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
|
show 3 more comments
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
Do they both decrypt to the same value, "test"?
– Ryan Wilson
Nov 14 '18 at 21:23
1
Does the signature verify withcerts[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 usecert.PublicKey.Key as RSACryptoServiceProvider(or rebuilt usingCspParameters) 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
|
show 3 more comments
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
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
c# certificate signature sha256
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 withcerts[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 usecert.PublicKey.Key as RSACryptoServiceProvider(or rebuilt usingCspParameters) 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
|
show 3 more comments
Do they both decrypt to the same value, "test"?
– Ryan Wilson
Nov 14 '18 at 21:23
1
Does the signature verify withcerts[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 usecert.PublicKey.Key as RSACryptoServiceProvider(or rebuilt usingCspParameters) 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
|
show 3 more comments
1 Answer
1
active
oldest
votes
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).
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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).
add a comment |
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).
add a comment |
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).
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).
answered Nov 16 '18 at 16:06
bartonjsbartonjs
13.5k12254
13.5k12254
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 usingCspParameters) 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