diff --git a/README.md b/README.md index b3cc4ec1d..8d2b99d25 100644 --- a/README.md +++ b/README.md @@ -96,12 +96,24 @@ The main types provided by this library are: ## Public Key Authentication **SSH.NET** supports the following private key formats: -* RSA in OpenSSL PEM ("BEGIN RSA PRIVATE KEY") and ssh.com ("BEGIN SSH2 ENCRYPTED PRIVATE KEY") format -* DSA in OpenSSL PEM ("BEGIN DSA PRIVATE KEY") and ssh.com ("BEGIN SSH2 ENCRYPTED PRIVATE KEY") format -* ECDSA 256/384/521 in OpenSSL PEM format ("BEGIN EC PRIVATE KEY") -* ECDSA 256/384/521, ED25519 and RSA in OpenSSH key format ("BEGIN OPENSSH PRIVATE KEY") - -Private keys in OpenSSL PEM and ssh.com format can be encrypted using one of the following cipher methods: +* RSA in + * OpenSSL traditional PEM format ("BEGIN RSA PRIVATE KEY") + * OpenSSL PKCS#8 PEM format ("BEGIN PRIVATE KEY", "BEGIN ENCRYPTED PRIVATE KEY") + * ssh.com format ("BEGIN SSH2 ENCRYPTED PRIVATE KEY") + * OpenSSH key format ("BEGIN OPENSSH PRIVATE KEY") +* DSA in + * OpenSSL traditional PEM format ("BEGIN DSA PRIVATE KEY") + * OpenSSL PKCS#8 PEM format ("BEGIN PRIVATE KEY", "BEGIN ENCRYPTED PRIVATE KEY") + * ssh.com format ("BEGIN SSH2 ENCRYPTED PRIVATE KEY") +* ECDSA 256/384/521 in + * OpenSSL traditional PEM format ("BEGIN EC PRIVATE KEY") + * OpenSSL PKCS#8 PEM format ("BEGIN PRIVATE KEY", "BEGIN ENCRYPTED PRIVATE KEY") + * OpenSSH key format ("BEGIN OPENSSH PRIVATE KEY") +* ED25519 in + * OpenSSL PKCS#8 PEM format ("BEGIN PRIVATE KEY", "BEGIN ENCRYPTED PRIVATE KEY") + * OpenSSH key format ("BEGIN OPENSSH PRIVATE KEY") + +Private keys in OpenSSL traditional PEM format can be encrypted using one of the following cipher methods: * DES-EDE3-CBC * DES-EDE3-CFB * DES-CBC @@ -109,6 +121,11 @@ Private keys in OpenSSL PEM and ssh.com format can be encrypted using one of the * AES-192-CBC * AES-256-CBC +Private keys in OpenSSL PKCS#8 PEM format can be encrypted using any cipher method BouncyCastle supports. + +Private keys in ssh.com format can be encrypted using one of the following cipher methods: +* 3des-cbc + Private keys in OpenSSH key format can be encrypted using one of the following cipher methods: * 3des-cbc * aes128-cbc diff --git a/src/Renci.SshNet/PrivateKeyConnectionInfo.cs b/src/Renci.SshNet/PrivateKeyConnectionInfo.cs index 86d50a2fd..e1de72d02 100644 --- a/src/Renci.SshNet/PrivateKeyConnectionInfo.cs +++ b/src/Renci.SshNet/PrivateKeyConnectionInfo.cs @@ -22,7 +22,7 @@ public class PrivateKeyConnectionInfo : ConnectionInfo, IDisposable /// Connection host. /// Connection username. /// Connection key files. - public PrivateKeyConnectionInfo(string host, string username, params PrivateKeyFile[] keyFiles) + public PrivateKeyConnectionInfo(string host, string username, params IPrivateKeySource[] keyFiles) : this(host, DefaultPort, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, keyFiles) { } diff --git a/src/Renci.SshNet/PrivateKeyFile.cs b/src/Renci.SshNet/PrivateKeyFile.cs index cfa24d32e..341a429d5 100644 --- a/src/Renci.SshNet/PrivateKeyFile.cs +++ b/src/Renci.SshNet/PrivateKeyFile.cs @@ -1,8 +1,9 @@ -#nullable enable +#nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; using System.Globalization; using System.IO; using System.Numerics; @@ -10,6 +11,11 @@ using System.Text; using System.Text.RegularExpressions; +using Org.BouncyCastle.Asn1.EdEC; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Pkcs; + using Renci.SshNet.Common; using Renci.SshNet.Security; using Renci.SshNet.Security.Cryptography; @@ -36,12 +42,12 @@ namespace Renci.SshNet /// ECDSA 256/384/521 in OpenSSL PEM and OpenSSH key format /// /// - /// ED25519 in OpenSSH key format + /// ED25519 in OpenSSL PEM and OpenSSH key format /// /// /// /// - /// The following encryption algorithms are supported for OpenSSL PEM and ssh.com format: + /// The following encryption algorithms are supported for OpenSSL traditional PEM: /// /// /// DES-EDE3-CBC @@ -62,6 +68,19 @@ namespace Renci.SshNet /// AES-256-CBC /// /// + /// + /// + /// Private keys in OpenSSL PKCS#8 PEM format can be encrypted using any cipher method BouncyCastle supports. + /// + /// + /// The following encryption algorithms are supported for ssh.com format: + /// + /// + /// 3des-cbc + /// + /// + /// + /// /// The following encryption algorithms are supported for OpenSSH format: /// /// @@ -99,7 +118,7 @@ namespace Renci.SshNet /// public partial class PrivateKeyFile : IPrivateKeySource, IDisposable { - private const string PrivateKeyPattern = @"^-+ *BEGIN (?\w+( \w+)*) PRIVATE KEY *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)(\r?\n)?-+ *END \k PRIVATE KEY *-+"; + private const string PrivateKeyPattern = @"^-+ *BEGIN (?\w+( \w+)*) *-+\r?\n((Proc-Type: 4,ENCRYPTED\r?\nDEK-Info: (?[A-Z0-9-]+),(?[A-F0-9]+)\r?\n\r?\n)|(Comment: ""?[^\r\n]*""?\r?\n))?(?([a-zA-Z0-9/+=]{1,80}\r?\n)+)(\r?\n)?-+ *END \k *-+"; #if NET7_0_OR_GREATER private static readonly Regex PrivateKeyRegex = GetPrivateKeyRegex(); @@ -233,6 +252,11 @@ private void Open(Stream privateKey, string? passPhrase) } var keyName = privateKeyMatch.Result("${keyName}"); + if (!keyName.EndsWith("PRIVATE KEY", StringComparison.Ordinal)) + { + throw new SshException("Invalid private key file."); + } + var cipherName = privateKeyMatch.Result("${cipherName}"); var salt = privateKeyMatch.Result("${salt}"); var data = privateKeyMatch.Result("${data}"); @@ -288,7 +312,7 @@ private void Open(Stream privateKey, string? passPhrase) switch (keyName) { - case "RSA": + case "RSA PRIVATE KEY": var rsaKey = new RsaKey(decryptedData); _key = rsaKey; _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key)); @@ -297,16 +321,17 @@ private void Open(Stream privateKey, string? passPhrase) _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(rsaKey, HashAlgorithmName.SHA256))); #pragma warning restore CA2000 // Dispose objects before losing scope break; - case "DSA": + case "DSA PRIVATE KEY": _key = new DsaKey(decryptedData); _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-dss", _key)); break; - case "EC": + case "EC PRIVATE KEY": _key = new EcdsaKey(decryptedData); _hostAlgorithms.Add(new KeyHostAlgorithm(_key.ToString(), _key)); break; - case "OPENSSH": - _key = ParseOpenSshV1Key(decryptedData, passPhrase); + case "PRIVATE KEY": + var privateKeyInfo = PrivateKeyInfo.GetInstance(binaryData); + _key = ParseOpenSslPkcs8PrivateKey(privateKeyInfo); if (_key is RsaKey parsedRsaKey) { _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key)); @@ -315,13 +340,55 @@ private void Open(Stream privateKey, string? passPhrase) _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(parsedRsaKey, HashAlgorithmName.SHA256))); #pragma warning restore CA2000 // Dispose objects before losing scope } + else if (_key is DsaKey parsedDsaKey) + { + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-dss", _key)); + } else { _hostAlgorithms.Add(new KeyHostAlgorithm(_key.ToString(), _key)); } break; - case "SSH2 ENCRYPTED": + case "ENCRYPTED PRIVATE KEY": + var encryptedPrivateKeyInfo = EncryptedPrivateKeyInfo.GetInstance(binaryData); + privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(passPhrase?.ToCharArray(), encryptedPrivateKeyInfo); + _key = ParseOpenSslPkcs8PrivateKey(privateKeyInfo); + if (_key is RsaKey parsedRsaKey2) + { + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key)); +#pragma warning disable CA2000 // Dispose objects before losing scope + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(parsedRsaKey2, HashAlgorithmName.SHA512))); + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(parsedRsaKey2, HashAlgorithmName.SHA256))); +#pragma warning restore CA2000 // Dispose objects before losing scope + } + else if (_key is DsaKey parsedDsaKey) + { + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-dss", _key)); + } + else + { + _hostAlgorithms.Add(new KeyHostAlgorithm(_key.ToString(), _key)); + } + + break; + case "OPENSSH PRIVATE KEY": + _key = ParseOpenSshV1Key(decryptedData, passPhrase); + if (_key is RsaKey parsedRsaKey3) + { + _hostAlgorithms.Add(new KeyHostAlgorithm("ssh-rsa", _key)); +#pragma warning disable CA2000 // Dispose objects before losing scope + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-512", _key, new RsaDigitalSignature(parsedRsaKey3, HashAlgorithmName.SHA512))); + _hostAlgorithms.Add(new KeyHostAlgorithm("rsa-sha2-256", _key, new RsaDigitalSignature(parsedRsaKey3, HashAlgorithmName.SHA256))); +#pragma warning restore CA2000 // Dispose objects before losing scope + } + else + { + _hostAlgorithms.Add(new KeyHostAlgorithm(_key.ToString(), _key)); + } + + break; + case "SSH2 ENCRYPTED PRIVATE KEY": var reader = new SshDataReader(decryptedData); var magicNumber = reader.ReadUInt32(); if (magicNumber != 0x3f6ff9eb) @@ -488,8 +555,8 @@ private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, strin } /// - /// Parses an OpenSSH V1 key file (i.e. ED25519 key) according to the the key spec: - /// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key. + /// Parses an OpenSSH V1 key file according to the key spec: + /// . /// /// The key file data (i.e. base64 encoded data between the header/footer). /// Passphrase or if there isn't one. @@ -712,6 +779,81 @@ private static Key ParseOpenSshV1Key(byte[] keyFileData, string? passPhrase) return parsedKey; } + /// + /// Parses an OpenSSL PKCS#8 key file according to RFC5208: + /// . + /// + /// The . + /// + /// The . + /// + /// Algorithm not supported. + private static Key ParseOpenSslPkcs8PrivateKey(PrivateKeyInfo privateKeyInfo) + { + var algorithmOid = privateKeyInfo.PrivateKeyAlgorithm.Algorithm; + var key = privateKeyInfo.PrivateKey.GetOctets(); + if (algorithmOid.Equals(PkcsObjectIdentifiers.RsaEncryption)) + { + return new RsaKey(key); + } + + if (algorithmOid.Equals(X9ObjectIdentifiers.IdDsa)) + { + var parameters = privateKeyInfo.PrivateKeyAlgorithm.Parameters.GetDerEncoded(); + var parametersReader = new AsnReader(parameters, AsnEncodingRules.BER); + var sequenceReader = parametersReader.ReadSequence(); + parametersReader.ThrowIfNotEmpty(); + + var p = sequenceReader.ReadInteger(); + var q = sequenceReader.ReadInteger(); + var g = sequenceReader.ReadInteger(); + sequenceReader.ThrowIfNotEmpty(); + + var keyReader = new AsnReader(key, AsnEncodingRules.BER); + var x = keyReader.ReadInteger(); + keyReader.ThrowIfNotEmpty(); + + var y = BigInteger.ModPow(g, x, p); + + return new DsaKey(p, q, g, y, x); + } + + if (algorithmOid.Equals(X9ObjectIdentifiers.IdECPublicKey)) + { + var parameters = privateKeyInfo.PrivateKeyAlgorithm.Parameters.GetDerEncoded(); + var parametersReader = new AsnReader(parameters, AsnEncodingRules.DER); + var curve = parametersReader.ReadObjectIdentifier(); + parametersReader.ThrowIfNotEmpty(); + + var privateKeyReader = new AsnReader(key, AsnEncodingRules.DER); + var sequenceReader = privateKeyReader.ReadSequence(); + privateKeyReader.ThrowIfNotEmpty(); + + var version = sequenceReader.ReadInteger(); + if (version != BigInteger.One) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "EC version '{0}' is not supported.", version)); + } + + var privatekey = sequenceReader.ReadOctetString(); + + var publicKeyReader = sequenceReader.ReadSequence(new Asn1Tag(TagClass.ContextSpecific, 1, isConstructed: true)); + var publickey = publicKeyReader.ReadBitString(out _); + publicKeyReader.ThrowIfNotEmpty(); + + sequenceReader.ThrowIfNotEmpty(); + + return new EcdsaKey(curve, publickey, privatekey.TrimLeadingZeros()); + } + + if (algorithmOid.Equals(EdECObjectIdentifiers.id_Ed25519)) + { + return new ED25519Key(key); + } + + throw new SshException(string.Format(CultureInfo.InvariantCulture, "Private key algorithm \"{0}\" is not supported.", algorithmOid)); + } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// diff --git a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs index ab37598ef..c970a323e 100644 --- a/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs +++ b/src/Renci.SshNet/Security/Cryptography/EcdsaKey.cs @@ -204,7 +204,7 @@ public EcdsaKey(SshKeyData publicKeyData) /// /// Initializes a new instance of the class. /// - /// The curve name. + /// The curve name or oid. /// Value of publickey. /// Value of privatekey. public EcdsaKey(string curve, byte[] publickey, byte[] privatekey) @@ -266,24 +266,27 @@ private static Impl Import(string curve_oid, byte[] publickey, byte[]? privateke #endif } - private static string GetCurveOid(string curve_s) + private static string GetCurveOid(string curve) { - if (string.Equals(curve_s, "nistp256", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(curve, "nistp256", StringComparison.OrdinalIgnoreCase) || + string.Equals(curve, ECDSA_P256_OID_VALUE)) { return ECDSA_P256_OID_VALUE; } - if (string.Equals(curve_s, "nistp384", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(curve, "nistp384", StringComparison.OrdinalIgnoreCase) || + string.Equals(curve, ECDSA_P384_OID_VALUE)) { return ECDSA_P384_OID_VALUE; } - if (string.Equals(curve_s, "nistp521", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(curve, "nistp521", StringComparison.OrdinalIgnoreCase) || + string.Equals(curve, ECDSA_P521_OID_VALUE)) { return ECDSA_P521_OID_VALUE; } - throw new SshException("Unexpected Curve Name: " + curve_s); + throw new SshException("Unexpected Curve: " + curve); } /// diff --git a/test/Data/Key.DSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt b/test/Data/Key.DSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt new file mode 100644 index 000000000..03773bbfb --- /dev/null +++ b/test/Data/Key.DSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt @@ -0,0 +1,12 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIjn9BgD9X0loCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBB3Zthr23nQDulzKryFEUTFBIIB +UDW8/IR0K5DRScH4Cl7HOoK20aR+TmUOGczE027RL++iosgk5rYUpIKn0pxIKM0U +StFGTqLz3G+bEh/Bm2Vt03Qv0Q2QZoX2e1Vktt32X2cLBNzGWfEpLuCD4vG8QDRW +uGkE1NHxJKQTJWQt/gwGituyhMThGoE3ZcuqeLmRlhUSgRccO6WJ2HkNOW7TM5RB +QbeBXmYB1H5S3FjpRAvd2p9dEzDsyquQaltFM4kekIxGjwiw5WSd+KsCGXFLa2Y2 +OXvcjRIIqGBJr+xvEVA86TNTfad+sKGqGUFszRmnGXA+VxEZju2OCpVhxTLEMX4Q +2vYz9i8jE78tpx7C6PTKoJe5FTdlTatvWvYD5cvcbazPUjuZbraI9ha4XvNtERGC +J0voz/7yeuNkW1ofxTUOu+snGhySC4AXkC44eZG4wUPfuQAswP8dFiQi2BthgVyP +kA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test/Data/Key.DSA.PKCS8.txt b/test/Data/Key.DSA.PKCS8.txt new file mode 100644 index 000000000..f5baf96e5 --- /dev/null +++ b/test/Data/Key.DSA.PKCS8.txt @@ -0,0 +1,9 @@ +-----BEGIN PRIVATE KEY----- +MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBALVl3fae2O4qwsAK95SUShX0KMUN +P+yl/uT3lGH9T/ZptnHSlrTxnTWXCl0g91KEeCaEnDDhLxm4aCv1Ag4B/yvcM4u3 +4qkmaNLy2LiAxiqdobZcNG61Pqwqd5IDkp38LBsn8tmb12xu9NalpUfOiSEB1cyC +r4zFZMrm0wtdyJQVAhUArvojZKn/2DgGI2Kx0ghxZlgHxGECgYAOVJ434UAR3Hn6 +lA5nWNfFOuUVH3W7nJaP0FQJiIPx7GUbdxO9qtDNTbWkWL3c9qx5+B7Ole4xM7cv +yXPrNQUYDHCFlS+Ue2x3IeJrkdfZkH9ePP25y5A0J4/c+8XXvQaj4zA5nfw13oy5 +Ptyd7d3Kq5tEDM8KiVdIhwkXjUA3PQQWAhQYRjs5PgIpnqG/euBPPh7EDZcnXg== +-----END PRIVATE KEY----- diff --git a/test/Data/Key.ECDSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt b/test/Data/Key.ECDSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt new file mode 100644 index 000000000..03fe0acbd --- /dev/null +++ b/test/Data/Key.ECDSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt @@ -0,0 +1,7 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAithDR1n5wCYQICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEMSzAQ3FSZ5hQHbmb7K9aB0EgZCS +RVfVmMp1SBllrUMvdMEz5Zwvthaa1/M3Mc6MEVEzgROEXY3X+ywECU9q18aIOct+ +m5bmFcRcwoxo/hj6fsnmeH567KRfnN4Al219azq5ccwTr68y8tasYsZHOFCkn3ve +Hkzu0+gylHZGWqo5TWif9O9DrII/KszsoX86jJxhORwqCnxMmKQQ/gGvexpAYJA= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test/Data/Key.ECDSA.PKCS8.txt b/test/Data/Key.ECDSA.PKCS8.txt new file mode 100644 index 000000000..49b96064f --- /dev/null +++ b/test/Data/Key.ECDSA.PKCS8.txt @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgR2poUqAkEiJtWPJS +HW/tjfkvAhAmuhx1NpgUvCXuIHShRANCAARAPkw7+f3KpINOzPDNWkCkvHlJAV5w +Tll8OSDGpV0dB5ybUEA+jNnh4oY2EqfvaFPv2YuWn0ddf6g0Ry5VPzcf +-----END PRIVATE KEY----- diff --git a/test/Data/Key.OPENSSH.ED25519.PKCS8.Encrypted.Aes.256.CBC.12345.txt b/test/Data/Key.OPENSSH.ED25519.PKCS8.Encrypted.Aes.256.CBC.12345.txt new file mode 100644 index 000000000..63aebfa2c --- /dev/null +++ b/test/Data/Key.OPENSSH.ED25519.PKCS8.Encrypted.Aes.256.CBC.12345.txt @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGbMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhiAnoQd2VMZwICCAAw +DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEENed/l3AFuDblxDnswMZXDcEQIpo +fCVdEbDerN0Rrh9i+Ymu+qpEqGlc6jycwR3rPtyL9jy0k5kauBxRn3Z5uCSlGzJL +JXxlMR+DWG6QDJdxrHI= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test/Data/Key.OPENSSH.ED25519.PKCS8.txt b/test/Data/Key.OPENSSH.ED25519.PKCS8.txt new file mode 100644 index 000000000..28bf6cafa --- /dev/null +++ b/test/Data/Key.OPENSSH.ED25519.PKCS8.txt @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIADMEXUw9TGuz7JykmHbzPOj8XebpZwo76iuxJtHkvAp +-----END PRIVATE KEY----- diff --git a/test/Data/Key.RSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt b/test/Data/Key.RSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt new file mode 100644 index 000000000..b7bdf6bd6 --- /dev/null +++ b/test/Data/Key.RSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIr0PK2BHcNKsCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBCClZC7H7nFtlsAd+cURqvqBIIE +wOYCGtgoABpWHCk5chj4EF3t2T+Eu753vz5FudgbFkkoMFVmxKLGB0BYVyxMIws/ +GLj6WYq8o51E867hr3f3XOc5nIJEffGSxF/fhYIMGuE9teOWYVTAc/vGlFzEWska +cXGgffBT+DqA4uaLEgcuYHd+hjhlNhKmNIFDsIP40sbHMP31NhuQyd1a63sTUJro +lcLEsESC1H4jru36RrrlRUrTjdgwPrraeu/GUw3Dq7Op5wTfLTsv06Ci3OAKWkGs +SMog36KgvU834YC1btAHL/T/R7lE2QFp8bHPSHA0iigsDiO6NoGcn5Ktvj0yxYzm +8T+uiKs0aEzHWRX+08J8iBIhbSuNm6z/J+CILT3ce5/1vE9Kc2Ml4iVAvwvIYvKB +TO1ZPe4/8gou/pMAHZ39/4euywZpYn9dMSPrsbLt0Wmqg+vVg9mVPD57eZiBgj0j +RqgOlrhymaIA/tDYQTt+DJOKVtGNMbPrXqVpkpaAbXVa7uXYq/euTi2OgzMOSIZj +JFLCWyZo/kpHa6/scIiRjFEDRLqIuhgKca5c4HQ23tRrtWvI+kMd5FK6IAAUplOy +rP1EOztLrwk7NmSpL/zbOJ585AgiGPkq8AAxWfEpMt5GTvcomMjQpv1bs82jLPSG +dC3N+7AOgMQQBDamwfallDgDpvlS7+pgmQJVsv9oT+XoHIXrkD7q7junA8T8Zd9a +LsBgFxMVid2X1PWRkC8+5M4zb7HZdqIyK/vI1kPVWvdUYTvRMHgXxa5wBW/kB3q9 +CQYGfOvg/Gsk7h/WqzlApqvcNZ8OJsxkhlElr2KkH5R9W3snAGedm/fHd1XWpkkL +tY7qHb9z4WW2QOZWBkFY1mfUTU0wrYexAsgLhVniHTy71zj3LLVmezPxDCIvYslX +hSY9/cofCsTx4W5toh/jh1NMsgQgaN2dGgCmHL+PwQTN3bL13KtKQUogNmHxKBMc +QZepJqoiiOtjT6LjBkQI9PY+Yzky4zToo/3Gh5i7ohYAABvT9MXtK4hbQP26JB0T +GL8pVLJSUCBQFj+87sJVILW+n1ak8OoEZQLNgs/V6lbtSc+0TW6t1ixZs7Xxh6KJ +1zLDu0IwebONt8R8KVLUPeV0aFAjr5R9KGNvaL+F0EexG+fNSe1vJ+iBf3fhQLb1 +EDI20FGYBlMfSwtNw0OhKZ5cG1lSx3BUtW1/nl3Y0+sYhRRtP628JjZ2YYifJZdc ++iNGSC2m4r6li9spPvQUyJLHb2vMIwHEqbAi+OE4fNCBVgm0vNPdhEZHAwa8HvRt +iQd2vsEE1tKSfj8z0XQl0T3u4WDVB0DvQqsSjVIfDL+vxlCZ540l26KWd1rJJOWi +fWRF4+MV07GQgoE8aL8bbLUNBJq5OJPTHSgUs7ke7D0sHas1Z2ue/LccZeMbi39U +kTqAyB0ag2/aaqL3/sUo1mi2HZBtqjg4P2gI6ymeVHeSQu8Hx62mw58C8WmoRk12 +3YfiHXZI6LHBt+CK+nIXvsf7ZXeHHVnvRJEJaeX+jrCyCKyqCz8r47ZUUb2sgqFf +8hTpheQKvCbYM+XtkkkuiudVevY67xrHpjAjDIU/4BXxC5KvlBQ8i8u21qtSq70P +fFQUbG2XVVscckIFQa8DuDc= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/test/Data/Key.RSA.PKCS8.txt b/test/Data/Key.RSA.PKCS8.txt new file mode 100644 index 000000000..723bd0f0a --- /dev/null +++ b/test/Data/Key.RSA.PKCS8.txt @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQC5O1ef4Fq1fWgm +6+Gp8lnDmNz+lwjElQ+a6gUIff5td8oEn/3iLE0RPNkFqzK9P+jNugBsIbepwk5j +F/YER5MAhd7WMsChN3UYoLAy9k7KOew833n+UKHB92cFszOllhMZ+hTKVeZ7+bOO +Mu78nSpeBHmXKT0cVP7HlgS1GXxVIeIOQspNnft3CGyqByz4+R+9gxQr4Lx6+d8T +S2BaApmTQRq2XzuctbJVcHgvOFIO0YosI8A6Ctft9h+mUPAnZYrU3qcbQWfFbUeE +N5Irt7ZNsBra9lCC8Vcxac7g781kqngI6k5F7KWJaF20oCOv/5wPjIN8+OGOMo5h +/Fu91EbhAgEjAoIBACpWtPFX2jgcqheGX3dNVliX9/+tfljRnSq5JbjMV2l6d1GD +p76rCk0VOOtaVL2K82I5JKsAZH6S0BamZB48PtuMUDD1qF9dIhRB/GNrf7kxz5ji +n4qWFlg4jJOWrLgiTYJHyjzgbzJHtAM+14oyjVdReuC4+AZ509XZJaW8rrRfIBeq +x7AOGLtJ4iZmiYrNuHfdJeNXFqTMd9rwERR4gh5H2gWVAy5h2jgaseEU7ZP6D+Cr +D+RV3XOR4TC3Tp9lUMStXCn63B7DHG/CuTgIpQLNq9+EN+Lmwnlh3OEMFGCTIGAk +j5/Xl0rKqo6IClqtX367OsPq8i42eVWipACK6AsCgYEA8GWLLfZ7zeeyEGScGfkc +4ras7drXMfG9iv83MhNe11k6Nq03J9e2TCIuX96Xm1mEm8p72ci1Ij/jDua+W9dl +9byHcxHSpvxq3iNZrzJraHAR6upomieqONv54GGDbuW1khBGTwG/oCldMBaRz50U +aEQjKyiQNBftXvYF2uLO6nUCgYEAxUEscrTkgVvzHt1qeUCYNfpuoI5sh3g9x7Y5 +440UiPWMijC8JdoTS33NThflJ03m6Oq7gOpeDNR014pwnFakU7vgwNHFPcJszPfp ++KQme/FaX/6rQdqVi4JjyCbXhVhxOFQECujdz3jUg257JsY0sgD2NztEyewW3JVO +81ilpT0CgYAUmv1NFSCN/eqw8q5LXn7RmqEbs6wLmGC0JIES67esDvZcdT897esN +1wtKC8PaHZ2m9Bk+jYveXT9ZDHa3ajvwfd+5Z+18BwHYhq/qcgk01mfvkG9dq6Dg +TV6Pk1Rol1i0v5D/dS2upHWzqinBVptZZO0SU+8aaHNuih3CTfR6fwKBgDK4/M0J +8Z2bTSUxnwk8fulPBoEOrjF2sMz0WAdQKdoTQWVc/S5OBPYnqdJAqKO08jvklp1+ +GC8vUT69MuZfbBWIFTjle9yuVn3ZWWvScEvBuCdQHWi0jNq7IPj0C4iwV6DFJZxn +xAImYogyWi6KvRfUXJHcCl/O/pB+Kj6TI0e/AoGBAK3DGiOTNRJ9XfMO3pthIsHO +BeCj9mv2VL6BKCA/vYPMg2ZbaBsDR+QzZm4mW1x9qQq/iBVf/XOijP7u37huPp2q +35uXuFjPRWdNINka6jksoOJ//iBIat1uULZAtO5fYeNYJl2y57uZHAvtxdHjOUig +T335HKF3G9znAdJ9iv8k +-----END PRIVATE KEY----- diff --git a/test/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs b/test/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs index 84b8e1d37..261455332 100644 --- a/test/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs +++ b/test/Renci.SshNet.Tests/Classes/PrivateKeyFileTest.cs @@ -303,8 +303,12 @@ public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsShar } [TestMethod] + [DataRow("Key.DSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt", "12345", typeof(DsaKey))] + [DataRow("Key.DSA.PKCS8.txt", null, typeof(DsaKey))] [DataRow("Key.DSA.txt", null, typeof(DsaKey))] [DataRow("Key.ECDSA.Encrypted.txt", "12345", typeof(EcdsaKey))] + [DataRow("Key.ECDSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt", "12345", typeof(EcdsaKey))] + [DataRow("Key.ECDSA.PKCS8.txt", null, typeof(EcdsaKey))] [DataRow("Key.ECDSA.txt", null, typeof(EcdsaKey))] [DataRow("Key.ECDSA384.Encrypted.txt", "12345", typeof(EcdsaKey))] [DataRow("Key.ECDSA384.txt", null, typeof(EcdsaKey))] @@ -326,6 +330,8 @@ public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsShar [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.256.CTR.txt", "12345", typeof(ED25519Key))] [DataRow("Key.OPENSSH.ED25519.Encrypted.ChaCha20.Poly1305.txt", "12345", typeof(ED25519Key))] [DataRow("Key.OPENSSH.ED25519.Encrypted.txt", "12345", typeof(ED25519Key))] + [DataRow("Key.OPENSSH.ED25519.PKCS8.Encrypted.Aes.256.CBC.12345.txt", "12345", typeof(ED25519Key))] + [DataRow("Key.OPENSSH.ED25519.PKCS8.txt", null, typeof(ED25519Key))] [DataRow("Key.OPENSSH.ED25519.txt", null, typeof(ED25519Key))] [DataRow("Key.OPENSSH.RSA.Encrypted.Aes.192.CTR.txt", "12345", typeof(RsaKey))] [DataRow("Key.OPENSSH.RSA.Encrypted.txt", "12345", typeof(RsaKey))] @@ -336,6 +342,8 @@ public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsShar [DataRow("Key.RSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(RsaKey))] [DataRow("Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt", "12345", typeof(RsaKey))] [DataRow("Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt", "1234567890", typeof(RsaKey))] + [DataRow("Key.RSA.PKCS8.Encrypted.Aes.256.CBC.12345.txt", "12345", typeof(RsaKey))] + [DataRow("Key.RSA.PKCS8.txt", null, typeof(RsaKey))] [DataRow("Key.RSA.txt", null, typeof(RsaKey))] [DataRow("Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(DsaKey))] [DataRow("Key.SSH2.DSA.txt", null, typeof(DsaKey))]