dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.28k stars 4.73k forks source link

ArgumentNullException when calling RSACertificateExtensions.GetRSAPrivateKey #17747

Closed beho closed 4 years ago

beho commented 8 years ago

Hi,

after migrating project to .NET Core 1.0.0 I had to change

RSACryptoServiceProvider cryptoProvider = (RSACryptoServiceProvider)_qzOptions.Certificate.PrivateKey;

to

RSA cryptoProvider = _qzOptions.Certificate.GetRSAPrivateKey();

Unfortunately this call throws following exception:

{System.ArgumentNullException: Value cannot be null.
Parameter name: source
   at System.Runtime.InteropServices.Marshal.CopyToManaged(IntPtr source, Object destination, Int32 startIndex, Int32 length)
   at Internal.Cryptography.Pal.Native.CRYPTOAPI_BLOB.ToByteArray()
   at Internal.Cryptography.Pal.CertificatePal.get_KeyAlgorithmParameters()
   at System.Security.Cryptography.X509Certificates.X509Certificate.GetKeyAlgorithmParameters()
   at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PublicKey()
   at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPrivateKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
   at System.Security.Cryptography.X509Certificates.RSACertificateExtensions.GetRSAPrivateKey(X509Certificate2 certificate)}

This code is for signing requests for printing using QZ Tray (https://qz.io/wiki/2.0-signing-messages). C# example on which my code is based is provided at https://github.com/qzind/tray/blob/2.0/assets/signing/sign-message.cs.

Thanks!

bartonjs commented 8 years ago

@beho That's really weird. I don't suppose you know how your certificate was generated?

It'd be great if we could get a copy of the public portion (assuming that cert.GetKeyAlgorithmParameters() on the public portion also throws) so that we can debug here and see what's going on.

beho commented 8 years ago

The process is described here – https://qz.io/wiki/generate-certificate. It claims that the certificate is generated in browser so I assume Web Cryptography API. Then I had to create pfx by running openssl pkcs12 -export -inkey private-key.pem -in digital-certificate.txt -out private-key.pfx as described in QZ's C# example (https://github.com/qzind/tray/blob/2.0/assets/signing/sign-message.cs).

Yes, call to GetKeyAlgorithmParameters() also fails. Public key as generated by their in-browser process is:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDh05CAXWp29GTbjk3gz
eCCe/1t7V3aNTwbtzkUtLZnbS9tge/+Iaqsz10IOWk3SndLhPIfaKUvX/pnkq5CX
IVyTTyRoFpyYrDfNoRmZ/3uTmMG50urk0Rg/+e4f2k32BfFTfB0W3V169+QQ6Xvv
uoyh62cppfi1msgFJ6WGmEF1r73Q6tK1vxfuA9wJfMWTl4Sg8nEf9NXsTc9VAwGK
RJbmTUN1b0xsqFvlFbxvaxPGwxNM29lXWlez5KEsh0sfUyTGQuTBtu5JMC57TGvL
0/TwgwrtOxQL5+N4lJAWnUQ+z3XXL694eSsuKlgw2yasO2ZwWnyzeap2vnN/CifU
gwIDAQAB
-----END PUBLIC KEY-----
bartonjs commented 8 years ago

Hm. That Public Key block looks normal to me. I don't suppose you have the full certificate? (-----BEGIN CERTIFICATE-----).

@AtsushiKan can you take a look at this one? I'm sure we can guard it with a defensive nullcheck somewhere; but I'd sort of like to understand what's happening. (Of course, it's also non-intuitive that we're calling get_PublicKey in GetPrivateKey, but I'm sure we have a reason)

beho commented 8 years ago

I'm not sure if that is what you mean but these are the contents of the digital-certificate.txt that was used in conjuction with private key to create pfx.

-----BEGIN CERTIFICATE-----
MIIE4jCCAsygAwIBAgIEMTI0NjALBgkqhkiG9w0BAQUwgZgxCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJOWTEbMBkGA1UECgwSUVogSW5kdXN0cmllcywgTExDMRswGQYD
VQQLDBJRWiBJbmR1c3RyaWVzLCBMTEMxGTAXBgNVBAMMEHF6aW5kdXN0cmllcy5j
b20xJzAlBgkqhkiG9w0BCQEWGHN1cHBvcnRAcXppbmR1c3RyaWVzLmNvbTAeFw0x
NjA0MDYyMTAwMDBaFw0xNzA0MDcyMTAwMDBaMIGtMQswCQYDVQQGDAJDWjEXMBUG
A1UECAwOQ3plY2ggUmVwdWJsaWMxDTALBgNVBAcMBEJybm8xGTAXBgNVBAoMEHNt
c3RpY2tldCBzLnIuby4xGTAXBgNVBAsMEHNtc3RpY2tldCBzLnIuby4xHjAcBgNV
BAMMFXBva2xhZG5hLnNtc3RpY2tldC5jejEgMB4GCSqGSIb3DQEJAQwRaW5mb0Bz
bXN0aWNrZXQuY3owggEgMAsGCSqGSIb3DQEBAQOCAQ8AMIIBCgKCAQEAsDh05CAX
Wp29GTbjk3gzeCCe/1t7V3aNTwbtzkUtLZnbS9tge/+Iaqsz10IOWk3SndLhPIfa
KUvX/pnkq5CXIVyTTyRoFpyYrDfNoRmZ/3uTmMG50urk0Rg/+e4f2k32BfFTfB0W
3V169+QQ6Xvvuoyh62cppfi1msgFJ6WGmEF1r73Q6tK1vxfuA9wJfMWTl4Sg8nEf
9NXsTc9VAwGKRJbmTUN1b0xsqFvlFbxvaxPGwxNM29lXWlez5KEsh0sfUyTGQuTB
tu5JMC57TGvL0/TwgwrtOxQL5+N4lJAWnUQ+z3XXL694eSsuKlgw2yasO2ZwWnyz
eap2vnN/CifUgwIDAQABoyMwITAfBgNVHSMEGDAWgBSQplC3hNS56l/yBYQTeEXo
qXVUXDALBgkqhkiG9w0BAQUDggIBAHMNLagyKZYla3gR0KOhxiUWuFG2gU7uB2v+
zeqmIh6XxG4/39r6SJgUIimZ2aVQjYLa/fgrn5FRXhDqMumLJ3rWp8GB05evmdWl
WMQrb6E39jsFXuCzev6mCjiHxzGC2I7SRvFmnCj5fvOF81V5dLjU2PnCNqPym9Aw
XbEHVXTxpM9okSeq/EoeuTA5NHl/EySwYiGoexz0Ia51M5cw5W5go2Abmtqs4bbz
7OFeZKP9fd1p+C/ZnekgKq+3SJ9qbEiJxoPir3rG2N0mw7iI5pwvbCixY9irZh5o
Lrc5RvH4hdpygNSm4MYEuBykEW0tizkcVanGCUmGdjxM22Y9XdPgKitS04rVk/2U
C1Gszv9KvtmQ2P3/HWWWiOQgljc3SFqBltt6TqJTGCtLEbWRw6V+sw3SALoafvLg
tIsyWUsjM5LunRkUQ+HIsmKo42943TmgUvgRuuo0nsEFI5TS7Jh0iC/2gQEt7XGh
wzOTZ0HzM3oNnTphlXFLBwL9MUgWKbhu5Fg486dDMeQmZmhztW/+F/uHHYFisk+1
tmr2prSh5i4fD71t4p+EGJJQxM4wCiXRLzggIVGUAIrzynxO2vjYiMQxAUH3tdsX
JI6fq+e/mFZOE2XQmYu3/hQEw8/2F6usF1lyvwMZt2TgQZF1/g8gFVQUY2mGLM1z
Wry5FNNo
-----END CERTIFICATE-----
--START INTERMEDIATE CERT--
-----BEGIN CERTIFICATE-----
MIIFEjCCA/qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgawxCzAJBgNVBAYTAlVT
MQswCQYDVQQIDAJOWTESMBAGA1UEBwwJQ2FuYXN0b3RhMRswGQYDVQQKDBJRWiBJ
bmR1c3RyaWVzLCBMTEMxGzAZBgNVBAsMElFaIEluZHVzdHJpZXMsIExMQzEZMBcG
A1UEAwwQcXppbmR1c3RyaWVzLmNvbTEnMCUGCSqGSIb3DQEJARYYc3VwcG9ydEBx
emluZHVzdHJpZXMuY29tMB4XDTE1MDMwMjAwNTAxOFoXDTM1MDMwMjAwNTAxOFow
gZgxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTEbMBkGA1UECgwSUVogSW5kdXN0
cmllcywgTExDMRswGQYDVQQLDBJRWiBJbmR1c3RyaWVzLCBMTEMxGTAXBgNVBAMM
EHF6aW5kdXN0cmllcy5jb20xJzAlBgkqhkiG9w0BCQEWGHN1cHBvcnRAcXppbmR1
c3RyaWVzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTDgNLU
iohl/rQoZ2bTMHVEk1mA020LYhgfWjO0+GsLlbg5SvWVFWkv4ZgffuVRXLHrwz1H
YpMyo+Zh8ksJF9ssJWCwQGO5ciM6dmoryyB0VZHGY1blewdMuxieXP7Kr6XD3GRM
GAhEwTxjUzI3ksuRunX4IcnRXKYkg5pjs4nLEhXtIZWDLiXPUsyUAEq1U1qdL1AH
EtdK/L3zLATnhPB6ZiM+HzNG4aAPynSA38fpeeZ4R0tINMpFThwNgGUsxYKsP9kh
0gxGl8YHL6ZzC7BC8FXIB/0Wteng0+XLAVto56Pyxt7BdxtNVuVNNXgkCi9tMqVX
xOk3oIvODDt0UoQUZ/umUuoMuOLekYUpZVk4utCqXXlB4mVfS5/zWB6nVxFX8Io1
9FOiDLTwZVtBmzmeikzb6o1QLp9F2TAvlf8+DIGDOo0DpPQUtOUyLPCh5hBaDGFE
ZhE56qPCBiQIc4T2klWX/80C5NZnd/tJNxjyUyk7bjdDzhzT10CGRAsqxAnsjvMD
2KcMf3oXN4PNgyfpbfq2ipxJ1u777Gpbzyf0xoKwH9FYigmqfRH2N2pEdiYawKrX
6pyXzGM4cvQ5X1Yxf2x/+xdTLdVaLnZgwrdqwFYmDejGAldXlYDl3jbBHVM1v+uY
5ItGTjk+3vLrxmvGy5XFVG+8fF/xaVfo5TW5AgMBAAGjUDBOMB0GA1UdDgQWBBSQ
plC3hNS56l/yBYQTeEXoqXVUXDAfBgNVHSMEGDAWgBQDRcZNwPqOqQvagw9BpW0S
BkOpXjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAJIO8SiNr9jpLQ
eUsFUmbueoxyI5L+P5eV92ceVOJ2tAlBA13vzF1NWlpSlrMmQcVUE/K4D01qtr0k
gDs6LUHvj2XXLpyEogitbBgipkQpwCTJVfC9bWYBwEotC7Y8mVjjEV7uXAT71GKT
x8XlB9maf+BTZGgyoulA5pTYJ++7s/xX9gzSWCa+eXGcjguBtYYXaAjjAqFGRAvu
pz1yrDWcA6H94HeErJKUXBakS0Jm/V33JDuVXY+aZ8EQi2kV82aZbNdXll/R6iGw
2ur4rDErnHsiphBgZB71C5FD4cdfSONTsYxmPmyUb5T+KLUouxZ9B0Wh28ucc1Lp
rbO7BnjW
-----END CERTIFICATE-----
ghost commented 8 years ago

Most other places we convert CRYPTAPI_BLOB’s (PKCS, desktop X509), we make a special case check if cbData==0 and return an empty array without dereferencing pbData. (Windows likely sets this NULL for empty blob.)

Looks like this one should do the same.

From: Jeremy Barton [mailto:notifications@github.com] Sent: Saturday, July 2, 2016 3:39 PM To: dotnet/corefx corefx@noreply.github.com Cc: Atsushi Kanamori atsushik@microsoft.com; Mention mention@noreply.github.com Subject: Re: [dotnet/corefx] ArgumentNullException when calling RSACertificateExtensions.GetRSAPrivateKey (#9792)

Hm. That Public Key block looks normal to me. I don't suppose you have the full certificate? (-----BEGIN CERTIFICATE-----).

@AtsushiKanhttps://github.com/AtsushiKan can you take a look at this one? I'm sure we can guard it with a defensive nullcheck somewhere; but I'd sort of like to understand what's happening. (Of course, it's also non-intuitive that we're calling get_PublicKey in GetPrivateKey, but I'm sure we have a reason)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://github.com/dotnet/corefx/issues/9792#issuecomment-230125403, or mute the threadhttps://github.com/notifications/unsubscribe/AMnGmisEF1SpWsRgYnidtsrdAnWKcVTkks5qRuiggaJpZM4JDEi4.

beho commented 8 years ago

Is there some workaround I could use until this is fixed ?

bartonjs commented 8 years ago

@beho Sorry for the delay. I have this working locally now with your provided certificate. The CA, when encoding the certificate, has omitted the Algorithm.Parameters values (every other cert I've seen encodes them as NULL (05 00) instead of omitting them).

Is it okay to use the certificate you provided here as a test case, or would you prefer that I generate a new one?

beho commented 8 years ago

It does not contain private key so I have no problem with it being used for tests.