Skip to content

Failure parsing private key with PEM format generated by OpenSSH 9.9p1 (macOS 15.4) #1629

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
emaf opened this issue Apr 9, 2025 · 3 comments

Comments

@emaf
Copy link

emaf commented Apr 9, 2025

The OpenSSH version that macOS 15.4 ships (9.9p1) generates PEM private keys including some extra metadata that makes SSH.NET fail to parse it.

Steps to reproduce it:

  1. Generate a private key on macOS 15.4 using the following command:
ssh-keygen -t ecdsa -b 521 -m PEM -f ./pkey.pem -q
  1. Add the following code to a .NET app:
var pkey = new PrivateKeyFile("<path to pkey.pem>");
  1. Run the app and you will get the following exception:
Exception has occurred: CLR/System.Formats.Asn1.AsnContentException
An unhandled exception of type 'System.Formats.Asn1.AsnContentException' occurred in System.Formats.Asn1.dll: 'The provided data is tagged with 'Universal' class value '16', but it should have been 'Universal' class value '6'.'
   at System.Formats.Asn1.AsnDecoder.CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber)
   at System.Formats.Asn1.AsnDecoder.GetPrimitiveContentSpan(ReadOnlySpan`1 source, AsnEncodingRules ruleSet, Asn1Tag expectedTag, UniversalTagNumber tagNumber, Int32& bytesConsumed)
   at System.Formats.Asn1.AsnDecoder.ReadObjectIdentifier(ReadOnlySpan`1 source, AsnEncodingRules ruleSet, Int32& bytesConsumed, Nullable`1 expectedTag)
   at System.Formats.Asn1.AsnReader.ReadObjectIdentifier(Nullable`1 expectedTag)
   at Renci.SshNet.Security.EcdsaKey..ctor(Byte[] data)
   at Renci.SshNet.PrivateKeyFile.Open(Stream privateKey, String passPhrase)
   at Renci.SshNet.PrivateKeyFile..ctor(String fileName, String passPhrase, String certificateFileName)
   at Renci.SshNet.PrivateKeyFile..ctor(String fileName)
   at Program.<Main>$(String[] args) in 

If you compare the result of running the same ssh-keygen command in macOS 15.4 (OpenSSH 9.9p1) and macOS 14.6.1 (OpenSHH 9.7p1), you'll see the newer version generates a lot of extra content that seems to be the culprit of the parsing content exception.

@Rob-Hague
Copy link
Collaborator

I'm not able to reproduce it with 9.9p2 in WSL. I get a key like:

-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIAPrBErAKobrDch+bduyeZ+bQfOjhKNPuwUsZlLCg1wpRsbDXSIS0x
QQe4wldV7bAOTh7GqAPgN9W5SswNmRK86J+gBwYFK4EEACOhgYkDgYYABADJL9qw
3O5ASozmvJwTG1yL2t/9U9na52ssbZvNirmCxbsZhAQ/C8XuzWWOveBvFtQbXtJ7
2fXWn0FiCB1hwmj8awGZLQJKvImjOr64c/Pn9jS3H/lQJW3iewUgqeVIziOMC3G1
y5tS8eZ9JsWDJs5DO8eO/Zkrqznj8wxPFQ2MVy+bGA==
-----END EC PRIVATE KEY-----

$ openssl asn1parse -in pkey.pem -i
    0:d=0  hl=3 l= 220 cons: SEQUENCE
    3:d=1  hl=2 l=   1 prim:  INTEGER           :01
    6:d=1  hl=2 l=  66 prim:  OCTET STRING      [HEX DUMP]:003EB044AC02A86EB0DC87E6DDBB2799F9B41F3A384A34FBB052C6652C2835C2946C6C35D2212D314107B8C25755EDB00E4E1EC6A803E037D5B94ACC0D9912BCE89F
   74:d=1  hl=2 l=   7 cons:  cont [ 0 ]
   76:d=2  hl=2 l=   5 prim:   OBJECT            :secp521r1
   83:d=1  hl=3 l= 137 cons:  cont [ 1 ]
   86:d=2  hl=3 l= 134 prim:   BIT STRING

Can you paste a key here which doesn't work?

@emaf
Copy link
Author

emaf commented Apr 21, 2025

@Rob-Hague this is what I get with 9.9p1 in macOS 15.4.1

