public override object Deserialize(Stream stream) { if (stream == null) { throw new ArgumentNullException("stream"); } using (var reader = new StreamReader(stream)) { var line = reader.ReadLine(); line = line.Trim(); var algoName = new string(line.TakeWhile(c => !char.IsWhiteSpace(c)).ToArray()); line = line.Substring(algoName.Length).Trim(); var data = new string(line.TakeWhile(c => !char.IsWhiteSpace(c)).ToArray()); line = line.Substring(data.Length).Trim(); var comment = line; PublicKeyAlgorithm algo; if (!TryParsePublicKeyAlgorithm(algoName, out algo)) { var message = string.Format("Unknown algorithm: {0}", algoName); throw new KeyFormatterException(message); } var parser = new BlobParser(Util.FromBase64(data)); OpensshCertificate cert; var publicKeyParams = parser.ReadSsh2PublicKeyData(out cert); var key = new SshKey(SshVersion.SSH2, publicKeyParams, null, comment, cert); return(key); } }
public byte[] SignRequest(ISshKey aKey, byte[] aSignData) { BlobBuilder builder = new BlobBuilder(); switch (aKey.Version) { case SshVersion.SSH1: builder.AddBytes(aKey.GetPublicKeyBlob()); var engine = new Pkcs1Encoding(new RsaEngine()); engine.Init(true /* encrypt */, aKey.GetPublicKeyParameters()); var encryptedData = engine.ProcessBlock(aSignData, 0, aSignData.Length); var challenge = new BigInteger(encryptedData); builder.AddSsh1BigIntBlob(challenge); builder.AddBytes(SessionId); builder.AddInt(1); // response type - must be 1 builder.InsertHeader(Agent.Message.SSH1_AGENTC_RSA_CHALLENGE); break; case SshVersion.SSH2: builder.AddBlob(aKey.GetPublicKeyBlob()); builder.AddBlob(aSignData); builder.InsertHeader(Agent.Message.SSH2_AGENTC_SIGN_REQUEST); break; default: throw new Exception(cUnsupportedSshVersion); } BlobParser replyParser = SendMessage(builder); var header = replyParser.ReadHeader(); switch (aKey.Version) { case SshVersion.SSH1: if (header.Message != Agent.Message.SSH1_AGENT_RSA_RESPONSE) { throw new AgentFailureException(); } byte[] response = new byte[16]; for (int i = 0; i < 16; i++) { response[i] = replyParser.ReadUInt8(); } return(response); case SshVersion.SSH2: if (header.Message != Agent.Message.SSH2_AGENT_SIGN_RESPONSE) { throw new AgentFailureException(); } return(replyParser.ReadBlob()); default: throw new Exception(cUnsupportedSshVersion); } }
OpensshCertificate ReadCertificate(BlobBuilder builder) { var serial = ReadUInt64(); builder.AddUInt64(serial); var type = (Ssh2CertType)ReadUInt32(); builder.AddUInt32((uint)type); var keyId = ReadString(); builder.AddStringBlob(keyId); var validPrincipals = ReadBlob(); builder.AddBlob(validPrincipals); var validAfter = ReadUInt64(); builder.AddUInt64(validAfter); var validBefore = ReadUInt64(); builder.AddUInt64(validBefore); var criticalOptions = ReadBlob(); builder.AddBlob(criticalOptions); var extensions = ReadBlob(); builder.AddBlob(extensions); var reserved = ReadBlob(); builder.AddBlob(reserved); var signatureKey = ReadBlob(); builder.AddBlob(signatureKey); var signature = ReadBlob(); builder.AddBlob(signature); var principalsParser = new BlobParser(validPrincipals); var principalsList = new List <string>(); while (principalsParser.Stream.Position < principalsParser.Stream.Length) { principalsList.Add(principalsParser.ReadString()); } var validAfterDateTime = validAfter == ulong.MaxValue ? DateTime.MaxValue : epoch.AddSeconds(validAfter); var validBeforeDateTime = validBefore == ulong.MaxValue ? DateTime.MaxValue : epoch.AddSeconds(validBefore); var signatureKeyParser = new BlobParser(signatureKey); OpensshCertificate unused; var sigKey = signatureKeyParser.ReadSsh2PublicKeyData(out unused); return(new OpensshCertificate(builder.GetBlob(), type, serial, keyId, principalsList, validAfterDateTime, validBeforeDateTime, criticalOptions, extensions, sigKey)); }
public override byte[] SendMessage(byte[] aMessage) { var hwnd = FindWindow(cPageantWindowClass, cPageantWindowClass); if (hwnd == IntPtr.Zero) { throw new AgentNotRunningException(); } var threadId = Thread.CurrentThread.ManagedThreadId; var mapName = String.Format("{0}{1:x8}", cMapNamePrefix, threadId); using (var mappedFile = MemoryMappedFile.CreateNew(mapName, 8192)) { if (mappedFile.SafeMemoryMappedFileHandle.IsInvalid) { throw new Exception("Invalid mapped file handle"); } using (var stream = mappedFile.CreateViewStream()) { stream.Write(aMessage, 0, aMessage.Length); var copyData = new COPYDATASTRUCT(); if (IntPtr.Size == 4) { copyData.dwData = new IntPtr(unchecked ((int)AGENT_COPYDATA_ID)); } else { copyData.dwData = new IntPtr(AGENT_COPYDATA_ID); } copyData.cbData = mapName.Length + 1; copyData.lpData = Marshal.StringToCoTaskMemAnsi(mapName); IntPtr copyDataPtr = Marshal.AllocHGlobal(Marshal.SizeOf(copyData)); Marshal.StructureToPtr(copyData, copyDataPtr, false); var resultPtr = SendMessage(hwnd, WM_COPYDATA, IntPtr.Zero, copyDataPtr); Marshal.FreeHGlobal(copyData.lpData); Marshal.FreeHGlobal(copyDataPtr); if (resultPtr == IntPtr.Zero) { throw new Exception("send message failed"); } stream.Position = 0; var parser = new BlobParser(stream); var replyLength = parser.ReadUInt32(); stream.Position = 0; var reply = new byte[replyLength + 4]; stream.Read(reply, 0, reply.Length); return(reply); } } }
public void SendMessageTest() { // TODO: Need to modify this test so that it does not use PageantAgent const string messageValue = "junk"; var builder = new BlobBuilder (); builder.AddStringBlob(messageValue); var messageBytes = builder.GetBlob(); using (var agent = new PageantAgent()) { var client = new PageantClient(); var reply = client.SendMessage(messageBytes); var replyParser = new BlobParser(reply); var replyHeader = replyParser.ReadHeader(); Assert.That(replyHeader.Message, Is.EqualTo(Agent.Message.SSH_AGENT_FAILURE)); } }
public override byte[] SendMessage(byte[] aMessage) { var hwnd = FindWindow(cPageantWindowClass, cPageantWindowClass); if (hwnd == IntPtr.Zero) { throw new AgentNotRunningException(); } var threadId = Thread.CurrentThread.ManagedThreadId; var mapName = String.Format("{0}{1:x8}", cMapNamePrefix, threadId); using (var mappedFile = MemoryMappedFile.CreateNew(mapName, 4096)) { if (mappedFile.SafeMemoryMappedFileHandle.IsInvalid) { throw new Exception("Invalid mapped file handle"); } using (var stream = mappedFile.CreateViewStream()) { stream.Write(aMessage, 0, aMessage.Length); var copyData = new COPYDATASTRUCT(); if (IntPtr.Size == 4) { copyData.dwData = new IntPtr(unchecked((int)AGENT_COPYDATA_ID)); } else { copyData.dwData = new IntPtr(AGENT_COPYDATA_ID); } copyData.cbData = mapName.Length + 1; copyData.lpData = Marshal.StringToCoTaskMemAnsi(mapName); IntPtr copyDataPtr = Marshal.AllocHGlobal(Marshal.SizeOf(copyData)); Marshal.StructureToPtr(copyData, copyDataPtr, false); var resultPtr = SendMessage(hwnd, WM_COPYDATA, IntPtr.Zero, copyDataPtr); Marshal.FreeHGlobal(copyData.lpData); Marshal.FreeHGlobal(copyDataPtr); if (resultPtr == IntPtr.Zero) { throw new Exception("send message failed"); } stream.Position = 0; var parser = new BlobParser(stream); var replyLength = parser.ReadInt(); stream.Position = 0; var reply = new byte[replyLength + 4]; stream.Read(reply, 0, reply.Length); return reply; } } }
public override object Deserialize(Stream stream) { /* check for required parameters */ if (stream == null) { throw new ArgumentNullException("stream"); } try { var reader = new StreamReader(stream); var firstLine = reader.ReadLine(); if (firstLine != MARK_BEGIN) { throw new KeyFormatterException("Bad file format - does not have expected header."); } var base64String = new StringBuilder(); while (true) { var line = reader.ReadLine(); if (line == MARK_END) { break; } base64String.Append(line); } /* reading unencrypted part */ BlobParser parser = new BlobParser(Util.FromBase64(base64String.ToString())); var magicBytes = parser.ReadBytes((uint)AUTH_MAGIC.Length); if (Encoding.UTF8.GetString(magicBytes) != AUTH_MAGIC) { throw new KeyFormatterException("Bad data - missing AUTH_MAGIC."); } var ciphername = parser.ReadString(); if (ciphername != CIPHERNAME_AES256_CBC && ciphername != CIPHERNAME_NONE) { throw new KeyFormatterException("Unsupported cyphername: " + ciphername); } var kdfname = parser.ReadString(); if (kdfname != KDFNAME_BCRYPT && kdfname != KDFNAME_NONE) { throw new KeyFormatterException("Unsupported kdfname: " + ciphername); } if (kdfname == KDFNAME_NONE && ciphername != CIPHERNAME_NONE) { throw new KeyFormatterException("Invalid format."); } var kdfoptions = parser.ReadBlob(); var keyCount = parser.ReadInt(); if (keyCount != 1) { throw new KeyFormatterException("Only one key allowed."); } var publicKeys = new List<byte[]>(); for (int i = 0; i < keyCount; i++) { publicKeys.Add(parser.ReadBlob()); } var privateKeys = parser.ReadBlob(); if (ciphername == CIPHERNAME_AES256_CBC) { Aes aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.None; if (privateKeys.Length < aes.BlockSize / 8 || privateKeys.Length % (aes.BlockSize / 8) != 0) { throw new KeyFormatterException("Bad private key encrypted length."); } var keyAndIV = new byte[aes.Key.Length + aes.IV.Length]; if (kdfname == KDFNAME_BCRYPT) { var kdfOptionsParser = new BlobParser(kdfoptions); var salt = kdfOptionsParser.ReadBlob(); var rounds = kdfOptionsParser.ReadInt(); var passphrase = GetPassphraseCallbackMethod(null); var passphraseChars = new char[passphrase.Length]; IntPtr passphrasePtr = Marshal.SecureStringToGlobalAllocUnicode(passphrase); for (int i = 0; i < passphrase.Length; i++) { passphraseChars[i] = (char)Marshal.ReadInt16(passphrasePtr, i * 2); } Marshal.ZeroFreeGlobalAllocUnicode(passphrasePtr); BCrypt.HashUsingOpensshBCryptPbkdf(passphraseChars, salt, ref keyAndIV, rounds); Array.Clear(passphraseChars, 0, passphraseChars.Length); } // Array.Copy to aes.Key and aes.IV directly does not work! var key = new byte[aes.Key.Length]; Array.Copy(keyAndIV, key, key.Length); aes.Key = key; var iv = new byte[aes.IV.Length]; Array.Copy(keyAndIV, key.Length, iv, 0, iv.Length); aes.IV = iv; using (ICryptoTransform decryptor = aes.CreateDecryptor()) { privateKeys = Util.GenericTransform(decryptor, privateKeys); } aes.Clear(); } parser = new BlobParser(privateKeys); var checkint1 = parser.ReadInt(); var checkint2 = parser.ReadInt(); if (checkint1 != checkint2) { throw new KeyFormatterException("checkint does not match in private key."); } var keys = new List<SshKey>(); for (int i = 0; i < keyCount; i++) { var publicKey = parser.ReadSsh2PublicKeyData(); var keyPair = parser.ReadSsh2KeyData(publicKey); var comment = parser.ReadString(); var key = new SshKey(SshVersion.SSH2, keyPair, comment); keys.Add(key); } return keys[0]; } catch (KeyFormatterException) { throw; } catch (Exception ex) { throw new KeyFormatterException("see inner exception", ex); } }
/// <summary> /// reads OpenSSH formatted public key blob and creates /// an AsymmetricKeyParameter object /// </summary> /// <returns>AsymmetricKeyParameter containing the public key</returns> public AsymmetricKeyParameter ReadSsh2PublicKeyData(out OpensshCertificate cert) { cert = null; var algorithm = Encoding.UTF8.GetString(ReadBlob()); var certBuilder = new BlobBuilder(); certBuilder.AddStringBlob(algorithm); switch (algorithm) { case PublicKeyAlgorithmExt.ALGORITHM_RSA_KEY: { var n = new BigInteger(1, ReadBlob()); // modulus var e = new BigInteger(1, ReadBlob()); // exponent if (n.BitLength < e.BitLength) { // In some cases, the modulus is first. We can always tell because // it is significantly larget than the exponent. return(new RsaKeyParameters(false, e, n)); } return(new RsaKeyParameters(false, n, e)); } case PublicKeyAlgorithmExt.ALGORITHM_RSA_CERT_V1: { var nonce = ReadBlob(); if (nonce.Length != 32) { // we are being called from SSH2_AGENTC_ADD_IDENTITY and this blob // is the whole certificate, not the nonce var certParser = new BlobParser(nonce); return(certParser.ReadSsh2PublicKeyData(out cert)); } else { certBuilder.AddBlob(nonce); var e = new BigInteger(1, ReadBlob()); certBuilder.AddBigIntBlob(e); var n = new BigInteger(1, ReadBlob()); certBuilder.AddBigIntBlob(n); cert = ReadCertificate(certBuilder); return(new RsaKeyParameters(false, n, e)); } } case PublicKeyAlgorithmExt.ALGORITHM_DSA_KEY: { var p = new BigInteger(1, ReadBlob()); var q = new BigInteger(1, ReadBlob()); var g = new BigInteger(1, ReadBlob()); var y = new BigInteger(1, ReadBlob()); var dsaParams = new DsaParameters(p, q, g); return(new DsaPublicKeyParameters(y, dsaParams)); } case PublicKeyAlgorithmExt.ALGORITHM_DSA_CERT_V1: { var nonce = ReadBlob(); if (nonce.Length != 32) { // we are being called from SSH2_AGENTC_ADD_IDENTITY and this blob // is the whole certificate, not the nonce var certParser = new BlobParser(nonce); return(certParser.ReadSsh2PublicKeyData(out cert)); } else { certBuilder.AddBlob(nonce); var p = new BigInteger(1, ReadBlob()); certBuilder.AddBigIntBlob(p); var q = new BigInteger(1, ReadBlob()); certBuilder.AddBigIntBlob(q); var g = new BigInteger(1, ReadBlob()); certBuilder.AddBigIntBlob(g); var y = new BigInteger(1, ReadBlob()); certBuilder.AddBigIntBlob(y); cert = ReadCertificate(certBuilder); var dsaParams = new DsaParameters(p, q, g); return(new DsaPublicKeyParameters(y, dsaParams)); } } case PublicKeyAlgorithmExt.ALGORITHM_ECDSA_SHA2_NISTP256_KEY: case PublicKeyAlgorithmExt.ALGORITHM_ECDSA_SHA2_NISTP384_KEY: case PublicKeyAlgorithmExt.ALGORITHM_ECDSA_SHA2_NISTP521_KEY: { var curveName = ReadString(); var publicKey = ReadBlob(); var x9Params = SecNamedCurves.GetByName(EcCurveToAlgorithm(curveName)); var domainParams = new ECDomainParameters(x9Params.Curve, x9Params.G, x9Params.N, x9Params.H); var point = x9Params.Curve.DecodePoint(publicKey); return(new ECPublicKeyParameters(point, domainParams)); } case PublicKeyAlgorithmExt.ALGORITHM_ECDSA_SHA2_NISTP256_CERT_V1: case PublicKeyAlgorithmExt.ALGORITHM_ECDSA_SHA2_NISTP384_CERT_V1: case PublicKeyAlgorithmExt.ALGORITHM_ECDSA_SHA2_NISTP521_CERT_V1: { var nonce = ReadBlob(); if (nonce.Length != 32) { // we are being called from SSH2_AGENTC_ADD_IDENTITY and this blob // is the whole certificate, not the nonce var certParser = new BlobParser(nonce); return(certParser.ReadSsh2PublicKeyData(out cert)); } else { certBuilder.AddBlob(nonce); var curveName = ReadString(); certBuilder.AddStringBlob(curveName); var publicKey = ReadBlob(); certBuilder.AddBlob(publicKey); cert = ReadCertificate(certBuilder); var x9Params = SecNamedCurves.GetByName(EcCurveToAlgorithm(curveName)); var domainParams = new ECDomainParameters(x9Params.Curve, x9Params.G, x9Params.N, x9Params.H); var point = x9Params.Curve.DecodePoint(publicKey); return(new ECPublicKeyParameters(point, domainParams)); } } case PublicKeyAlgorithmExt.ALGORITHM_ED25519: { var publicKey = ReadBlob(); return(new Ed25519PublicKeyParameter(publicKey)); } case PublicKeyAlgorithmExt.ALGORITHM_ED25519_CERT_V1: { var nonce = ReadBlob(); if (nonce.Length != 32) { // we are being called from SSH2_AGENTC_ADD_IDENTITY and this blob // is the whole certificate, not the nonce var certParser = new BlobParser(nonce); certParser.ReadSsh2PublicKeyData(out cert); var publicKey = ReadBlob(); return(new Ed25519PublicKeyParameter(publicKey)); } else { certBuilder.AddBlob(nonce); var publicKey = ReadBlob(); certBuilder.AddBlob(publicKey); cert = ReadCertificate(certBuilder); return(new Ed25519PublicKeyParameter(publicKey)); } } default: // unsupported encryption algorithm throw new Exception("Unsupported algorithm"); } }
/// <summary> /// Answers the message. /// </summary> /// <param name='messageStream'>Message stream.</param> /// <param name="process">The calling process or <c>null</c> if the process /// could not be obtained.</param> /// <remarks>code based on winpgnt.c from PuTTY source code</remarks> public void AnswerMessage(Stream messageStream, Process process = null) { if (messageStream.CanTimeout) { messageStream.ReadTimeout = 5000; } var messageParser = new BlobParser(messageStream); var responseBuilder = new BlobBuilder(); BlobHeader header; try { header = messageParser.ReadHeader(); if (MessageReceived != null) { var eventArgs = new MessageReceivedEventArgs(header); MessageReceived(this, eventArgs); if (eventArgs.Fail) { throw new Exception (); } } } catch (Exception) { header = new BlobHeader(); header.Message = Message.UNKNOWN; // this will cause the switch statement below to use the default case // which returns an error to the stream. } switch (header.Message) { case Message.SSH1_AGENTC_REQUEST_RSA_IDENTITIES: /* * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER. */ try { if (header.BlobLength > 1) { // ruby net-ssh tries to send a SSH2_AGENT_REQUEST_VERSION message // which has the same id number as SSH1_AGENTC_REQUEST_RSA_IDENTITIES // with a string tacked on. We need to read the string from the // stream, but it is not used for anything. messageParser.ReadString (); } var keyList = ListKeys(SshVersion.SSH1); if (FilterKeyListCallback != null) { keyList = FilterKeyListCallback(keyList); } foreach (SshKey key in keyList) { responseBuilder.AddBytes(key.GetPublicKeyBlob()); responseBuilder.AddStringBlob(key.Comment); } responseBuilder.InsertHeader(Message.SSH1_AGENT_RSA_IDENTITIES_ANSWER, keyList.Count); // TODO may want to check that there is enough room in the message stream break; // succeeded } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH2_AGENTC_REQUEST_IDENTITIES: /* * Reply with SSH2_AGENT_IDENTITIES_ANSWER. */ try { var keyList = ListKeys(SshVersion.SSH2); if (FilterKeyListCallback != null) { keyList = FilterKeyListCallback(keyList); } foreach (SshKey key in keyList) { responseBuilder.AddBlob(key.GetPublicKeyBlob()); responseBuilder.AddStringBlob(key.Comment); } responseBuilder.InsertHeader(Message.SSH2_AGENT_IDENTITIES_ANSWER, keyList.Count); // TODO may want to check that there is enough room in the message stream break; // succeeded } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH1_AGENTC_RSA_CHALLENGE: /* * Reply with either SSH1_AGENT_RSA_RESPONSE or * SSH_AGENT_FAILURE, depending on whether we have that key * or not. */ try { //Reading publicKey information var publicKeyParams = messageParser.ReadSsh1PublicKeyData(true); //Searching for Key here var matchingKey = mKeyList.Where(key => key.Version == SshVersion.SSH1 && (key.GetPublicKeyParameters().Equals(publicKeyParams))).Single(); //Reading challenge var encryptedChallenge = messageParser.ReadSsh1BigIntBlob(); var sessionId = messageParser.ReadBytes(16); //Checking responseType field if (messageParser.ReadInt() != 1) { goto default; //responseType !=1 is not longer supported } //Answering to the challenge var engine = new Pkcs1Encoding(new RsaEngine()); engine.Init(false /* decrypt */, matchingKey.GetPrivateKeyParameters()); var decryptedChallenge = engine.ProcessBlock(encryptedChallenge, 0, encryptedChallenge.Length); using (MD5 md5 = MD5.Create()) { var md5Buffer = new byte[48]; decryptedChallenge.CopyTo(md5Buffer, 0); sessionId.CopyTo(md5Buffer, 32); responseBuilder.AddBytes(md5.ComputeHash(md5Buffer)); responseBuilder.InsertHeader(Message.SSH1_AGENT_RSA_RESPONSE); break; } } catch (InvalidOperationException) { // this is expected if there is not a matching key } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH2_AGENTC_SIGN_REQUEST: /* * Reply with either SSH2_AGENT_SIGN_RESPONSE or SSH_AGENT_FAILURE, * depending on whether we have that key or not. */ try { var keyBlob = messageParser.ReadBlob(); var reqData = messageParser.ReadBlob(); var flags = new SignRequestFlags(); try { // usually, there are no flags, so parser will throw flags = (SignRequestFlags)messageParser.ReadInt(); } catch { } var matchingKey = mKeyList.Where(key => key.Version == SshVersion.SSH2 && key.GetPublicKeyBlob().SequenceEqual(keyBlob)).First(); var confirmConstraints = matchingKey.Constraints .Where(constraint => constraint.Type == KeyConstraintType.SSH_AGENT_CONSTRAIN_CONFIRM); if (confirmConstraints.Count() > 0) { if (!ConfirmUserPermissionCallback.Invoke(matchingKey, process)) { goto default; } } /* create signature */ var signKey = matchingKey; var signer = signKey.GetSigner(); var algName = signKey.Algorithm.GetIdentifierString(); signer.Init(true, signKey.GetPrivateKeyParameters()); signer.BlockUpdate(reqData, 0, reqData.Length); byte[] signature = signer.GenerateSignature(); signature = signKey.FormatSignature(signature); BlobBuilder signatureBuilder = new BlobBuilder(); if (!flags.HasFlag(SignRequestFlags.SSH_AGENT_OLD_SIGNATURE)) { signatureBuilder.AddStringBlob(algName); } signatureBuilder.AddBlob(signature); responseBuilder.AddBlob(signatureBuilder.GetBlob()); responseBuilder.InsertHeader(Message.SSH2_AGENT_SIGN_RESPONSE); try { KeyUsed(this, new KeyUsedEventArgs(signKey, process)); } catch { } break; // succeeded } catch (InvalidOperationException) { // this is expected if there is not a matching key } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failure case Message.SSH1_AGENTC_ADD_RSA_IDENTITY: case Message.SSH1_AGENTC_ADD_RSA_ID_CONSTRAINED: /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ if (IsLocked) { goto default; } bool ssh1constrained = (header.Message == Message.SSH1_AGENTC_ADD_RSA_ID_CONSTRAINED); try { var publicKeyParams = messageParser.ReadSsh1PublicKeyData(false); var keyPair = messageParser.ReadSsh1KeyData(publicKeyParams); SshKey key = new SshKey(SshVersion.SSH1, keyPair); key.Comment = messageParser.ReadString(); key.Source = "External client"; if (ssh1constrained) { while (messageStream.Position < header.BlobLength + 4) { KeyConstraint constraint = new KeyConstraint(); constraint.Type = (KeyConstraintType)messageParser.ReadByte(); if (constraint.Type == KeyConstraintType.SSH_AGENT_CONSTRAIN_LIFETIME) { constraint.Data = messageParser.ReadInt(); } key.AddConstraint(constraint); } } AddKey(key); responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; } catch (CallbackNullException) { // this is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH2_AGENTC_ADD_IDENTITY: case Message.SSH2_AGENTC_ADD_ID_CONSTRAINED: /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ if (IsLocked) { goto default; } bool constrained = (header.Message == Message.SSH2_AGENTC_ADD_ID_CONSTRAINED); try { var publicKeyParams = messageParser.ReadSsh2PublicKeyData(); var keyPair = messageParser.ReadSsh2KeyData(publicKeyParams); SshKey key = new SshKey(SshVersion.SSH2, keyPair); key.Comment = messageParser.ReadString(); key.Source = "External client"; if (constrained) { while (messageStream.Position < header.BlobLength + 4) { KeyConstraint constraint = new KeyConstraint(); constraint.Type = (KeyConstraintType)messageParser.ReadByte(); if (constraint.Type == KeyConstraintType.SSH_AGENT_CONSTRAIN_LIFETIME) { constraint.Data = messageParser.ReadInt(); } key.AddConstraint(constraint); } } AddKey(key); responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; // success! } catch (CallbackNullException) { // this is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH1_AGENTC_REMOVE_RSA_IDENTITY: case Message.SSH2_AGENTC_REMOVE_IDENTITY: /* * Remove from the list and return SSH_AGENT_SUCCESS, or * perhaps SSH_AGENT_FAILURE if it wasn't in the list to * start with. */ if (IsLocked) { goto default; } SshVersion removeVersion; byte[] rKeyBlob; if (header.Message == Message.SSH1_AGENTC_REMOVE_RSA_IDENTITY) { removeVersion = SshVersion.SSH1; rKeyBlob = messageParser.ReadBytes(header.BlobLength - 1); } else if (header.Message == Message.SSH2_AGENTC_REMOVE_IDENTITY) { removeVersion = SshVersion.SSH2; rKeyBlob = messageParser.ReadBlob(); } else { Debug.Fail("Should not get here."); goto default; } try { var matchingKey = mKeyList.Get(removeVersion, rKeyBlob); var startKeyListLength = mKeyList.Count; RemoveKey(matchingKey); // only succeed if key was removed if (mKeyList.Count == startKeyListLength - 1) { responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; //success! } } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES: case Message.SSH2_AGENTC_REMOVE_ALL_IDENTITIES: /* * Remove all SSH-1 or SSH-2 keys. */ if (IsLocked) { goto default; } SshVersion removeAllVersion; if (header.Message == Message.SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES) { removeAllVersion = SshVersion.SSH1; } else if (header.Message == Message.SSH2_AGENTC_REMOVE_ALL_IDENTITIES) { removeAllVersion = SshVersion.SSH2; } else { Debug.Fail("Should not get here."); goto default; } try { RemoveAllKeys(removeAllVersion); responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; //success! } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH_AGENTC_LOCK: try { var passphrase = new PinnedArray<byte>(messageParser.ReadBlob()); try { Lock(passphrase.Data); } finally { passphrase.Clear(); } if (IsLocked) { responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; } } catch (AgentLockedException) { // This is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; case Message.SSH_AGENTC_UNLOCK: try { var passphrase = new PinnedArray<byte>(messageParser.ReadBlob()); try { Unlock(passphrase.Data); } finally { passphrase.Clear(); } if (!IsLocked) { responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; } } catch (AgentLockedException) { // This is expected } catch (PassphraseException) { // This is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; default: responseBuilder.Clear(); responseBuilder.InsertHeader(Message.SSH_AGENT_FAILURE); break; } /* write response to stream */ if (messageStream.CanSeek) messageStream.Position = 0; messageStream.Write(responseBuilder.GetBlob(), 0, responseBuilder.Length); messageStream.Flush(); }
public void TestAnswerSSH2_AGENTC_SIGN_REQUEST() { const string signatureData = "this is the data that gets signed"; byte[] signatureDataBytes = Encoding.UTF8.GetBytes(signatureData); BlobBuilder builder = new BlobBuilder(); Agent agent = new TestAgent(allKeys); Agent.BlobHeader header; byte[] signatureBlob; BlobParser signatureParser; string algorithm; byte[] signature; ISigner signer; bool signatureOk; BigInteger r, s; DerSequence seq; /* test signatures */ foreach (ISshKey key in allKeys.Where(key => key.Version == SshVersion.SSH2)) { builder.Clear(); builder.AddBlob(key.GetPublicKeyBlob()); builder.AddStringBlob(signatureData); builder.InsertHeader(Agent.Message.SSH2_AGENTC_SIGN_REQUEST); PrepareMessage(builder); agent.AnswerMessage(stream); RewindStream(); /* check that proper response type was received */ header = parser.ReadHeader(); Assert.That(header.Message, Is.EqualTo(Agent.Message.SSH2_AGENT_SIGN_RESPONSE)); signatureBlob = parser.ReadBlob(); signatureParser = new BlobParser(signatureBlob); algorithm = signatureParser.ReadString(); Assert.That(algorithm, Is.EqualTo(key.Algorithm.GetIdentifierString())); signature = signatureParser.ReadBlob(); if (key.Algorithm == PublicKeyAlgorithm.SSH_RSA) { Assert.That(signature.Length == key.Size / 8); } else if (key.Algorithm == PublicKeyAlgorithm.SSH_DSS) { Assert.That(signature.Length, Is.EqualTo(40)); r = new BigInteger(1, signature, 0, 20); s = new BigInteger(1, signature, 20, 20); seq = new DerSequence(new DerInteger(r), new DerInteger(s)); signature = seq.GetDerEncoded(); } else if (key.Algorithm == PublicKeyAlgorithm.ECDSA_SHA2_NISTP256 || key.Algorithm == PublicKeyAlgorithm.ECDSA_SHA2_NISTP384 || key.Algorithm == PublicKeyAlgorithm.ECDSA_SHA2_NISTP521) { Assert.That(signature.Length, Is.AtLeast(key.Size / 4 + 8)); Assert.That(signature.Length, Is.AtMost(key.Size / 4 + 10)); BlobParser sigParser = new BlobParser(signature); r = new BigInteger(sigParser.ReadBlob()); s = new BigInteger(sigParser.ReadBlob()); seq = new DerSequence(new DerInteger(r), new DerInteger(s)); signature = seq.GetDerEncoded(); } else if (key.Algorithm == PublicKeyAlgorithm.ED25519) { Assert.That(signature.Length, Is.EqualTo(64)); } signer = key.GetSigner(); signer.Init(false, key.GetPublicKeyParameters()); signer.BlockUpdate(signatureDataBytes, 0, signatureDataBytes.Length); signatureOk = signer.VerifySignature(signature); Assert.That(signatureOk, Is.True, "invalid signature"); Assert.That(header.BlobLength, Is.EqualTo(stream.Position - 4)); } /* test DSA key old signature format */ builder.Clear(); builder.AddBlob(dsaKey.GetPublicKeyBlob()); builder.AddStringBlob(signatureData); builder.AddInt((uint)Agent.SignRequestFlags.SSH_AGENT_OLD_SIGNATURE); builder.InsertHeader(Agent.Message.SSH2_AGENTC_SIGN_REQUEST); PrepareMessage(builder); agent.AnswerMessage(stream); RewindStream(); header = parser.ReadHeader(); Assert.That(header.Message, Is.EqualTo(Agent.Message.SSH2_AGENT_SIGN_RESPONSE)); signatureBlob = parser.ReadBlob(); signatureParser = new BlobParser(signatureBlob); signature = signatureParser.ReadBlob(); Assert.That(signature.Length == 40); r = new BigInteger(1, signature, 0, 20); s = new BigInteger(1, signature, 20, 20); seq = new DerSequence(new DerInteger(r), new DerInteger(s)); signature = seq.GetDerEncoded(); signer = dsaKey.GetSigner(); signer.Init(false, dsaKey.GetPublicKeyParameters()); signer.BlockUpdate(signatureDataBytes, 0, signatureDataBytes.Length); signatureOk = signer.VerifySignature(signature); Assert.That(signatureOk, Is.True, "invalid signature"); Assert.That(header.BlobLength, Is.EqualTo(stream.Position - 4)); /* test key not found */ agent = new TestAgent(); builder.Clear(); builder.AddBlob(dsaKey.GetPublicKeyBlob()); builder.AddStringBlob(signatureData); builder.InsertHeader(Agent.Message.SSH2_AGENTC_SIGN_REQUEST); PrepareMessage(builder); agent.AnswerMessage(stream); RewindStream(); Agent.BlobHeader header2 = parser.ReadHeader(); Assert.That(header2.BlobLength, Is.EqualTo(1)); Assert.That(header2.Message, Is.EqualTo(Agent.Message.SSH_AGENT_FAILURE)); /* test confirm constraint */ agent = new TestAgent(); Agent.KeyConstraint testConstraint = new Agent.KeyConstraint(); testConstraint.Type = Agent.KeyConstraintType.SSH_AGENT_CONSTRAIN_CONFIRM; SshKey testKey = dsaKey.Clone(); bool confirmCallbackReturnValue = false; agent.ConfirmUserPermissionCallback = delegate(ISshKey k, Process p) { return confirmCallbackReturnValue; }; testKey.AddConstraint(testConstraint); agent.AddKey(testKey); builder.Clear(); builder.AddBlob(dsaKey.GetPublicKeyBlob()); builder.AddStringBlob(signatureData); builder.InsertHeader(Agent.Message.SSH2_AGENTC_SIGN_REQUEST); PrepareMessage(builder); agent.AnswerMessage(stream); RewindStream(); header2 = parser.ReadHeader(); Assert.That(header2.BlobLength, Is.EqualTo(1)); Assert.That(header2.Message, Is.EqualTo(Agent.Message.SSH_AGENT_FAILURE)); confirmCallbackReturnValue = true; PrepareMessage(builder); agent.AnswerMessage(stream); RewindStream(); header2 = parser.ReadHeader(); Assert.That(header2.BlobLength, Is.Not.EqualTo(1)); Assert.That(header2.Message, Is.EqualTo(Agent.Message.SSH2_AGENT_SIGN_RESPONSE)); }
public override object Deserialize(Stream stream) { /* check for required parameters */ if (stream == null) { throw new ArgumentNullException("stream"); } try { var reader = new StreamReader(stream); var firstLine = reader.ReadLine(); if (firstLine != MARK_BEGIN) { throw new KeyFormatterException("Bad file format - does not have expected header."); } var base64String = new StringBuilder(); while (true) { var line = reader.ReadLine(); if (line == MARK_END) { break; } base64String.Append(line); } /* reading unencrypted part */ BlobParser parser = new BlobParser(Util.FromBase64(base64String.ToString())); var magicBytes = parser.ReadBytes((uint)AUTH_MAGIC.Length); if (Encoding.UTF8.GetString(magicBytes) != AUTH_MAGIC) { throw new KeyFormatterException("Bad data - missing AUTH_MAGIC."); } var ciphername = parser.ReadString(); if (!IsSupportCipher(ciphername)) { throw new KeyFormatterException("Unsupported cyphername: " + ciphername); } var kdfname = parser.ReadString(); if (kdfname != KDFNAME_BCRYPT && kdfname != KDFNAME_NONE) { throw new KeyFormatterException("Unsupported kdfname: " + ciphername); } if (kdfname == KDFNAME_NONE && ciphername != CIPHERNAME_NONE) { throw new KeyFormatterException("Invalid format."); } var kdfoptions = parser.ReadBlob(); var keyCount = parser.ReadUInt32(); if (keyCount != 1) { throw new KeyFormatterException("Only one key allowed."); } var publicKeys = new List <byte[]>(); for (int i = 0; i < keyCount; i++) { publicKeys.Add(parser.ReadBlob()); } var privateKeys = parser.ReadBlob(); var keyAndIV = new byte[32 + 16]; if (kdfname == KDFNAME_BCRYPT) { var kdfOptionsParser = new BlobParser(kdfoptions); var salt = kdfOptionsParser.ReadBlob(); var rounds = kdfOptionsParser.ReadUInt32(); var passphrase = GetPassphraseCallbackMethod(null); var passphraseChars = new char[passphrase.Length]; var passphrasePtr = Marshal.SecureStringToGlobalAllocUnicode(passphrase); for (int i = 0; i < passphrase.Length; i++) { passphraseChars[i] = (char)Marshal.ReadInt16(passphrasePtr, i * 2); } Marshal.ZeroFreeGlobalAllocUnicode(passphrasePtr); BCrypt.HashUsingOpensshBCryptPbkdf(passphraseChars, salt, ref keyAndIV, rounds); Array.Clear(passphraseChars, 0, passphraseChars.Length); } var key = new byte[32]; Array.Copy(keyAndIV, key, key.Length); var iv = new byte[16]; Array.Copy(keyAndIV, key.Length, iv, 0, iv.Length); switch (ciphername) { case CIPHERNAME_AES256_CBC: var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.None; aes.Key = key; aes.IV = iv; if (privateKeys.Length < aes.BlockSize / 8 || privateKeys.Length % (aes.BlockSize / 8) != 0) { throw new KeyFormatterException("Bad private key encrypted length."); } using (ICryptoTransform decryptor = aes.CreateDecryptor()) { privateKeys = Util.GenericTransform(decryptor, privateKeys); } aes.Clear(); break; case CIPHERNAME_AES256_CTR: var ctrCipher = CipherUtilities.GetCipher("AES/CTR/NoPadding"); ctrCipher.Init(false, new ParametersWithIV(new KeyParameter(key), iv)); privateKeys = ctrCipher.DoFinal(privateKeys); break; } parser = new BlobParser(privateKeys); var checkint1 = parser.ReadUInt32(); var checkint2 = parser.ReadUInt32(); if (checkint1 != checkint2) { throw new KeyFormatterException("checkint does not match in private key."); } var keys = new List <SshKey>(); for (int i = 0; i < keyCount; i++) { OpensshCertificate cert; var publicKey = parser.ReadSsh2PublicKeyData(out cert); var keyPair = parser.ReadSsh2KeyData(publicKey); var comment = parser.ReadString(); var sshKey = new SshKey(SshVersion.SSH2, keyPair, comment, cert); keys.Add(sshKey); } return(keys[0]); } catch (KeyFormatterException) { throw; } catch (Exception ex) { throw new KeyFormatterException("see inner exception", ex); } }
private static AsymmetricCipherKeyPair CreateCipherKeyPair( PublicKeyAlgorithm algorithm, byte[] publicKeyBlob, byte[] privateKeyBlob) { var parser = new BlobParser(publicKeyBlob); var publicKey = parser.ReadSsh2PublicKeyData(); parser = new BlobParser(privateKeyBlob); switch (algorithm) { case PublicKeyAlgorithm.SSH_RSA: var rsaPublicKeyParams = (RsaKeyParameters)publicKey; var d = new BigInteger(1, parser.ReadBlob()); var p = new BigInteger(1, parser.ReadBlob()); var q = new BigInteger(1, parser.ReadBlob()); var inverseQ = new BigInteger(1, parser.ReadBlob()); /* compute missing parameters */ var dp = d.Remainder(p.Subtract(BigInteger.One)); var dq = d.Remainder(q.Subtract(BigInteger.One)); RsaPrivateCrtKeyParameters rsaPrivateKeyParams = new RsaPrivateCrtKeyParameters(rsaPublicKeyParams.Modulus, rsaPublicKeyParams.Exponent, d, p, q, dp, dq, inverseQ); return new AsymmetricCipherKeyPair(rsaPublicKeyParams, rsaPrivateKeyParams); case PublicKeyAlgorithm.SSH_DSS: var dsaPublicKeyParams = (DsaPublicKeyParameters)publicKey; var x = new BigInteger(1, parser.ReadBlob()); DsaPrivateKeyParameters dsaPrivateKeyParams = new DsaPrivateKeyParameters(x, dsaPublicKeyParams.Parameters); return new AsymmetricCipherKeyPair(dsaPublicKeyParams, dsaPrivateKeyParams); case PublicKeyAlgorithm.ED25519: var ed25596PublicKey = (Ed25519PublicKeyParameter)publicKey; byte[] privBlob = parser.ReadBlob(); byte[] privSig = new byte[64]; // OpenSSH's "private key" is actually the private key with the public key tacked on ... Array.Copy(privBlob, 0, privSig, 0, 32); Array.Copy(ed25596PublicKey.Key, 0, privSig, 32, 32); var ed25596PrivateKey = new Ed25519PrivateKeyParameter(privSig); return new AsymmetricCipherKeyPair(ed25596PublicKey, ed25596PrivateKey); case PublicKeyAlgorithm.ECDSA_SHA2_NISTP256: case PublicKeyAlgorithm.ECDSA_SHA2_NISTP384: case PublicKeyAlgorithm.ECDSA_SHA2_NISTP521: var ecPublicKeyParams = (ECPublicKeyParameters)publicKey; var ecdsaPrivate = new BigInteger(1, parser.ReadBlob()); ECPrivateKeyParameters ecPrivateKeyParams = new ECPrivateKeyParameters(ecdsaPrivate, ecPublicKeyParams.Parameters); return new AsymmetricCipherKeyPair(ecPublicKeyParams, ecPrivateKeyParams); default: // unsupported encryption algorithm throw new PpkFormatterException(PpkFormatterException.PpkErrorType.PublicKeyEncryption); } }
public override object Deserialize(Stream aStream) { /* check for required parameters */ if (aStream == null) { throw new ArgumentNullException("aStream"); } /* reading unencrypted part */ BlobParser parser = new BlobParser(aStream); parser.ReadBytes((uint)FILE_HEADER_LINE.Length + 2); //Skipping header line byte cipherType = parser.ReadUInt8(); if (cipherType != SSH_CIPHER_3DES && cipherType != SSH_CIPHER_NONE) { //TripleDes is the only encryption supported throw new KeyFormatterException("Unsupported cypherType: " + cipherType); } parser.ReadUInt32(); //reserved /* reading public key */ AsymmetricKeyParameter aPublicKeyParameter = parser.ReadSsh1PublicKeyData(false); String keyComment = parser.ReadString(); /* reading private key */ byte[] inputBuffer = new byte[aStream.Length]; aStream.Read(inputBuffer, 0, inputBuffer.Length); byte[] ouputBuffer; try { if (cipherType == 3) { /* private key is 3DES encrypted */ PasswordFinder pwFinder = null; if (GetPassphraseCallbackMethod != null) { pwFinder = new PasswordFinder(GetPassphraseCallbackMethod); } byte[] keydata; try { using (MD5 md5 = MD5.Create()) { char[] md5Buffer = pwFinder.GetPassword(); keydata = md5.ComputeHash(Encoding.ASCII.GetBytes(md5Buffer)); } } catch (PasswordException ex) { if (GetPassphraseCallbackMethod == null) { throw new CallbackNullException(); } throw new KeyFormatterException("see inner exception", ex); } /* decryption */ DesSsh1Engine desEngine = new DesSsh1Engine(); desEngine.Init(false, new KeyParameter(keydata)); BufferedBlockCipher bufferedBlockCipher = new BufferedBlockCipher(desEngine); ouputBuffer = bufferedBlockCipher.ProcessBytes(inputBuffer); } else { /* private key is stored in plain text */ ouputBuffer = inputBuffer; } var privateKeyParser = new BlobParser(ouputBuffer); /* checking result of decryption */ byte[] resultCheck = privateKeyParser.ReadBytes(4); if (resultCheck[0] != resultCheck[2] || resultCheck[1] != resultCheck[3]) { throw new KeyFormatterException("bad passphrase"); } /* reading private key */ var keyPair = privateKeyParser.ReadSsh1KeyData(aPublicKeyParameter); SshKey key = new SshKey(SshVersion.SSH1, keyPair); key.Comment = keyComment; return(key); } catch (KeyFormatterException) { throw; } catch (Exception ex) { throw new KeyFormatterException("see inner exception", ex); } }
public ICollection<ISshKey> ListKeys(SshVersion aVersion) { BlobBuilder builder = new BlobBuilder(); switch (aVersion) { case SshVersion.SSH1: builder.InsertHeader(Agent.Message.SSH1_AGENTC_REQUEST_RSA_IDENTITIES); break; case SshVersion.SSH2: builder.InsertHeader(Agent.Message.SSH2_AGENTC_REQUEST_IDENTITIES); break; default: throw new Exception(cUnsupportedSshVersion); } BlobParser replyParser = SendMessage(builder); var keyCollection = new List<ISshKey>(); var header = replyParser.ReadHeader(); switch (aVersion) { case SshVersion.SSH1: if (header.Message != Agent.Message.SSH1_AGENT_RSA_IDENTITIES_ANSWER) { throw new AgentFailureException(); } var ssh1KeyCount = replyParser.ReadInt(); for (var i = 0; i < ssh1KeyCount; i++) { var publicKeyParams = replyParser.ReadSsh1PublicKeyData(true); var comment = replyParser.ReadString(); keyCollection.Add( new SshKey(SshVersion.SSH1, publicKeyParams, null, comment)); } break; case SshVersion.SSH2: if (header.Message != Agent.Message.SSH2_AGENT_IDENTITIES_ANSWER) { throw new AgentFailureException(); } var ssh2KeyCount = replyParser.ReadInt(); for (var i = 0; i < ssh2KeyCount; i++) { var publicKeyBlob = replyParser.ReadBlob(); var publicKeyParser = new BlobParser(publicKeyBlob); var publicKeyParams = publicKeyParser.ReadSsh2PublicKeyData(); var comment = replyParser.ReadString(); keyCollection.Add( new SshKey(SshVersion.SSH2, publicKeyParams, null, comment)); } break; default: throw new Exception(cUnsupportedSshVersion); } return keyCollection; }
static AgentTest() { buffer = new byte[4096]; stream = new MemoryStream(buffer); parser = new BlobParser(stream); rsa1Key = KeyGenerator.CreateKey(SshVersion.SSH1, PublicKeyAlgorithm.SSH_RSA, "SSH1 RSA test key"); rsaKey = KeyGenerator.CreateKey(SshVersion.SSH2, PublicKeyAlgorithm.SSH_RSA, "SSH2 RSA test key"); dsaKey = KeyGenerator.CreateKey(SshVersion.SSH2, PublicKeyAlgorithm.SSH_DSS, "SSH2 DSA test key"); ecdsa256Key = KeyGenerator.CreateKey(SshVersion.SSH2, PublicKeyAlgorithm.ECDSA_SHA2_NISTP256, "SSH2 ECDSA 256 test key"); ecdsa384Key = KeyGenerator.CreateKey(SshVersion.SSH2, PublicKeyAlgorithm.ECDSA_SHA2_NISTP384, "SSH2 ECDSA 384 test key"); ecdsa521Key = KeyGenerator.CreateKey(SshVersion.SSH2, PublicKeyAlgorithm.ECDSA_SHA2_NISTP521, "SSH2 ECDSA 521 test key"); ed25519Key = KeyGenerator.CreateKey(SshVersion.SSH2, PublicKeyAlgorithm.ED25519, "SSH2 ED25519 test key"); List<ISshKey> allKeysList = new List<ISshKey>(); allKeysList.Add(rsa1Key); allKeysList.Add(rsaKey); allKeysList.Add(dsaKey); allKeysList.Add(ecdsa256Key); allKeysList.Add(ecdsa384Key); allKeysList.Add(ecdsa521Key); allKeysList.Add(ed25519Key); allKeys = allKeysList.AsReadOnly(); }
/// <summary> /// Answers the message. /// </summary> /// <param name='messageStream'>Message stream.</param> /// <param name="process">The calling process or <c>null</c> if the process /// could not be obtained.</param> /// <remarks>code based on winpgnt.c from PuTTY source code</remarks> public void AnswerMessage(Stream messageStream, Process process = null) { if (messageStream.CanTimeout) { messageStream.ReadTimeout = 5000; } var messageParser = new BlobParser(messageStream); var responseBuilder = new BlobBuilder(); BlobHeader header; try { header = messageParser.ReadHeader(); if (MessageReceived != null) { var eventArgs = new MessageReceivedEventArgs(header); MessageReceived(this, eventArgs); if (eventArgs.Fail) { throw new Exception(); } } } catch (Exception) { header = new BlobHeader(); header.Message = Message.UNKNOWN; // this will cause the switch statement below to use the default case // which returns an error to the stream. } switch (header.Message) { case Message.SSH1_AGENTC_REQUEST_RSA_IDENTITIES: /* * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER. */ try { if (header.BlobLength > 1) { // ruby net-ssh tries to send a SSH2_AGENT_REQUEST_VERSION message // which has the same id number as SSH1_AGENTC_REQUEST_RSA_IDENTITIES // with a string tacked on. We need to read the string from the // stream, but it is not used for anything. messageParser.ReadString(); } var keyList = ListKeys(SshVersion.SSH1); if (FilterKeyListCallback != null) { keyList = FilterKeyListCallback(keyList); } foreach (SshKey key in keyList) { responseBuilder.AddBytes(key.GetPublicKeyBlob()); responseBuilder.AddStringBlob(key.Comment); } responseBuilder.InsertHeader(Message.SSH1_AGENT_RSA_IDENTITIES_ANSWER, keyList.Count); // TODO may want to check that there is enough room in the message stream break; // succeeded } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH2_AGENTC_REQUEST_IDENTITIES: /* * Reply with SSH2_AGENT_IDENTITIES_ANSWER. */ try { var keyList = ListKeys(SshVersion.SSH2); if (FilterKeyListCallback != null) { keyList = FilterKeyListCallback(keyList); } foreach (SshKey key in keyList) { responseBuilder.AddBlob(key.GetPublicKeyBlob()); responseBuilder.AddStringBlob(key.Comment); } responseBuilder.InsertHeader(Message.SSH2_AGENT_IDENTITIES_ANSWER, keyList.Count); // TODO may want to check that there is enough room in the message stream break; // succeeded } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH1_AGENTC_RSA_CHALLENGE: /* * Reply with either SSH1_AGENT_RSA_RESPONSE or * SSH_AGENT_FAILURE, depending on whether we have that key * or not. */ try { //Reading publicKey information var publicKeyParams = messageParser.ReadSsh1PublicKeyData(true); //Searching for Key here var matchingKey = mKeyList.Where(key => key.Version == SshVersion.SSH1 && (key.GetPublicKeyParameters().Equals(publicKeyParams))).Single(); //Reading challenge var encryptedChallenge = messageParser.ReadSsh1BigIntBlob(); var sessionId = messageParser.ReadBytes(16); //Checking responseType field if (messageParser.ReadUInt32() != 1) { goto default; //responseType !=1 is not longer supported } //Answering to the challenge var engine = new Pkcs1Encoding(new RsaEngine()); engine.Init(false /* decrypt */, matchingKey.GetPrivateKeyParameters()); var decryptedChallenge = engine.ProcessBlock(encryptedChallenge, 0, encryptedChallenge.Length); using (MD5 md5 = MD5.Create()) { var md5Buffer = new byte[48]; decryptedChallenge.CopyTo(md5Buffer, 0); sessionId.CopyTo(md5Buffer, 32); responseBuilder.AddBytes(md5.ComputeHash(md5Buffer)); responseBuilder.InsertHeader(Message.SSH1_AGENT_RSA_RESPONSE); break; } } catch (InvalidOperationException) { // this is expected if there is not a matching key } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH2_AGENTC_SIGN_REQUEST: /* * Reply with either SSH2_AGENT_SIGN_RESPONSE or SSH_AGENT_FAILURE, * depending on whether we have that key or not. */ try { var keyBlob = messageParser.ReadBlob(); var reqData = messageParser.ReadBlob(); var flags = new SignRequestFlags(); try { // usually, there are no flags, so parser will throw flags = (SignRequestFlags)messageParser.ReadUInt32(); } catch { } var matchingKey = mKeyList.Where(key => key.Version == SshVersion.SSH2 && key.GetPublicKeyBlob().SequenceEqual(keyBlob)).First(); var confirmConstraints = matchingKey.Constraints .Where(constraint => constraint.Type == KeyConstraintType.SSH_AGENT_CONSTRAIN_CONFIRM); if (confirmConstraints.Count() > 0) { if (!ConfirmUserPermissionCallback.Invoke(matchingKey, process)) { goto default; } } /* create signature */ var signKey = matchingKey; var signer = signKey.GetSigner(); var algName = signKey.Algorithm.GetIdentifierString(); signer.Init(true, signKey.GetPrivateKeyParameters()); signer.BlockUpdate(reqData, 0, reqData.Length); byte[] signature = signer.GenerateSignature(); signature = signKey.FormatSignature(signature); BlobBuilder signatureBuilder = new BlobBuilder(); if (!flags.HasFlag(SignRequestFlags.SSH_AGENT_OLD_SIGNATURE)) { signatureBuilder.AddStringBlob(algName); } signatureBuilder.AddBlob(signature); responseBuilder.AddBlob(signatureBuilder.GetBlob()); responseBuilder.InsertHeader(Message.SSH2_AGENT_SIGN_RESPONSE); try { KeyUsed(this, new KeyUsedEventArgs(signKey, process)); } catch { } break; // succeeded } catch (InvalidOperationException) { // this is expected if there is not a matching key } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failure case Message.SSH1_AGENTC_ADD_RSA_IDENTITY: case Message.SSH1_AGENTC_ADD_RSA_ID_CONSTRAINED: /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ if (IsLocked) { goto default; } bool ssh1constrained = (header.Message == Message.SSH1_AGENTC_ADD_RSA_ID_CONSTRAINED); try { var publicKeyParams = messageParser.ReadSsh1PublicKeyData(false); var keyPair = messageParser.ReadSsh1KeyData(publicKeyParams); SshKey key = new SshKey(SshVersion.SSH1, keyPair); key.Comment = messageParser.ReadString(); key.Source = "External client"; if (ssh1constrained) { while (messageStream.Position < header.BlobLength + 4) { KeyConstraint constraint = new KeyConstraint(); constraint.Type = (KeyConstraintType)messageParser.ReadUInt8(); if (constraint.Type == KeyConstraintType.SSH_AGENT_CONSTRAIN_LIFETIME) { constraint.Data = messageParser.ReadUInt32(); } key.AddConstraint(constraint); } } AddKey(key); responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; } catch (CallbackNullException) { // this is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH2_AGENTC_ADD_IDENTITY: case Message.SSH2_AGENTC_ADD_ID_CONSTRAINED: /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ if (IsLocked) { goto default; } bool constrained = (header.Message == Message.SSH2_AGENTC_ADD_ID_CONSTRAINED); try { OpensshCertificate cert; var publicKeyParams = messageParser.ReadSsh2PublicKeyData(out cert); var keyPair = messageParser.ReadSsh2KeyData(publicKeyParams); SshKey key = new SshKey(SshVersion.SSH2, keyPair, null, cert); key.Comment = messageParser.ReadString(); key.Source = "External client"; if (constrained) { while (messageStream.Position < header.BlobLength + 4) { KeyConstraint constraint = new KeyConstraint(); constraint.Type = (KeyConstraintType)messageParser.ReadUInt8(); if (constraint.Type == KeyConstraintType.SSH_AGENT_CONSTRAIN_LIFETIME) { constraint.Data = messageParser.ReadUInt32(); } key.AddConstraint(constraint); } } AddKey(key); responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; // success! } catch (CallbackNullException) { // this is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH1_AGENTC_REMOVE_RSA_IDENTITY: case Message.SSH2_AGENTC_REMOVE_IDENTITY: /* * Remove from the list and return SSH_AGENT_SUCCESS, or * perhaps SSH_AGENT_FAILURE if it wasn't in the list to * start with. */ if (IsLocked) { goto default; } SshVersion removeVersion; byte[] rKeyBlob; if (header.Message == Message.SSH1_AGENTC_REMOVE_RSA_IDENTITY) { removeVersion = SshVersion.SSH1; rKeyBlob = messageParser.ReadBytes(header.BlobLength - 1); } else if (header.Message == Message.SSH2_AGENTC_REMOVE_IDENTITY) { removeVersion = SshVersion.SSH2; rKeyBlob = messageParser.ReadBlob(); } else { Debug.Fail("Should not get here."); goto default; } try { var matchingKey = mKeyList.Get(removeVersion, rKeyBlob); var startKeyListLength = mKeyList.Count; RemoveKey(matchingKey); // only succeed if key was removed if (mKeyList.Count == startKeyListLength - 1) { responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; //success! } } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES: case Message.SSH2_AGENTC_REMOVE_ALL_IDENTITIES: /* * Remove all SSH-1 or SSH-2 keys. */ if (IsLocked) { goto default; } SshVersion removeAllVersion; if (header.Message == Message.SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES) { removeAllVersion = SshVersion.SSH1; } else if (header.Message == Message.SSH2_AGENTC_REMOVE_ALL_IDENTITIES) { removeAllVersion = SshVersion.SSH2; } else { Debug.Fail("Should not get here."); goto default; } try { RemoveAllKeys(removeAllVersion); responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; //success! } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; // failed case Message.SSH_AGENTC_LOCK: try { var passphrase = new PinnedArray <byte>(messageParser.ReadBlob()); try { Lock(passphrase.Data); } finally { passphrase.Clear(); } if (IsLocked) { responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; } } catch (AgentLockedException) { // This is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; case Message.SSH_AGENTC_UNLOCK: try { var passphrase = new PinnedArray <byte>(messageParser.ReadBlob()); try { Unlock(passphrase.Data); } finally { passphrase.Clear(); } if (!IsLocked) { responseBuilder.InsertHeader(Message.SSH_AGENT_SUCCESS); break; } } catch (AgentLockedException) { // This is expected } catch (PassphraseException) { // This is expected } catch (Exception ex) { Debug.Fail(ex.ToString()); } goto default; default: responseBuilder.Clear(); responseBuilder.InsertHeader(Message.SSH_AGENT_FAILURE); break; } /* write response to stream */ if (messageStream.CanSeek) { messageStream.Position = 0; } messageStream.Write(responseBuilder.GetBlob(), 0, responseBuilder.Length); messageStream.Flush(); }
public void TestSignRequest() { var agentClient = new TestAgentClient(); var data = Encoding.UTF8.GetBytes("Data to be signed"); foreach (var key in allKeys) { agentClient.Agent.AddKey(key); var signature = agentClient.SignRequest(key, data); switch (key.Version) { case SshVersion.SSH1: using (MD5 md5 = MD5.Create()) { var md5Buffer = new byte[48]; data.CopyTo(md5Buffer, 0); agentClient.SessionId.CopyTo(md5Buffer, 32); var expctedSignature = md5.ComputeHash(md5Buffer); Assert.That(signature, Is.EqualTo(expctedSignature)); } break; case SshVersion.SSH2: BlobParser signatureParser = new BlobParser(signature); var algorithm = signatureParser.ReadString(); Assert.That(algorithm, Is.EqualTo(key.Algorithm.GetIdentifierString())); signature = signatureParser.ReadBlob(); if (key.Algorithm == PublicKeyAlgorithm.SSH_RSA) { Assert.That(signature.Length == key.Size / 8); } else if (key.Algorithm == PublicKeyAlgorithm.SSH_DSS) { Assert.That(signature.Length, Is.EqualTo(40)); var r = new BigInteger(1, signature, 0, 20); var s = new BigInteger(1, signature, 20, 20); var seq = new DerSequence(new DerInteger(r), new DerInteger(s)); signature = seq.GetDerEncoded(); } else if (key.Algorithm == PublicKeyAlgorithm.ECDSA_SHA2_NISTP256 || key.Algorithm == PublicKeyAlgorithm.ECDSA_SHA2_NISTP384 || key.Algorithm == PublicKeyAlgorithm.ECDSA_SHA2_NISTP521) { Assert.That(signature.Length, Is.AtLeast(key.Size / 4 + 8)); Assert.That(signature.Length, Is.AtMost(key.Size / 4 + 10)); BlobParser parser = new BlobParser(signature); var r = new BigInteger(parser.ReadBlob()); var s = new BigInteger(parser.ReadBlob()); var seq = new DerSequence(new DerInteger(r), new DerInteger(s)); signature = seq.GetDerEncoded(); } var signer = key.GetSigner(); signer.Init(false, key.GetPublicKeyParameters()); signer.BlockUpdate(data, 0, data.Length); var valid = signer.VerifySignature(signature); Assert.That(valid, Is.True); break; default: Assert.Fail("Unexpected Ssh Version"); break; } } }
public ICollection <ISshKey> ListKeys(SshVersion aVersion) { BlobBuilder builder = new BlobBuilder(); switch (aVersion) { case SshVersion.SSH1: builder.InsertHeader(Agent.Message.SSH1_AGENTC_REQUEST_RSA_IDENTITIES); break; case SshVersion.SSH2: builder.InsertHeader(Agent.Message.SSH2_AGENTC_REQUEST_IDENTITIES); break; default: throw new Exception(cUnsupportedSshVersion); } BlobParser replyParser = SendMessage(builder); var keyCollection = new List <ISshKey>(); var header = replyParser.ReadHeader(); switch (aVersion) { case SshVersion.SSH1: if (header.Message != Agent.Message.SSH1_AGENT_RSA_IDENTITIES_ANSWER) { throw new AgentFailureException(); } var ssh1KeyCount = replyParser.ReadUInt32(); for (var i = 0; i < ssh1KeyCount; i++) { var publicKeyParams = replyParser.ReadSsh1PublicKeyData(true); var comment = replyParser.ReadString(); keyCollection.Add( new SshKey(SshVersion.SSH1, publicKeyParams, null, comment)); } break; case SshVersion.SSH2: if (header.Message != Agent.Message.SSH2_AGENT_IDENTITIES_ANSWER) { throw new AgentFailureException(); } var ssh2KeyCount = replyParser.ReadUInt32(); for (var i = 0; i < ssh2KeyCount; i++) { var publicKeyBlob = replyParser.ReadBlob(); var publicKeyParser = new BlobParser(publicKeyBlob); OpensshCertificate cert; var publicKeyParams = publicKeyParser.ReadSsh2PublicKeyData(out cert); var comment = replyParser.ReadString(); keyCollection.Add( new SshKey(SshVersion.SSH2, publicKeyParams, null, comment, cert)); } break; default: throw new Exception(cUnsupportedSshVersion); } return(keyCollection); }
public override object Deserialize(Stream aStream) { /* check for required parameters */ if (aStream == null) { throw new ArgumentNullException("aStream"); } /* reading unencrypted part */ BlobParser parser = new BlobParser(aStream); parser.ReadBytes((uint)FILE_HEADER_LINE.Length + 2); //Skipping header line byte cipherType = parser.ReadByte(); if (cipherType != SSH_CIPHER_3DES && cipherType != SSH_CIPHER_NONE) { //TripleDes is the only encryption supported throw new KeyFormatterException("Unsupported cypherType: " + cipherType); } parser.ReadInt(); //reserved /* reading public key */ AsymmetricKeyParameter aPublicKeyParameter = parser.ReadSsh1PublicKeyData(false); String keyComment = parser.ReadString(); /* reading private key */ byte[] inputBuffer = new byte[aStream.Length]; aStream.Read(inputBuffer, 0, inputBuffer.Length); byte[] ouputBuffer; try { if (cipherType == 3) { /* private key is 3DES encrypted */ PasswordFinder pwFinder = null; if (GetPassphraseCallbackMethod != null) { pwFinder = new PasswordFinder(GetPassphraseCallbackMethod); } byte[] keydata; try { using (MD5 md5 = MD5.Create()) { char[] md5Buffer = pwFinder.GetPassword(); keydata = md5.ComputeHash(Encoding.ASCII.GetBytes(md5Buffer)); } } catch (PasswordException ex) { if (GetPassphraseCallbackMethod == null) { throw new CallbackNullException(); } throw new KeyFormatterException("see inner exception", ex); } /* decryption */ DesSsh1Engine desEngine = new DesSsh1Engine(); desEngine.Init(false, new KeyParameter(keydata)); BufferedBlockCipher bufferedBlockCipher = new BufferedBlockCipher(desEngine); ouputBuffer = bufferedBlockCipher.ProcessBytes(inputBuffer); } else { /* private key is stored in plain text */ ouputBuffer = inputBuffer; } var privateKeyParser = new BlobParser(ouputBuffer); /* checking result of decryption */ byte[] resultCheck = privateKeyParser.ReadBytes(4); if (resultCheck[0] != resultCheck[2] || resultCheck[1] != resultCheck[3]) { throw new KeyFormatterException("bad passphrase"); } /* reading private key */ var keyPair = privateKeyParser.ReadSsh1KeyData(aPublicKeyParameter); SshKey key = new SshKey(SshVersion.SSH1, keyPair); key.Comment = keyComment; return key; } catch (KeyFormatterException) { throw; } catch (Exception ex) { throw new KeyFormatterException("see inner exception", ex); } }
private static AsymmetricCipherKeyPair CreateCipherKeyPair( PublicKeyAlgorithm algorithm, byte[] publicKeyBlob, byte[] privateKeyBlob) { var parser = new BlobParser(publicKeyBlob); OpensshCertificate cert; var publicKey = parser.ReadSsh2PublicKeyData(out cert); parser = new BlobParser(privateKeyBlob); switch (algorithm) { case PublicKeyAlgorithm.SSH_RSA: var rsaPublicKeyParams = (RsaKeyParameters)publicKey; var d = new BigInteger(1, parser.ReadBlob()); var p = new BigInteger(1, parser.ReadBlob()); var q = new BigInteger(1, parser.ReadBlob()); var inverseQ = new BigInteger(1, parser.ReadBlob()); /* compute missing parameters */ var dp = d.Remainder(p.Subtract(BigInteger.One)); var dq = d.Remainder(q.Subtract(BigInteger.One)); RsaPrivateCrtKeyParameters rsaPrivateKeyParams = new RsaPrivateCrtKeyParameters(rsaPublicKeyParams.Modulus, rsaPublicKeyParams.Exponent, d, p, q, dp, dq, inverseQ); return(new AsymmetricCipherKeyPair(rsaPublicKeyParams, rsaPrivateKeyParams)); case PublicKeyAlgorithm.SSH_DSS: var dsaPublicKeyParams = (DsaPublicKeyParameters)publicKey; var x = new BigInteger(1, parser.ReadBlob()); DsaPrivateKeyParameters dsaPrivateKeyParams = new DsaPrivateKeyParameters(x, dsaPublicKeyParams.Parameters); return(new AsymmetricCipherKeyPair(dsaPublicKeyParams, dsaPrivateKeyParams)); case PublicKeyAlgorithm.ED25519: var ed25596PublicKey = (Ed25519PublicKeyParameter)publicKey; byte[] privBlob = parser.ReadBlob(); byte[] privSig = new byte[64]; // OpenSSH's "private key" is actually the private key with the public key tacked on ... Array.Copy(privBlob, 0, privSig, 0, 32); Array.Copy(ed25596PublicKey.Key, 0, privSig, 32, 32); var ed25596PrivateKey = new Ed25519PrivateKeyParameter(privSig); return(new AsymmetricCipherKeyPair(ed25596PublicKey, ed25596PrivateKey)); case PublicKeyAlgorithm.ECDSA_SHA2_NISTP256: case PublicKeyAlgorithm.ECDSA_SHA2_NISTP384: case PublicKeyAlgorithm.ECDSA_SHA2_NISTP521: var ecPublicKeyParams = (ECPublicKeyParameters)publicKey; var ecdsaPrivate = new BigInteger(1, parser.ReadBlob()); ECPrivateKeyParameters ecPrivateKeyParams = new ECPrivateKeyParameters(ecdsaPrivate, ecPublicKeyParams.Parameters); return(new AsymmetricCipherKeyPair(ecPublicKeyParams, ecPrivateKeyParams)); default: // unsupported encryption algorithm throw new PpkFormatterException(PpkFormatterException.PpkErrorType.PublicKeyEncryption); } }