PrivateKeyFileTest.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using Microsoft.VisualStudio.TestTools.UnitTesting;
  5. using Renci.SshNet.Common;
  6. using Renci.SshNet.Security;
  7. using Renci.SshNet.Tests.Common;
  8. namespace Renci.SshNet.Tests.Classes
  9. {
  10. /// <summary>
  11. /// old private key information/
  12. /// </summary>
  13. [TestClass]
  14. public class PrivateKeyFileTest : TestBase
  15. {
  16. private string _temporaryFile;
  17. [TestInitialize]
  18. public void SetUp()
  19. {
  20. _temporaryFile = GetTempFileName();
  21. }
  22. [TestCleanup]
  23. public void TearDown()
  24. {
  25. if (_temporaryFile != null)
  26. {
  27. File.Delete(_temporaryFile);
  28. }
  29. }
  30. /// <summary>
  31. /// A test for <see cref="PrivateKeyFile(string)"/> ctor.
  32. ///</summary>
  33. [TestMethod]
  34. public void ConstructorWithFileNameShouldThrowArgumentNullExceptionWhenFileNameIsNull()
  35. {
  36. string fileName = null;
  37. try
  38. {
  39. _ = new PrivateKeyFile(fileName);
  40. Assert.Fail();
  41. }
  42. catch (ArgumentNullException ex)
  43. {
  44. Assert.IsNull(ex.InnerException);
  45. Assert.AreEqual("fileName", ex.ParamName);
  46. }
  47. }
  48. /// <summary>
  49. /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
  50. ///</summary>
  51. [TestMethod]
  52. public void ConstructorWithFileNameAndPassphraseShouldThrowArgumentNullExceptionWhenFileNameIsNull()
  53. {
  54. string fileName = null;
  55. try
  56. {
  57. _ = new PrivateKeyFile(fileName, "12345");
  58. Assert.Fail();
  59. }
  60. catch (ArgumentNullException ex)
  61. {
  62. Assert.IsNull(ex.InnerException);
  63. Assert.AreEqual("fileName", ex.ParamName);
  64. }
  65. }
  66. [TestMethod]
  67. public void ConstructorWithPrivateKeyShouldThrowArgumentNullExceptionWhenPrivateKeyIsNull()
  68. {
  69. Stream privateKey = null;
  70. try
  71. {
  72. _ = new PrivateKeyFile(privateKey);
  73. Assert.Fail();
  74. }
  75. catch (ArgumentNullException ex)
  76. {
  77. Assert.IsNull(ex.InnerException);
  78. Assert.AreEqual("privateKey", ex.ParamName);
  79. }
  80. }
  81. [TestMethod]
  82. public void ConstructorWithPrivateKeyAndPassphraseShouldThrowArgumentNullExceptionWhenPrivateKeyIsNull()
  83. {
  84. Stream privateKey = null;
  85. try
  86. {
  87. _ = new PrivateKeyFile(privateKey, "12345");
  88. Assert.Fail();
  89. }
  90. catch (ArgumentNullException ex)
  91. {
  92. Assert.IsNull(ex.InnerException);
  93. Assert.AreEqual("privateKey", ex.ParamName);
  94. }
  95. }
  96. [TestMethod]
  97. public void ConstructorWithKeyShouldThrowArgumentNullExceptionWhenKeyIsNull()
  98. {
  99. Key key = null;
  100. try
  101. {
  102. _ = new PrivateKeyFile(key);
  103. Assert.Fail();
  104. }
  105. catch (ArgumentNullException ex)
  106. {
  107. Assert.IsNull(ex.InnerException);
  108. Assert.AreEqual("key", ex.ParamName);
  109. }
  110. }
  111. [TestMethod]
  112. public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshExceptionWhenPassphraseIsWrong()
  113. {
  114. using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
  115. {
  116. try
  117. {
  118. _ = new PrivateKeyFile(stream, "34567");
  119. Assert.Fail();
  120. }
  121. catch (SshException ex)
  122. {
  123. Assert.IsInstanceOfType<SshException>(ex);
  124. Assert.IsNull(ex.InnerException);
  125. Assert.AreEqual("Invalid passphrase.", ex.Message);
  126. }
  127. }
  128. }
  129. [TestMethod]
  130. public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPassphraseIsNull()
  131. {
  132. using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
  133. {
  134. try
  135. {
  136. _ = new PrivateKeyFile(stream, null);
  137. Assert.Fail();
  138. }
  139. catch (SshPassPhraseNullOrEmptyException ex)
  140. {
  141. Assert.IsInstanceOfType<SshPassPhraseNullOrEmptyException>(ex);
  142. Assert.IsNull(ex.InnerException);
  143. Assert.AreEqual("Private key is encrypted but passphrase is empty.", ex.Message);
  144. }
  145. }
  146. }
  147. [TestMethod]
  148. public void Test_PrivateKey_SSH2_Encrypted_ShouldThrowSshPassPhraseNullOrEmptyExceptionWhenPassphraseIsEmpty()
  149. {
  150. using (var stream = GetData("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt"))
  151. {
  152. try
  153. {
  154. _ = new PrivateKeyFile(stream, string.Empty);
  155. Assert.Fail();
  156. }
  157. catch (SshPassPhraseNullOrEmptyException ex)
  158. {
  159. Assert.IsInstanceOfType<SshPassPhraseNullOrEmptyException>(ex);
  160. Assert.IsNull(ex.InnerException);
  161. Assert.AreEqual("Private key is encrypted but passphrase is empty.", ex.Message);
  162. }
  163. }
  164. }
  165. /// <summary>
  166. ///A test for Dispose
  167. ///</summary>
  168. [TestMethod]
  169. public void DisposeTest()
  170. {
  171. using (var privateKeyStream = GetData("Key.RSA.txt"))
  172. {
  173. var target = new PrivateKeyFile(privateKeyStream);
  174. target.Dispose();
  175. }
  176. }
  177. /// <summary>
  178. /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
  179. ///</summary>
  180. [TestMethod]
  181. public void ConstructorWithFileNameAndPassphrase()
  182. {
  183. using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
  184. {
  185. SaveStreamToFile(stream, _temporaryFile);
  186. }
  187. using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read))
  188. {
  189. var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
  190. TestRsaKeyFile(privateKeyFile);
  191. }
  192. }
  193. /// <summary>
  194. /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
  195. ///</summary>
  196. [TestMethod]
  197. public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenNeededPassphraseIsEmpty()
  198. {
  199. var passphrase = string.Empty;
  200. using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
  201. {
  202. SaveStreamToFile(stream, _temporaryFile);
  203. }
  204. try
  205. {
  206. _ = new PrivateKeyFile(_temporaryFile, passphrase);
  207. Assert.Fail();
  208. }
  209. catch (SshPassPhraseNullOrEmptyException ex)
  210. {
  211. Assert.IsNull(ex.InnerException);
  212. Assert.AreEqual("Private key is encrypted but passphrase is empty.", ex.Message);
  213. }
  214. }
  215. /// <summary>
  216. /// A test for <see cref="PrivateKeyFile(string, string)"/> ctor.
  217. ///</summary>
  218. [TestMethod]
  219. public void ConstructorWithFileNameAndPassphraseShouldThrowSshPassPhraseNullOrEmptyExceptionWhenNeededPassphraseIsNull()
  220. {
  221. string passphrase = null;
  222. using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
  223. {
  224. SaveStreamToFile(stream, _temporaryFile);
  225. }
  226. try
  227. {
  228. _ = new PrivateKeyFile(_temporaryFile, passphrase);
  229. Assert.Fail();
  230. }
  231. catch (SshPassPhraseNullOrEmptyException ex)
  232. {
  233. Assert.IsNull(ex.InnerException);
  234. Assert.AreEqual("Private key is encrypted but passphrase is empty.", ex.Message);
  235. }
  236. }
  237. /// <summary>
  238. /// A test for <see cref="PrivateKeyFile(string)"/> ctor.
  239. ///</summary>
  240. [TestMethod]
  241. public void ConstructorWithFileName()
  242. {
  243. using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
  244. {
  245. SaveStreamToFile(stream, _temporaryFile);
  246. }
  247. var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
  248. TestRsaKeyFile(privateKeyFile);
  249. }
  250. [TestMethod]
  251. public void ConstructorWithFileNameShouldBeAbleToReadFileThatIsSharedForReadAccess()
  252. {
  253. using (var stream = GetData("Key.RSA.txt"))
  254. {
  255. SaveStreamToFile(stream, _temporaryFile);
  256. }
  257. using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read))
  258. {
  259. var privateKeyFile = new PrivateKeyFile(_temporaryFile);
  260. TestRsaKeyFile(privateKeyFile);
  261. }
  262. }
  263. [TestMethod]
  264. public void ConstructorWithFileNameAndPassPhraseShouldBeAbleToReadFileThatIsSharedForReadAccess()
  265. {
  266. using (var stream = GetData("Key.RSA.Encrypted.Aes.128.CBC.12345.txt"))
  267. {
  268. SaveStreamToFile(stream, _temporaryFile);
  269. }
  270. using (var fs = File.Open(_temporaryFile, FileMode.Open, FileAccess.Read, FileShare.Read))
  271. {
  272. var privateKeyFile = new PrivateKeyFile(_temporaryFile, "12345");
  273. TestRsaKeyFile(privateKeyFile);
  274. }
  275. }
  276. [TestMethod]
  277. [DataRow("Key.DSA.txt", null, typeof(DsaKey))]
  278. [DataRow("Key.ECDSA.Encrypted.txt", "12345", typeof(EcdsaKey))]
  279. [DataRow("Key.ECDSA.txt", null, typeof(EcdsaKey))]
  280. [DataRow("Key.ECDSA384.Encrypted.txt", "12345", typeof(EcdsaKey))]
  281. [DataRow("Key.ECDSA384.txt", null, typeof(EcdsaKey))]
  282. [DataRow("Key.ECDSA521.Encrypted.txt", "12345", typeof(EcdsaKey))]
  283. [DataRow("Key.ECDSA521.txt", null, typeof(EcdsaKey))]
  284. [DataRow("Key.OPENSSH.ECDSA.Encrypted.Aes.128.CTR.txt", "12345", typeof(EcdsaKey))]
  285. [DataRow("Key.OPENSSH.ECDSA.Encrypted.txt", "12345", typeof(EcdsaKey))]
  286. [DataRow("Key.OPENSSH.ECDSA.txt", null, typeof(EcdsaKey))]
  287. [DataRow("Key.OPENSSH.ECDSA384.Encrypted.Aes.256.GCM.txt", "12345", typeof(EcdsaKey))]
  288. [DataRow("Key.OPENSSH.ECDSA384.Encrypted.txt", "12345", typeof(EcdsaKey))]
  289. [DataRow("Key.OPENSSH.ECDSA384.txt", null, typeof(EcdsaKey))]
  290. [DataRow("Key.OPENSSH.ECDSA521.Encrypted.Aes.192.CBC.txt", "12345", typeof(EcdsaKey))]
  291. [DataRow("Key.OPENSSH.ECDSA521.Encrypted.txt", "12345", typeof(EcdsaKey))]
  292. [DataRow("Key.OPENSSH.ECDSA521.txt", null, typeof(EcdsaKey))]
  293. [DataRow("Key.OPENSSH.ED25519.Encrypted.3Des.CBC.txt", "12345", typeof(ED25519Key))]
  294. [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.128.CBC.txt", "12345", typeof(ED25519Key))]
  295. [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.128.GCM.txt", "12345", typeof(ED25519Key))]
  296. [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.256.CBC.txt", "12345", typeof(ED25519Key))]
  297. [DataRow("Key.OPENSSH.ED25519.Encrypted.Aes.256.CTR.txt", "12345", typeof(ED25519Key))]
  298. [DataRow("Key.OPENSSH.ED25519.Encrypted.ChaCha20.Poly1305.txt", "12345", typeof(ED25519Key))]
  299. [DataRow("Key.OPENSSH.ED25519.Encrypted.txt", "12345", typeof(ED25519Key))]
  300. [DataRow("Key.OPENSSH.ED25519.txt", null, typeof(ED25519Key))]
  301. [DataRow("Key.OPENSSH.RSA.Encrypted.Aes.192.CTR.txt", "12345", typeof(RsaKey))]
  302. [DataRow("Key.OPENSSH.RSA.Encrypted.txt", "12345", typeof(RsaKey))]
  303. [DataRow("Key.OPENSSH.RSA.txt", null, typeof(RsaKey))]
  304. [DataRow("Key.RSA.Encrypted.Aes.128.CBC.12345.txt", "12345", typeof(RsaKey))]
  305. [DataRow("Key.RSA.Encrypted.Aes.192.CBC.12345.txt", "12345", typeof(RsaKey))]
  306. [DataRow("Key.RSA.Encrypted.Aes.256.CBC.12345.txt", "12345", typeof(RsaKey))]
  307. [DataRow("Key.RSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(RsaKey))]
  308. [DataRow("Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt", "12345", typeof(RsaKey))]
  309. [DataRow("Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt", "1234567890", typeof(RsaKey))]
  310. [DataRow("Key.RSA.txt", null, typeof(RsaKey))]
  311. [DataRow("Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(DsaKey))]
  312. [DataRow("Key.SSH2.DSA.txt", null, typeof(DsaKey))]
  313. [DataRow("Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt", "12345", typeof(RsaKey))]
  314. [DataRow("Key.SSH2.RSA.txt", null, typeof(RsaKey))]
  315. public void Test_PrivateKey(string name, string passPhrase, Type expectedKeyType)
  316. {
  317. using (var stream = GetData(name))
  318. {
  319. var pkFile = new PrivateKeyFile(stream, passPhrase);
  320. Assert.IsInstanceOfType(pkFile.Key, expectedKeyType);
  321. if (expectedKeyType == typeof(RsaKey))
  322. {
  323. TestRsaKeyFile(pkFile);
  324. }
  325. }
  326. }
  327. private void SaveStreamToFile(Stream stream, string fileName)
  328. {
  329. var buffer = new byte[4000];
  330. using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
  331. {
  332. var bytesRead = stream.Read(buffer, 0, buffer.Length);
  333. while (bytesRead > 0)
  334. {
  335. fs.Write(buffer, 0, bytesRead);
  336. bytesRead = stream.Read(buffer, 0, buffer.Length);
  337. }
  338. }
  339. }
  340. private string GetTempFileName()
  341. {
  342. var tempFile = Path.GetTempFileName();
  343. File.Delete(tempFile);
  344. return tempFile;
  345. }
  346. private static void TestRsaKeyFile(PrivateKeyFile rsaPrivateKeyFile)
  347. {
  348. Assert.IsNotNull(rsaPrivateKeyFile.HostKeyAlgorithms);
  349. Assert.AreEqual(3, rsaPrivateKeyFile.HostKeyAlgorithms.Count);
  350. var algorithms = rsaPrivateKeyFile.HostKeyAlgorithms.ToList();
  351. // ssh-rsa should be attempted first during authentication by default.
  352. // See https://github.com/sshnet/SSH.NET/issues/1233#issuecomment-1871196405
  353. Assert.AreEqual("ssh-rsa", algorithms[0].Name);
  354. Assert.AreEqual("rsa-sha2-512", algorithms[1].Name);
  355. Assert.AreEqual("rsa-sha2-256", algorithms[2].Name);
  356. }
  357. }
  358. }