-----BEGIN EC PRIVATE KEY-----
MIICnQIBAQRCAVNb14beg8V2WP3f0ojF7b4wQ8o/6zoWe48NOExO6P0OhXk5VzTw
XoWRXgcFr0T3LfWelaXcKOJ6CGhdrMB2gKJ3oIIBxjCCAcICAQEwTQYHKoZIzj0B
AQJCAf//////////////////////////////////////////////////////////
////////////////////////////MIGeBEIB////////////////////////////
//////////////////////////////////////////////////////////wEQVGV
PrlhjhyaH5KaIaC2hUDuotpyW5mzFfO4tImRjvEJ4VYZOVHsfpN7FlLAvTuxvwc1
c9+IPSw08e9FH9RrUD8AAxUA0J6IACkcuFOWzGcXOTKEqqDaZLoEgYUEAMaFjga3
BATpzZ4+y2YjlbRCnGSBOQU/tSH4KK9ga009uqFLXnfv51ko/h3BJ6L/qN4zSLPB
hWpCm/l+fjHC5b1mARg5KWp4mjvABFyKX7QsfRvZmPVESVebRGgXr70XJz5mLJfu
cple9CZAxVC5AT+tB2E1PHCGonLCQIi+lHaf0WZQAkIB////////////////////
///////////////////////6UYaHg78vlmt/zAFI9wml0Du1ybiJnEeuu2+3HpE4
ZAkCAQGhgYkDgYYABADdJGVfsxtBKv4PHOQkfbKTZ0AFiWVeuUUJekfq9vgDPxAb
T2W7/z9INXUt0pgEqyAGabX5NLuTZ7Nyzx3SRTxbGAGQ+z6ErrcN0azmOvuGfObl
y9QbFh1LDYmZegwyANj/ciupbd4pdUPR1CAAOJyaBsTDU+3mjpR40lG6ktKOitQ8
Ig==
-----END EC PRIVATE KEY-----

openssl asn1parse -in pkey2.pem -i                 
    0:d=0  hl=4 l= 669 cons: SEQUENCE          
    4:d=1  hl=2 l=   1 prim:  INTEGER           :01
    7:d=1  hl=2 l=  66 prim:  OCTET STRING      [HEX DUMP]:01535BD786DE83C57658FDDFD288C5EDBE3043CA3FEB3A167B8F0D384C4EE8FD0E8579395734F05E85915E0705AF44F72DF59E95A5DC28E27A08685DACC07680A277
   75:d=1  hl=4 l= 454 cons:  cont [ 0 ]        
   79:d=2  hl=4 l= 450 cons:   SEQUENCE          
   83:d=3  hl=2 l=   1 prim:    INTEGER           :01
   86:d=3  hl=2 l=  77 cons:    SEQUENCE          
   88:d=4  hl=2 l=   7 prim:     OBJECT            :prime-field
   97:d=4  hl=2 l=  66 prim:     INTEGER           :01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
  165:d=3  hl=3 l= 158 cons:    SEQUENCE          
  168:d=4  hl=2 l=  66 prim:     OCTET STRING      [HEX DUMP]:01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC
  236:d=4  hl=2 l=  65 prim:     OCTET STRING      [HEX DUMP]:51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00
  303:d=4  hl=2 l=  21 prim:     BIT STRING        
  326:d=3  hl=3 l= 133 prim:    OCTET STRING      [HEX DUMP]:0400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650
  462:d=3  hl=2 l=  66 prim:    INTEGER           :01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409
  530:d=3  hl=2 l=   1 prim:    INTEGER           :01
  533:d=1  hl=3 l= 137 cons:  cont [ 1 ]        
  536:d=2  hl=3 l= 134 prim:   BIT STRING     

@Rob-Hague
Copy link
Collaborator

The key is using explicit parameters rather than a named curve, despite actually appearing to be secp521r1 by comparison of the parameters. The trouble is, SSH uses named algorithms e.g. ecdsa-sha2-nistp521 which would necessitate inspecting the parameters to figure out if it is a known curve. It would not be that hard since we only support nistp256, nistp384, nistp521 atm (per https://datatracker.ietf.org/doc/html/rfc5656#section-10.1).

At a glance, openssh/openssh-portable@7bdfc20 appears to be related. It adds some logic to do that mapping to named curves.

The parameter structure is defined on pages 102 & 103 of https://www.secg.org/sec1-v2.pdf. The .NET implementation is at

https://github.com/dotnet/runtime/blob/1a7343c82afe5d8f527d51b7946900640b7d6b03/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECPrivateKey.xml.cs#L87
https://github.com/dotnet/runtime/blob/1a7343c82afe5d8f527d51b7946900640b7d6b03/src/libraries/Common/src/System/Security/Cryptography/Asn1/ECDomainParameters.xml.cs#L99
https://github.com/dotnet/runtime/blob/1a7343c82afe5d8f527d51b7946900640b7d6b03/src/libraries/Common/src/System/Security/Cryptography/Asn1/SpecifiedECDomain.xml.cs#L96

(we could use ECDsa.ImportECPrivateKey but it is not available on net462 and netstandard2.0. maybe BouncyCastle can help - but this still does not solve the mapping to named curves)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy