SSH2Connection.cs
上传用户:szltgg
上传日期:2019-05-16
资源大小:604k
文件大小:38k
源码类别:

Telnet服务器

开发平台:

C#

  1. /*
  2.  Copyright (c) 2005 Poderosa Project, All Rights Reserved.
  3.  This file is a part of the Granados SSH Client Library that is subject to
  4.  the license included in the distributed package.
  5.  You may not use this file except in compliance with the license.
  6.  $Id: SSH2Connection.cs,v 1.2 2005/04/20 08:58:56 okajima Exp $
  7. */
  8. using System;
  9. using System.Collections;
  10. using System.IO;
  11. using System.Threading;
  12. using System.Diagnostics;
  13. using System.Net.Sockets;
  14. using System.Text;
  15. using System.Security.Cryptography;
  16. using Granados.PKI;
  17. using Granados.SSHC;
  18. using Granados.Toolkit;
  19. namespace Granados.SSHCV2
  20. {
  21. public sealed class SSH2Connection : SSHConnection {
  22. //packet count for transmission and reception
  23. private int _tSequence;
  24. //MAC for transmission and reception
  25. private MAC _tMAC;
  26. private SSH2PacketBuilder _packetBuilder;
  27. //server info
  28. private SSH2ConnectionInfo _cInfo;
  29. private bool _waitingForPortForwardingResponse;
  30. private KeyExchanger _asyncKeyExchanger;
  31. public SSH2Connection(SSHConnectionParameter param, ISSHConnectionEventReceiver r, string serverversion, string clientversion) : base(param, r) {
  32. _cInfo = new SSH2ConnectionInfo();
  33. _cInfo._serverVersionString = serverversion;
  34. _cInfo._clientVersionString = clientversion;
  35. _packetBuilder = new SSH2PacketBuilder(new SynchronizedSSH2PacketHandler());
  36. }
  37. internal override IByteArrayHandler PacketBuilder {
  38. get {
  39. return _packetBuilder;
  40. }
  41. }
  42. public override SSHConnectionInfo ConnectionInfo {
  43. get {
  44. return _cInfo;
  45. }
  46. }
  47. internal override AuthenticationResult Connect(AbstractSocket s) {
  48. _stream = s;
  49. KeyExchanger kex = new KeyExchanger(this, null);
  50. if(!kex.SynchronousKexExchange()) {
  51. _stream.Close();
  52. return AuthenticationResult.Failure;
  53. }
  54. //Step3 user authentication
  55. ServiceRequest("ssh-userauth");
  56. _authenticationResult = UserAuth();
  57. return _authenticationResult;
  58. }
  59. private void ServiceRequest(string servicename) {
  60. SSH2DataWriter wr = new SSH2DataWriter();
  61. wr.WritePacketType(PacketType.SSH_MSG_SERVICE_REQUEST);
  62. wr.Write(servicename);
  63. TransmitPacket(wr.ToByteArray());
  64. byte[] response = ReceivePacket().Data;
  65. SSH2DataReader re = new SSH2DataReader(response);
  66. PacketType t = re.ReadPacketType();
  67. if(t!=PacketType.SSH_MSG_SERVICE_ACCEPT) {
  68. throw new SSHException("service establishment failed "+t);
  69. }
  70. string s = Encoding.ASCII.GetString(re.ReadString());
  71. if(servicename!=s)
  72. throw new SSHException("protocol error");
  73. }
  74. private AuthenticationResult UserAuth() {
  75. string sn = "ssh-connection"; 
  76. SSH2DataWriter wr = new SSH2DataWriter();
  77. wr.WritePacketType(PacketType.SSH_MSG_USERAUTH_REQUEST);
  78. wr.Write(_param.UserName);
  79. if(_param.AuthenticationType==AuthenticationType.Password) {
  80. //Password authentication
  81. wr.Write(sn); 
  82. wr.Write("password");
  83. wr.Write(false);
  84. wr.Write(_param.Password);
  85. }
  86. else if(_param.AuthenticationType==AuthenticationType.KeyboardInteractive) {
  87. wr.Write(sn); 
  88. wr.Write("keyboard-interactive");
  89. wr.Write(""); //lang
  90. wr.Write(""); //submethod
  91. }
  92. else {
  93. //public key authentication
  94. SSH2UserAuthKey kp = SSH2UserAuthKey.FromSECSHStyleFile(_param.IdentityFile, _param.Password);
  95. SSH2DataWriter signsource = new SSH2DataWriter();
  96. signsource.WriteAsString(_sessionID);
  97. signsource.WritePacketType(PacketType.SSH_MSG_USERAUTH_REQUEST);
  98. signsource.Write(_param.UserName);
  99. signsource.Write(sn);
  100. signsource.Write("publickey");
  101. signsource.Write(true);
  102. signsource.Write(SSH2Util.PublicKeyAlgorithmName(kp.Algorithm));
  103. signsource.WriteAsString(kp.GetPublicKeyBlob());
  104. SSH2DataWriter signpack = new SSH2DataWriter();
  105. signpack.Write(SSH2Util.PublicKeyAlgorithmName(kp.Algorithm));
  106. signpack.WriteAsString(kp.Sign(signsource.ToByteArray()));
  107. wr.Write(sn);
  108. wr.Write("publickey");
  109. wr.Write(true);
  110. wr.Write(SSH2Util.PublicKeyAlgorithmName(kp.Algorithm));
  111. wr.WriteAsString(kp.GetPublicKeyBlob());
  112. wr.WriteAsString(signpack.ToByteArray());
  113. }
  114. TransmitPacket(wr.ToByteArray());
  115. _authenticationResult = ProcessAuthenticationResponse();
  116. if(_authenticationResult==AuthenticationResult.Failure)
  117. throw new SSHException(Strings.GetString("AuthenticationFailed"));
  118. return _authenticationResult;
  119. }
  120. private AuthenticationResult ProcessAuthenticationResponse() {
  121. do {
  122. SSH2DataReader response = new SSH2DataReader(ReceivePacket().Data);
  123. PacketType h = response.ReadPacketType();
  124. if(h==PacketType.SSH_MSG_USERAUTH_FAILURE) {
  125. string msg = Encoding.ASCII.GetString(response.ReadString());
  126. return AuthenticationResult.Failure;
  127. }
  128. else if(h==PacketType.SSH_MSG_USERAUTH_BANNER) {
  129. Debug.WriteLine("USERAUTH_BANNER");
  130. }
  131. else if(h==PacketType.SSH_MSG_USERAUTH_SUCCESS) {
  132. _packetBuilder.Handler = new CallbackSSH2PacketHandler(this);
  133. return AuthenticationResult.Success; //successfully exit
  134. }
  135. else if(h==PacketType.SSH_MSG_USERAUTH_INFO_REQUEST) {
  136. string name = Encoding.ASCII.GetString(response.ReadString());
  137. string inst = Encoding.ASCII.GetString(response.ReadString());
  138. string lang = Encoding.ASCII.GetString(response.ReadString());
  139. int num = response.ReadInt32();
  140. string[] prompts = new string[num];
  141. for(int i=0; i<num; i++) {
  142. prompts[i] = Encoding.ASCII.GetString(response.ReadString());
  143. bool echo = response.ReadBool();
  144. }
  145. _eventReceiver.OnAuthenticationPrompt(prompts);
  146. return AuthenticationResult.Prompt;
  147. }
  148. else
  149. throw new SSHException("protocol error: unexpected packet type "+h);
  150. } while(true);
  151. }
  152. public AuthenticationResult DoKeyboardInteractiveAuth(string[] input) {
  153. if(_param.AuthenticationType!=AuthenticationType.KeyboardInteractive)
  154. throw new SSHException("DoKeyboardInteractiveAuth() must be called with keyboard-interactive authentication");
  155. SSH2DataWriter re = new SSH2DataWriter();
  156. re.WritePacketType(PacketType.SSH_MSG_USERAUTH_INFO_RESPONSE);
  157. re.Write(input.Length);
  158. foreach(string t in input)
  159. re.Write(t);
  160. TransmitPacket(re.ToByteArray());
  161. _authenticationResult = ProcessAuthenticationResponse();
  162. //try again on failure
  163. if(_authenticationResult==AuthenticationResult.Failure) {
  164. SSH2DataWriter wr = new SSH2DataWriter();
  165. wr.WritePacketType(PacketType.SSH_MSG_USERAUTH_REQUEST);
  166. wr.Write(_param.UserName);
  167. wr.Write("ssh-connection"); 
  168. wr.Write("keyboard-interactive");
  169. wr.Write(""); //lang
  170. wr.Write(""); //submethod
  171. TransmitPacket(wr.ToByteArray());
  172. _authenticationResult = ProcessAuthenticationResponse();
  173. }
  174. return _authenticationResult;
  175. }
  176. public override SSHChannel OpenShell(ISSHChannelEventReceiver receiver) {
  177. //open channel
  178. SSH2DataWriter wr = new SSH2DataWriter();
  179. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_OPEN);
  180. wr.Write("session");
  181. int local_channel = this.RegisterChannelEventReceiver(null, receiver)._localID;
  182. wr.Write(local_channel);
  183. wr.Write(_param.WindowSize); //initial window size
  184. int windowsize = _param.WindowSize;
  185. wr.Write(_param.MaxPacketSize); //max packet size
  186. SSH2Channel channel = new SSH2Channel(this, ChannelType.Shell, local_channel);
  187. TransmitPacket(wr.ToByteArray());
  188. return channel;
  189. }
  190. public override SSHChannel ForwardPort(ISSHChannelEventReceiver receiver, string remote_host, int remote_port, string originator_host, int originator_port) {
  191. SSH2DataWriter wr = new SSH2DataWriter();
  192. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_OPEN);
  193. wr.Write("direct-tcpip");
  194. int local_id = RegisterChannelEventReceiver(null, receiver)._localID;
  195. wr.Write(local_id);
  196. wr.Write(_param.WindowSize); //initial window size
  197. int windowsize = _param.WindowSize;
  198. wr.Write(_param.MaxPacketSize); //max packet size
  199. wr.Write(remote_host);
  200. wr.Write(remote_port);
  201. wr.Write(originator_host);
  202. wr.Write(originator_port);
  203. SSH2Channel channel = new SSH2Channel(this, ChannelType.ForwardedLocalToRemote, local_id);
  204. TransmitPacket(wr.ToByteArray());
  205. return channel;
  206. }
  207. public override void ListenForwardedPort(string allowed_host, int bind_port) {
  208. SSH2DataWriter wr = new SSH2DataWriter();
  209. wr.WritePacketType(PacketType.SSH_MSG_GLOBAL_REQUEST);
  210. wr.Write("tcpip-forward");
  211. wr.Write(true);
  212. wr.Write(allowed_host);
  213. wr.Write(bind_port);
  214. _waitingForPortForwardingResponse = true;
  215. TransmitPacket(wr.ToByteArray());
  216. }
  217. public override void CancelForwardedPort(string host, int port) {
  218. SSH2DataWriter wr = new SSH2DataWriter();
  219. wr.WritePacketType(PacketType.SSH_MSG_GLOBAL_REQUEST);
  220. wr.Write("cancel-tcpip-forward");
  221. wr.Write(true);
  222. wr.Write(host);
  223. wr.Write(port);
  224. TransmitPacket(wr.ToByteArray());
  225. }
  226. private void ProcessPortforwardingRequest(ISSHConnectionEventReceiver receiver, SSH2DataReader reader) {
  227. string method = Encoding.ASCII.GetString(reader.ReadString());
  228. int remote_channel = reader.ReadInt32();
  229. int window_size = reader.ReadInt32(); //skip initial window size
  230. int servermaxpacketsize = reader.ReadInt32();
  231. string host = Encoding.ASCII.GetString(reader.ReadString());
  232. int port = reader.ReadInt32();
  233. string originator_ip = Encoding.ASCII.GetString(reader.ReadString());
  234. int originator_port = reader.ReadInt32();
  235. PortForwardingCheckResult r = receiver.CheckPortForwardingRequest(host,port,originator_ip,originator_port);
  236. SSH2DataWriter wr = new SSH2DataWriter();
  237. if(r.allowed) {
  238. //send OPEN_CONFIRMATION
  239. SSH2Channel channel = new SSH2Channel(this, ChannelType.ForwardedRemoteToLocal, RegisterChannelEventReceiver(null, r.channel)._localID, remote_channel, servermaxpacketsize);
  240. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
  241. wr.Write(remote_channel);
  242. wr.Write(channel.LocalChannelID);
  243. wr.Write(_param.WindowSize); //initial window size
  244. wr.Write(_param.MaxPacketSize); //max packet size
  245. receiver.EstablishPortforwarding(r.channel, channel);
  246. }
  247. else {
  248. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_OPEN_FAILURE);
  249. wr.Write(remote_channel);
  250. wr.Write(r.reason_code);
  251. wr.Write(r.reason_message);
  252. wr.Write(""); //lang tag
  253. }
  254. TransmitPacket(wr.ToByteArray());
  255. }
  256. internal SSH2Packet TransmitPacket(byte[] payload) {
  257. lock(_tLockObject) { 
  258. SSH2Packet p = SSH2Packet.FromPlainPayload(payload, _tCipher==null? 8 : _tCipher.BlockSize, _param.Random);
  259. if(_tMAC!=null) p.CalcHash(_tMAC, _tSequence);
  260. _tSequence++;
  261. p.WriteTo(_stream, _tCipher);
  262. return p;
  263. }
  264. }
  265. //synchronous reception
  266. internal SSH2Packet ReceivePacket() {
  267. while(true) {
  268. SSH2Packet p = null;
  269. SynchronizedSSH2PacketHandler handler = (SynchronizedSSH2PacketHandler)_packetBuilder.Handler;
  270. if(!handler.HasPacket) {
  271. handler.Wait();
  272. if(handler.State==ReceiverState.Error)
  273. throw new SSHException(handler.ErrorMessage);
  274. else if(handler.State==ReceiverState.Closed)
  275. throw new SSHException("socket closed");
  276. }
  277. p = handler.PopPacket();
  278. SSH2DataReader r = new SSH2DataReader(p.Data);
  279. PacketType pt = r.ReadPacketType();
  280. if(pt==PacketType.SSH_MSG_IGNORE) {
  281. if(_eventReceiver!=null) _eventReceiver.OnIgnoreMessage(r.ReadString());
  282. }
  283. else if(pt==PacketType.SSH_MSG_DEBUG) {
  284. bool f = r.ReadBool();
  285. if(_eventReceiver!=null) _eventReceiver.OnDebugMessage(f, r.ReadString());
  286. }
  287. else
  288. return p;
  289. }
  290. }
  291. internal void AsyncReceivePacket(SSH2Packet packet) {
  292. try {
  293. ProcessPacket(packet);
  294. }
  295. catch(Exception ex) {
  296. //Debug.WriteLine(ex.StackTrace);
  297. if(!_closed)
  298. _eventReceiver.OnError(ex, ex.Message);
  299. }
  300. }
  301. private bool ProcessPacket(SSH2Packet packet) {
  302. SSH2DataReader r = new SSH2DataReader(packet.Data);
  303. PacketType pt = r.ReadPacketType();
  304. //Debug.WriteLine("ProcessPacket pt="+pt);
  305. if(pt==PacketType.SSH_MSG_DISCONNECT) {
  306. int errorcode = r.ReadInt32();
  307. //string description = Encoding.ASCII.GetString(r.ReadString());
  308. _eventReceiver.OnConnectionClosed();
  309. return false;
  310. }
  311. else if(_waitingForPortForwardingResponse) {
  312. if(pt!=PacketType.SSH_MSG_REQUEST_SUCCESS)
  313. _eventReceiver.OnUnknownMessage((byte)pt, r.Image);
  314. _waitingForPortForwardingResponse = false;
  315. return true;
  316. }
  317. else if(pt==PacketType.SSH_MSG_CHANNEL_OPEN) {
  318. ProcessPortforwardingRequest(_eventReceiver, r);
  319. return true;
  320. }
  321. else if(pt>=PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION && pt<=PacketType.SSH_MSG_CHANNEL_FAILURE) {
  322. int local_channel = r.ReadInt32();
  323. ChannelEntry e = FindChannelEntry(local_channel);
  324. if(e!=null) //throw new SSHException("Unknown channel "+local_channel);
  325. ((SSH2Channel)e._channel).ProcessPacket(e._receiver, pt, 5+r.Rest, r);
  326. else
  327. Debug.WriteLine("unexpected channel pt="+pt+" local_channel="+local_channel.ToString());
  328. return true;
  329. }
  330. else if(pt==PacketType.SSH_MSG_IGNORE) {
  331. _eventReceiver.OnIgnoreMessage(r.ReadString());
  332. return true;
  333. }
  334. else if(_asyncKeyExchanger!=null) {
  335. _asyncKeyExchanger.AsyncProcessPacket(packet);
  336. return true;
  337. }
  338. else if(pt==PacketType.SSH_MSG_KEXINIT) {
  339. //Debug.WriteLine("Host sent KEXINIT");
  340. _asyncKeyExchanger = new KeyExchanger(this, _sessionID);
  341. _asyncKeyExchanger.AsyncProcessPacket(packet);
  342. return true;
  343. }
  344. else {
  345. _eventReceiver.OnUnknownMessage((byte)pt, r.Image);
  346. return false;
  347. }
  348. }
  349. public override void Disconnect(string msg) {
  350. if(_closed) return;
  351. SSH2DataWriter wr = new SSH2DataWriter();
  352. wr.WritePacketType(PacketType.SSH_MSG_DISCONNECT);
  353. wr.Write(0);
  354. wr.Write(msg);
  355. wr.Write(""); //language
  356. TransmitPacket(wr.ToByteArray());
  357. _stream.Flush();
  358. _closed = true;
  359. _stream.Close();
  360. }
  361. public override void Close() {
  362. if(_closed) return;
  363. _closed = true;
  364. _stream.Close();
  365. public override void SendIgnorableData(string msg) {
  366. SSH2DataWriter w = new SSH2DataWriter();
  367. w.WritePacketType(PacketType.SSH_MSG_IGNORE);
  368. w.Write(msg);
  369. TransmitPacket(w.ToByteArray());
  370. }
  371. public void ReexchangeKeys() {
  372. _asyncKeyExchanger = new KeyExchanger(this, _sessionID);
  373. _asyncKeyExchanger.AsyncStartReexchange();
  374. }
  375. internal void LockCommunication() {
  376. _packetBuilder.SetSignal(false);
  377. }
  378. internal void UnlockCommunication() {
  379. _packetBuilder.SetSignal(true);
  380. }
  381. internal void RefreshKeys(byte[] sessionID, Cipher tc, Cipher rc, MAC tm, MAC rm) {
  382. _sessionID = sessionID;
  383. _tCipher = tc;
  384. _tMAC = tm;
  385. _packetBuilder.SetCipher(rc, _param.CheckMACError? rm : null);
  386. _asyncKeyExchanger = null;
  387. }
  388. }
  389. public class SSH2Channel : SSHChannel {
  390. //channel property
  391. protected int _windowSize;
  392. protected int _leftWindowSize;
  393. protected int _serverMaxPacketSize;
  394. protected int _allowedDataSize;
  395. //negotiation status
  396. protected int _negotiationStatus;
  397. public SSH2Channel(SSHConnection con, ChannelType type, int local_id) : base(con, type, local_id) {
  398. _windowSize = _leftWindowSize = con.Param.WindowSize;
  399. _negotiationStatus = type==ChannelType.Shell? 3 : type==ChannelType.ForwardedLocalToRemote? 1 : type==ChannelType.Session? 1 : 0;
  400. }
  401. public SSH2Channel(SSHConnection con, ChannelType type, int local_id, int remote_id, int maxpacketsize) : base(con, type, local_id) {
  402. _windowSize = _leftWindowSize = con.Param.WindowSize;
  403. Debug.Assert(type==ChannelType.ForwardedRemoteToLocal);
  404. _remoteID = remote_id;
  405. _serverMaxPacketSize = maxpacketsize;
  406. _negotiationStatus = 0;
  407. }
  408. public override void ResizeTerminal(int width, int height, int pixel_width, int pixel_height) {
  409. SSH2DataWriter wr = new SSH2DataWriter();
  410. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_REQUEST);
  411. wr.Write(_remoteID);
  412. wr.Write("window-change");
  413. wr.Write(false);
  414. wr.Write(width);
  415. wr.Write(height);
  416. wr.Write(pixel_width); //no graphics
  417. wr.Write(pixel_height);
  418. TransmitPacket(wr.ToByteArray());
  419. }
  420. public override void Transmit(byte[] data) {
  421. //!!it is better idea that we wait a WINDOW_ADJUST if the left size is lack
  422. SSH2DataWriter wr = new SSH2DataWriter();
  423. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_DATA);
  424. wr.Write(_remoteID);
  425. wr.WriteAsString(data);
  426. TransmitPacket(wr.ToByteArray());
  427. }
  428. public override void Transmit(byte[] data, int offset, int length) {
  429. SSH2DataWriter wr = new SSH2DataWriter();
  430. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_DATA);
  431. wr.Write(_remoteID);
  432. wr.WriteAsString(data, offset, length);
  433. TransmitPacket(wr.ToByteArray());
  434. }
  435. public override void SendEOF() {
  436. if(_connection.IsClosed) return;
  437. SSH2DataWriter wr = new SSH2DataWriter();
  438. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_EOF);
  439. wr.Write(_remoteID);
  440. TransmitPacket(wr.ToByteArray());
  441. }
  442. public override void Close() {
  443. if(_connection.IsClosed) return;
  444. SSH2DataWriter wr = new SSH2DataWriter();
  445. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_CLOSE);
  446. wr.Write(_remoteID);
  447. TransmitPacket(wr.ToByteArray());
  448. }
  449. //maybe this is SSH2 only feature
  450. public void SetEnvironmentVariable(string name, string value) {
  451. SSH2DataWriter wr = new SSH2DataWriter();
  452. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_REQUEST);
  453. wr.Write(_remoteID);
  454. wr.Write("env");
  455. wr.Write(false);
  456. wr.Write(name);
  457. wr.Write(value);
  458. TransmitPacket(wr.ToByteArray());
  459. }
  460. public void SendBreak(int time) {
  461. SSH2DataWriter wr = new SSH2DataWriter();
  462. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_REQUEST);
  463. wr.Write(_remoteID);
  464. wr.Write("break");
  465. wr.Write(true);
  466. wr.Write(time);
  467. TransmitPacket(wr.ToByteArray());
  468. }
  469. internal void ProcessPacket(ISSHChannelEventReceiver receiver, PacketType pt, int data_length, SSH2DataReader re) {
  470. //NOTE: the offset of 're' is next to 'receipiant channel' field
  471. _leftWindowSize -= data_length;
  472. if(pt!=PacketType.SSH_MSG_CHANNEL_EOF && pt!=PacketType.SSH_MSG_CHANNEL_CLOSE) { //window adjust is not necessary if the channel is being closed
  473. while(_leftWindowSize <= _windowSize) {
  474. SSH2DataWriter adj = new SSH2DataWriter();
  475. adj.WritePacketType(PacketType.SSH_MSG_CHANNEL_WINDOW_ADJUST);
  476. adj.Write(_remoteID);
  477. adj.Write(_windowSize);
  478. TransmitPacket(adj.ToByteArray());
  479. _leftWindowSize += _windowSize;
  480. //Debug.WriteLine("Window size is adjusted to " + _leftWindowSize);
  481. }
  482. }
  483. if(pt==PacketType.SSH_MSG_CHANNEL_WINDOW_ADJUST) {
  484. int w = re.ReadInt32();
  485. //some servers may not send SSH_MSG_CHANNEL_WINDOW_ADJUST. 
  486. //it is dangerous to wait this message in send procedure
  487. _allowedDataSize += w;
  488. //Debug.WriteLine(String.Format("Window Adjust +={0}",w));
  489. }
  490. else if(_negotiationStatus!=0) { //when the negotiation is not completed
  491. if(_type==ChannelType.Shell)
  492. OpenShell(receiver, pt, re);
  493. else if(_type==ChannelType.ForwardedLocalToRemote)
  494. ReceivePortForwardingResponse(receiver, pt, re);
  495. else if(_type==ChannelType.Session)
  496. EstablishSession(receiver, pt, re);
  497. }
  498. else {
  499. switch(pt) {
  500. case PacketType.SSH_MSG_CHANNEL_DATA: {
  501. int len = re.ReadInt32();
  502. receiver.OnData(re.Image, re.Offset, len);
  503. }
  504. break;
  505. case PacketType.SSH_MSG_CHANNEL_EXTENDED_DATA: {
  506. int t = re.ReadInt32();
  507. byte[] data = re.ReadString();
  508. receiver.OnExtendedData(t, data);
  509. }
  510. break;
  511. case PacketType.SSH_MSG_CHANNEL_REQUEST: {
  512. string request = Encoding.ASCII.GetString(re.ReadString());
  513. bool reply = re.ReadBool();
  514. if(request=="exit-status") {
  515. int status = re.ReadInt32();
  516. }
  517. else if(reply) { //we reject unknown requests including keep-alive check
  518. SSH2DataWriter wr = new SSH2DataWriter();
  519. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_FAILURE);
  520. wr.Write(_remoteID);
  521. TransmitPacket(wr.ToByteArray());
  522. }
  523. }
  524. break;
  525. case PacketType.SSH_MSG_CHANNEL_EOF:
  526. receiver.OnChannelEOF();
  527. break;
  528. case PacketType.SSH_MSG_CHANNEL_CLOSE:
  529. _connection.UnregisterChannelEventReceiver(_localID);
  530. receiver.OnChannelClosed();
  531. break;
  532. case PacketType.SSH_MSG_CHANNEL_FAILURE:
  533. case PacketType.SSH_MSG_CHANNEL_SUCCESS:
  534. receiver.OnMiscPacket((byte)pt, re.Image, re.Offset, re.Rest);
  535. break;
  536. default:
  537. receiver.OnMiscPacket((byte)pt, re.Image, re.Offset, re.Rest);
  538. Debug.WriteLine("Unknown Packet "+pt);
  539. break;
  540. }
  541. }
  542. }
  543. private void TransmitPacket(byte[] data) {
  544. _allowedDataSize -= data.Length;
  545. ((SSH2Connection)_connection).TransmitPacket(data);
  546. }
  547. private void OpenShell(ISSHChannelEventReceiver receiver, PacketType pt, SSH2DataReader reader) {
  548. if(_negotiationStatus==3) {
  549. if(pt!=PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
  550. if(pt!=PacketType.SSH_MSG_CHANNEL_OPEN_FAILURE)
  551. receiver.OnChannelError(null, "opening channel failed; packet type="+pt);
  552. else {
  553. int errcode = reader.ReadInt32();
  554. string msg = Encoding.ASCII.GetString(reader.ReadString());
  555. receiver.OnChannelError(null, msg);
  556. }
  557. Close();
  558. }
  559. else {
  560. _remoteID = reader.ReadInt32();
  561. _allowedDataSize = reader.ReadInt32();
  562. _serverMaxPacketSize = reader.ReadInt32();
  563. //open pty
  564. SSH2DataWriter wr = new SSH2DataWriter();
  565. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_REQUEST);
  566. wr.Write(_remoteID);
  567. wr.Write("pty-req");
  568. wr.Write(true);
  569. wr.Write(_connection.Param.TerminalName);
  570. wr.Write(_connection.Param.TerminalWidth);
  571. wr.Write(_connection.Param.TerminalHeight);
  572. wr.Write(_connection.Param.TerminalPixelWidth);
  573. wr.Write(_connection.Param.TerminalPixelHeight);
  574. wr.WriteAsString(new byte[0]);
  575. TransmitPacket(wr.ToByteArray());
  576. _negotiationStatus = 2;
  577. }
  578. }
  579. else if(_negotiationStatus==2) {
  580. if(pt!=PacketType.SSH_MSG_CHANNEL_SUCCESS) {
  581. receiver.OnChannelError(null, "opening pty failed");
  582. Close();
  583. }
  584. else {
  585. //open shell
  586. SSH2DataWriter wr = new SSH2DataWriter();
  587. wr.WritePacketType(PacketType.SSH_MSG_CHANNEL_REQUEST);
  588. wr.Write(_remoteID);
  589. wr.Write("shell");
  590. wr.Write(true);
  591. TransmitPacket(wr.ToByteArray());
  592. _negotiationStatus = 1;
  593. }
  594. }
  595. else if(_negotiationStatus==1) {
  596. if(pt!=PacketType.SSH_MSG_CHANNEL_SUCCESS) {
  597. receiver.OnChannelError(null, "Opening shell failed: packet type="+pt.ToString());
  598. Close();
  599. }
  600. else {
  601. receiver.OnChannelReady();
  602. _negotiationStatus = 0; //goal!
  603. }
  604. }
  605. else
  606. Debug.Assert(false);
  607. }
  608. private void ReceivePortForwardingResponse(ISSHChannelEventReceiver receiver, PacketType pt, SSH2DataReader reader) {
  609. if(_negotiationStatus==1) {
  610. if(pt!=PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
  611. if(pt!=PacketType.SSH_MSG_CHANNEL_OPEN_FAILURE)
  612. receiver.OnChannelError(null, "opening channel failed; packet type="+pt);
  613. else {
  614. int errcode = reader.ReadInt32();
  615. string msg = Encoding.ASCII.GetString(reader.ReadString());
  616. receiver.OnChannelError(null, msg);
  617. }
  618. Close();
  619. }
  620. else {
  621. _remoteID = reader.ReadInt32();
  622. _serverMaxPacketSize = reader.ReadInt32();
  623. _negotiationStatus = 0;
  624. receiver.OnChannelReady();
  625. }
  626. }
  627. else
  628. Debug.Assert(false);
  629. }
  630. private void EstablishSession(ISSHChannelEventReceiver receiver, PacketType pt, SSH2DataReader reader) {
  631. if(_negotiationStatus==1) {
  632. if(pt!=PacketType.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
  633. if(pt!=PacketType.SSH_MSG_CHANNEL_OPEN_FAILURE)
  634. receiver.OnChannelError(null, "opening channel failed; packet type="+pt);
  635. else {
  636. int remote_id = reader.ReadInt32();
  637. int errcode = reader.ReadInt32();
  638. string msg = Encoding.ASCII.GetString(reader.ReadString());
  639. receiver.OnChannelError(null, msg);
  640. }
  641. Close();
  642. }
  643. else {
  644. _remoteID = reader.ReadInt32();
  645. _serverMaxPacketSize = reader.ReadInt32();
  646. _negotiationStatus = 0;
  647. receiver.OnChannelReady();
  648. }
  649. }
  650. else
  651. Debug.Assert(false);
  652. }
  653. }
  654. internal class KeyExchanger {
  655. private SSH2Connection _con;
  656. private SSHConnectionParameter _param;
  657. private SSH2ConnectionInfo _cInfo;
  658. //payload of KEXINIT message
  659. private byte[] _serverKEXINITPayload;
  660. private byte[] _clientKEXINITPayload;
  661. //true if the host sent KEXINIT first
  662. private bool _startedByHost;
  663. private ManualResetEvent _newKeyEvent;
  664. //status
  665. private enum Status {
  666. INITIAL,
  667. WAIT_KEXINIT,
  668. WAIT_KEXDH_REPLY,
  669. WAIT_NEWKEYS,
  670. FINISHED
  671. }
  672. private Status _status;
  673. private BigInteger _x;
  674. private BigInteger _e;
  675. private BigInteger _k;
  676. private byte[] _hash;
  677. private byte[] _sessionID;
  678. //results
  679. Cipher _rc;
  680. Cipher _tc;
  681. MAC _rm;
  682. MAC _tm;
  683. private void TransmitPacket(byte[] payload) {
  684. _con.TransmitPacket(payload);
  685. }
  686. public KeyExchanger(SSH2Connection con, byte[] sessionID) {
  687. _con = con;
  688. _param = con.Param;
  689. _cInfo = (SSH2ConnectionInfo)con.ConnectionInfo;
  690. _sessionID = sessionID;
  691. _status = Status.INITIAL;
  692. }
  693. public bool SynchronousKexExchange() {
  694. SendKEXINIT();
  695. ProcessKEXINIT(_con.ReceivePacket());
  696. SendKEXDHINIT();
  697. if(!ProcessKEXDHREPLY(_con.ReceivePacket())) return false;
  698. SendNEWKEYS();
  699. ProcessNEWKEYS(_con.ReceivePacket());
  700. return true;
  701. }
  702. public void AsyncStartReexchange() {
  703. _startedByHost = false;
  704. _status = Status.WAIT_KEXINIT;
  705. SendKEXINIT();
  706. }
  707. public void AsyncProcessPacket(SSH2Packet packet) {
  708. switch(_status) {
  709. case Status.INITIAL:
  710. _startedByHost = true;
  711. ProcessKEXINIT(packet);
  712. SendKEXINIT();
  713. SendKEXDHINIT();
  714. break;
  715. case Status.WAIT_KEXINIT:
  716. ProcessKEXINIT(packet);
  717. SendKEXDHINIT();
  718. break;
  719. case Status.WAIT_KEXDH_REPLY:
  720. ProcessKEXDHREPLY(packet);
  721. SendNEWKEYS();
  722. break;
  723. case Status.WAIT_NEWKEYS:
  724. ProcessNEWKEYS(packet);
  725. Debug.Assert(_status==Status.FINISHED);
  726. break;
  727. }
  728. }
  729. private void SendKEXINIT() {
  730. SSH2DataWriter wr = new SSH2DataWriter();
  731. wr.WritePacketType(PacketType.SSH_MSG_KEXINIT);
  732. byte[] cookie = new byte[16];
  733. _param.Random.NextBytes(cookie);
  734. wr.Write(cookie);
  735. wr.Write("diffie-hellman-group1-sha1"); //    kex_algorithms
  736. wr.Write(FormatHostKeyAlgorithmDescription());            //    server_host_key_algorithms
  737. wr.Write(FormatCipherAlgorithmDescription());      //    encryption_algorithms_client_to_server
  738. wr.Write(FormatCipherAlgorithmDescription());      //    encryption_algorithms_server_to_client
  739. wr.Write("hmac-sha1");                  //    mac_algorithms_client_to_server
  740. wr.Write("hmac-sha1");                  //    mac_algorithms_server_to_client
  741. wr.Write("none");                       //    compression_algorithms_client_to_server
  742. wr.Write("none");                       //    compression_algorithms_server_to_client
  743. wr.Write("");                           //    languages_client_to_server
  744. wr.Write("");                           //    languages_server_to_client
  745. wr.Write(false); //Indicates whether a guessed key exchange packet follows
  746. wr.Write(0);       //reserved for future extension
  747. _clientKEXINITPayload = wr.ToByteArray();
  748. _status = Status.WAIT_KEXINIT;
  749. TransmitPacket(_clientKEXINITPayload);
  750. }
  751. private void ProcessKEXINIT(SSH2Packet packet) {
  752. _serverKEXINITPayload = packet.Data;
  753. SSH2DataReader re = new SSH2DataReader(_serverKEXINITPayload);
  754. byte[] head = re.Read(17); //Type and cookie
  755. if(head[0]!=(byte)PacketType.SSH_MSG_KEXINIT) throw new SSHException(String.Format("Server response is not SSH_MSG_KEXINIT but {0}", head[0]));
  756. Encoding enc = Encoding.ASCII;
  757. string kex = enc.GetString(re.ReadString());
  758. _cInfo._supportedKEXAlgorithms = kex;
  759. CheckAlgorithmSupport("keyexchange", kex, "diffie-hellman-group1-sha1");
  760. string host_key = enc.GetString(re.ReadString());
  761. _cInfo._supportedHostKeyAlgorithms = host_key;
  762. _cInfo._algorithmForHostKeyVerification = DecideHostKeyAlgorithm(host_key);
  763. string enc_cs = enc.GetString(re.ReadString());
  764. _cInfo._supportedCipherAlgorithms = enc_cs;
  765. _cInfo._algorithmForTransmittion = DecideCipherAlgorithm(enc_cs);
  766. string enc_sc = enc.GetString(re.ReadString());
  767. _cInfo._algorithmForReception = DecideCipherAlgorithm(enc_sc);
  768. string mac_cs = enc.GetString(re.ReadString());
  769. CheckAlgorithmSupport("mac", mac_cs, "hmac-sha1");
  770. string mac_sc = enc.GetString(re.ReadString());
  771. CheckAlgorithmSupport("mac", mac_sc, "hmac-sha1");
  772. string comp_cs = enc.GetString(re.ReadString());
  773. CheckAlgorithmSupport("compression", comp_cs, "none");
  774. string comp_sc = enc.GetString(re.ReadString());
  775. CheckAlgorithmSupport("compression", comp_sc, "none");
  776. string lang_cs = enc.GetString(re.ReadString());
  777. string lang_sc = enc.GetString(re.ReadString());
  778. bool flag = re.ReadBool();
  779. int reserved = re.ReadInt32();
  780. Debug.Assert(re.Rest==0);
  781. if(flag) throw new SSHException("Algorithm negotiation failed"); 
  782. }
  783. private void SendKEXDHINIT() {
  784. //Round1 computes and sends [e]
  785. byte[] sx = new byte[16];
  786. _param.Random.NextBytes(sx);
  787. _x = new BigInteger(sx);
  788. _e = new BigInteger(2).modPow(_x, DH_PRIME);
  789. SSH2DataWriter wr = new SSH2DataWriter();
  790. wr.WritePacketType(PacketType.SSH_MSG_KEXDH_INIT);
  791. wr.Write(_e);
  792. _status = Status.WAIT_KEXDH_REPLY;
  793. TransmitPacket(wr.ToByteArray());
  794. }
  795. private bool ProcessKEXDHREPLY(SSH2Packet packet) {
  796. //Round2 receives response
  797. SSH2DataReader re = new SSH2DataReader(packet.Data);
  798. PacketType h = re.ReadPacketType();
  799. if(h!=PacketType.SSH_MSG_KEXDH_REPLY) throw new SSHException(String.Format("KeyExchange response is not KEXDH_REPLY but {0}", h));
  800. byte[] key_and_cert = re.ReadString();
  801. BigInteger f = re.ReadMPInt();
  802. byte[] signature = re.ReadString();
  803. Debug.Assert(re.Rest==0);
  804. //Round3 calc hash H
  805. SSH2DataWriter wr = new SSH2DataWriter();
  806. _k = f.modPow(_x, DH_PRIME);
  807. wr = new SSH2DataWriter();
  808. wr.Write(_cInfo._clientVersionString);
  809. wr.Write(_cInfo._serverVersionString);
  810. wr.WriteAsString(_clientKEXINITPayload);
  811. wr.WriteAsString(_serverKEXINITPayload);
  812. wr.WriteAsString(key_and_cert);
  813. wr.Write(_e);
  814. wr.Write(f);
  815. wr.Write(_k);
  816. _hash = new SHA1CryptoServiceProvider().ComputeHash(wr.ToByteArray());
  817. if(!VerifyHostKey(key_and_cert, signature, _hash)) return false;
  818. //Debug.WriteLine("hash="+DebugUtil.DumpByteArray(hash));
  819. if(_sessionID==null) _sessionID = _hash;
  820. return true;
  821. }
  822. private void SendNEWKEYS() {
  823. _status = Status.WAIT_NEWKEYS;
  824. _newKeyEvent = new ManualResetEvent(false);
  825. TransmitPacket(new byte[1] {(byte)PacketType.SSH_MSG_NEWKEYS});
  826. //establish Ciphers
  827. _tc = CipherFactory.CreateCipher(SSHProtocol.SSH2, _cInfo._algorithmForTransmittion,
  828. DeriveKey(_k, _hash, 'C', CipherFactory.GetKeySize(_cInfo._algorithmForTransmittion)), DeriveKey(_k, _hash, 'A', CipherFactory.GetBlockSize(_cInfo._algorithmForTransmittion)));
  829. _rc = CipherFactory.CreateCipher(SSHProtocol.SSH2, _cInfo._algorithmForReception,
  830. DeriveKey(_k, _hash, 'D', CipherFactory.GetKeySize(_cInfo._algorithmForReception)), DeriveKey(_k, _hash, 'B', CipherFactory.GetBlockSize(_cInfo._algorithmForReception)));
  831. //establish MACs
  832. MACAlgorithm ma = MACAlgorithm.HMACSHA1;
  833. _tm = MACFactory.CreateMAC(MACAlgorithm.HMACSHA1, DeriveKey(_k, _hash, 'E', MACFactory.GetSize(ma)));
  834. _rm = MACFactory.CreateMAC(MACAlgorithm.HMACSHA1, DeriveKey(_k, _hash, 'F', MACFactory.GetSize(ma)));
  835. _newKeyEvent.Set();
  836. }
  837. private void ProcessNEWKEYS(SSH2Packet packet) {
  838. //confirms new key
  839. try {
  840. byte[] response = packet.Data;
  841. if(response.Length!=1 || response[0]!=(byte)PacketType.SSH_MSG_NEWKEYS) throw new SSHException("SSH_MSG_NEWKEYS failed");
  842. _newKeyEvent.WaitOne();
  843. _newKeyEvent.Close();
  844. _con.LockCommunication();
  845. _con.RefreshKeys(_sessionID, _tc, _rc, _tm, _rm);
  846. _status = Status.FINISHED;
  847. }
  848. finally {
  849. _con.UnlockCommunication();
  850. }
  851. }
  852. private bool VerifyHostKey(byte[] K_S, byte[] signature, byte[] hash) {
  853. SSH2DataReader re1 = new SSH2DataReader(K_S);
  854. string algorithm = Encoding.ASCII.GetString(re1.ReadString());
  855. if(algorithm!=SSH2Util.PublicKeyAlgorithmName(_cInfo._algorithmForHostKeyVerification))
  856. throw new SSHException("Protocol Error: Host Key Algorithm Mismatch");
  857. SSH2DataReader re2 = new SSH2DataReader(signature);
  858. algorithm = Encoding.ASCII.GetString(re2.ReadString());
  859. if(algorithm!=SSH2Util.PublicKeyAlgorithmName(_cInfo._algorithmForHostKeyVerification))
  860. throw new SSHException("Protocol Error: Host Key Algorithm Mismatch");
  861. byte[] sigbody = re2.ReadString();
  862. Debug.Assert(re2.Rest==0);
  863. if(_cInfo._algorithmForHostKeyVerification==PublicKeyAlgorithm.RSA)
  864. VerifyHostKeyByRSA(re1, sigbody, hash);
  865. else if(_cInfo._algorithmForHostKeyVerification==PublicKeyAlgorithm.DSA)
  866. VerifyHostKeyByDSS(re1, sigbody, hash);
  867. else
  868. throw new SSHException("Bad host key algorithm "+_cInfo._algorithmForHostKeyVerification);
  869. //ask the client whether he accepts the host key
  870. if(!_startedByHost && _param.KeyCheck!=null && !_param.KeyCheck(_cInfo))
  871. return false;
  872. else
  873. return true;
  874. }
  875. private void VerifyHostKeyByRSA(SSH2DataReader pubkey, byte[] sigbody, byte[] hash) {
  876. BigInteger exp = pubkey.ReadMPInt();
  877. BigInteger mod = pubkey.ReadMPInt();
  878. Debug.Assert(pubkey.Rest==0);
  879. //Debug.WriteLine(exp.ToHexString());
  880. //Debug.WriteLine(mod.ToHexString());
  881. RSAPublicKey pk = new RSAPublicKey(exp, mod);
  882. pk.VerifyWithSHA1(sigbody, new SHA1CryptoServiceProvider().ComputeHash(hash));
  883. _cInfo._hostkey = pk;
  884. }
  885. private void VerifyHostKeyByDSS(SSH2DataReader pubkey, byte[] sigbody, byte[] hash) {
  886. BigInteger p = pubkey.ReadMPInt();
  887. BigInteger q = pubkey.ReadMPInt();
  888. BigInteger g = pubkey.ReadMPInt();
  889. BigInteger y = pubkey.ReadMPInt();
  890. Debug.Assert(pubkey.Rest==0);
  891. //Debug.WriteLine(p.ToHexString());
  892. //Debug.WriteLine(q.ToHexString());
  893. //Debug.WriteLine(g.ToHexString());
  894. //Debug.WriteLine(y.ToHexString());
  895. DSAPublicKey pk = new DSAPublicKey(p,g,q,y);
  896. pk.Verify(sigbody, new SHA1CryptoServiceProvider().ComputeHash(hash));
  897. _cInfo._hostkey = pk;
  898. }
  899. private byte[] DeriveKey(BigInteger key, byte[] hash, char ch, int length) {
  900. byte[] result = new byte[length];
  901. SSH2DataWriter wr = new SSH2DataWriter();
  902. wr.Write(key);
  903. wr.Write(hash);
  904. wr.Write((byte)ch);
  905. wr.Write(_sessionID);
  906. byte[] h1 = new SHA1CryptoServiceProvider().ComputeHash(wr.ToByteArray());
  907. if(h1.Length >= length) {
  908. Array.Copy(h1, 0, result, 0, length);
  909. return result;
  910. }
  911. else {
  912. wr = new SSH2DataWriter();
  913. wr.Write(key);
  914. wr.Write(_sessionID);
  915. wr.Write(h1);
  916. byte[] h2 = new SHA1CryptoServiceProvider().ComputeHash(wr.ToByteArray());
  917. if(h1.Length+h2.Length >= length) {
  918. Array.Copy(h1, 0, result, 0, h1.Length);
  919. Array.Copy(h2, 0, result, h1.Length, length-h1.Length);
  920. return result;
  921. }
  922. else
  923. throw new SSHException("necessary key length is too big"); //long key is not supported
  924. }
  925. }
  926. private static void CheckAlgorithmSupport(string title, string data, string algorithm_name) {
  927. string[] t = data.Split(',');
  928. foreach(string s in t) {
  929. if(s==algorithm_name) return; //found!
  930. }
  931. throw new SSHException("Server does not support "+algorithm_name+" for "+title);
  932. }
  933. private PublicKeyAlgorithm DecideHostKeyAlgorithm(string data) {
  934. string[] t = data.Split(',');
  935. foreach(PublicKeyAlgorithm a in _param.PreferableHostKeyAlgorithms) {
  936. if(SSHUtil.ContainsString(t, SSH2Util.PublicKeyAlgorithmName(a))) {
  937. return a;
  938. }
  939. }
  940. throw new SSHException("The negotiation of host key verification algorithm is failed");
  941. }
  942. private CipherAlgorithm DecideCipherAlgorithm(string data) {
  943. string[] t = data.Split(',');
  944. foreach(CipherAlgorithm a in _param.PreferableCipherAlgorithms) {
  945. if(SSHUtil.ContainsString(t, CipherFactory.AlgorithmToSSH2Name(a))) {
  946. return a;
  947. }
  948. }
  949. throw new SSHException("The negotiation of encryption algorithm is failed");
  950. }
  951. private string FormatHostKeyAlgorithmDescription() {
  952. StringBuilder b = new StringBuilder();
  953. if(_param.PreferableHostKeyAlgorithms.Length==0) throw new SSHException("HostKeyAlgorithm is not set");
  954. b.Append(SSH2Util.PublicKeyAlgorithmName(_param.PreferableHostKeyAlgorithms[0]));
  955. for(int i=1; i<_param.PreferableHostKeyAlgorithms.Length; i++) {
  956. b.Append(',');
  957. b.Append(SSH2Util.PublicKeyAlgorithmName(_param.PreferableHostKeyAlgorithms[i]));
  958. }
  959. return b.ToString();
  960. }
  961. private string FormatCipherAlgorithmDescription() {
  962. StringBuilder b = new StringBuilder();
  963. if(_param.PreferableCipherAlgorithms.Length==0) throw new SSHException("CipherAlgorithm is not set");
  964. b.Append(CipherFactory.AlgorithmToSSH2Name(_param.PreferableCipherAlgorithms[0]));
  965. for(int i=1; i<_param.PreferableCipherAlgorithms.Length; i++) {
  966. b.Append(',');
  967. b.Append(CipherFactory.AlgorithmToSSH2Name(_param.PreferableCipherAlgorithms[i]));
  968. }
  969. return b.ToString();
  970. }
  971. /*
  972.  * the seed of diffie-hellman KX defined in the spec of SSH2
  973.  */ 
  974. private static BigInteger _dh_prime = null;
  975. private static BigInteger DH_PRIME {
  976. get {
  977. if(_dh_prime==null) {
  978. StringBuilder sb = new StringBuilder();
  979. sb.Append("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1");
  980. sb.Append("29024E088A67CC74020BBEA63B139B22514A08798E3404DD");
  981. sb.Append("EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245");
  982. sb.Append("E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED");
  983. sb.Append("EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381");
  984. sb.Append("FFFFFFFFFFFFFFFF");
  985. _dh_prime = new BigInteger(sb.ToString(), 16);
  986. }
  987. return _dh_prime;
  988. }
  989. }
  990. }
  991. }