Class for encapsulating information on encryption keys so that it can be used in PuTTY related programs
Inheritance: ISshKey
Beispiel #1
0
        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);
            }
        }
Beispiel #2
0
        static AgentClientTest()
        {
            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<SshKey> keyList = new List<SshKey>();
              keyList.Add(rsa1Key);
              keyList.Add(rsaKey);
              keyList.Add(dsaKey);
              keyList.Add(ecdsa256Key);
              keyList.Add(ecdsa384Key);
              keyList.Add(ecdsa521Key);
              keyList.Add(ed25519Key);
              allKeys = keyList.AsReadOnly();
        }
Beispiel #3
0
        public SshKey Clone()
        {
            AsymmetricCipherKeyPair keyPair = new AsymmetricCipherKeyPair(
                GetPublicKeyParameters(), GetPrivateKeyParameters());
            SshKey newKey = new SshKey(Version, keyPair, Comment);

            newKey.Source = Source;
            foreach (Agent.KeyConstraint constraint in keyConstraints)
            {
                newKey.AddConstraint(constraint);
            }
            return(newKey);
        }
Beispiel #4
0
 public void TestFormatSignature()
 {
     var random = new Random();
       var dsa_key = new SshKey(SshVersion.SSH2, new DsaPublicKeyParameters (
     new BigInteger ("1"),
     new DsaParameters(new BigInteger ("2"), new BigInteger ("3"),
                   new BigInteger ("4"))));
       // test that dsa signature works when values are not full 20 bytes.
       byte[] r_bytes = new byte[19];
       byte[] s_bytes = new byte[19];
       random.NextBytes(r_bytes);
       random.NextBytes(s_bytes);
       var r = new DerInteger(r_bytes);
       var s = new DerInteger(s_bytes);
       var sequence = new DerSequence(r, s);
       var signature = dsa_key.FormatSignature(sequence.GetEncoded());
       Assert.That(signature.Count(), Is.EqualTo(40));
 }
Beispiel #5
0
        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);
              }
        }
        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);
            }
        }
Beispiel #7
0
        /// <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 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);
            }
        }
Beispiel #9
0
        /// <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();
        }
Beispiel #10
0
        /// <summary>
        /// Parses the data from a PuTTY Private Key (.ppk) file.
        /// </summary>
        /// <param name="data">The data to parse.</param>
        /// <exception cref="dlech.SshAgentLib.PpkFormatterException">
        /// there was a problem parsing the file data
        /// </exception>
        /// <exception cref="CallBackNullException">
        /// data is encrypted and passphrase callback is null
        /// </exception>
        public override object Deserialize(Stream aStream)
        {
            FileData fileData = new FileData();

              /* check for required parameters */
              if (aStream == null) {
            throw new ArgumentNullException("aStream");
              }

              string line;
              string[] pair = new string[2];
              int lineCount, i;

              StreamReader reader = new StreamReader(aStream, Encoding.GetEncoding(1252));
              char[] delimArray = { cDelimeter };

              try {
            /* read file version */
            line = reader.ReadLine();
            pair = line.Split(delimArray, 2);
            if (!pair[0].StartsWith(puttyUserKeyFileKey)) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                          puttyUserKeyFileKey + " expected");
            }
            string ppkFileVersion = pair[0].Remove(0, puttyUserKeyFileKey.Length);
            if (!ppkFileVersion.TryParseVersion(ref fileData.ppkFileVersion)) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileVersion);
            }
            if (fileData.ppkFileVersion == Version.V1) {
              if (WarnOldFileFormatCallbackMethod != null) {
            WarnOldFileFormatCallbackMethod.Invoke();
              }
            }

            /* read public key encryption algorithm type */
            string algorithm = pair[1].Trim();
            if (!algorithm.TryParsePublicKeyAlgorithm(ref fileData.publicKeyAlgorithm)) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.PublicKeyEncryption);
            }

            /* read private key encryption algorithm type */
            line = reader.ReadLine();
            pair = line.Split(delimArray, 2);
            if (pair[0] != privateKeyEncryptionKey) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                          privateKeyEncryptionKey + " expected");
            }
            algorithm = pair[1].Trim();
            if (!algorithm.TryParsePrivateKeyAlgorithm(ref fileData.privateKeyAlgorithm)) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.PrivateKeyEncryption);
            }

            /* read comment */
            line = reader.ReadLine();
            pair = line.Split(delimArray, 2);
            if (pair[0] != commentKey) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                          commentKey + " expected");
            }
            fileData.comment = pair[1].Trim();

            /* read public key */
            line = reader.ReadLine();
            pair = line.Split(delimArray, 2);
            if (pair[0] != publicKeyLinesKey) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                          publicKeyLinesKey + " expected");
            }
            if (!int.TryParse(pair[1], out lineCount)) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                          "integer expected");
            }
            string publicKeyString = string.Empty;
            for (i = 0; i < lineCount; i++) {
              publicKeyString += reader.ReadLine();
            }
            fileData.publicKeyBlob = Util.FromBase64(publicKeyString);

            /* read private key */
            line = reader.ReadLine();
            pair = line.Split(delimArray, 2);
            if (pair[0] != privateKeyLinesKey) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                          privateKeyLinesKey + " expected");
            }
            if (!int.TryParse(pair[1], out lineCount)) {
              throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                          "integer expected");
            }
            string privateKeyString = string.Empty;
            for (i = 0; i < lineCount; i++) {
              privateKeyString += reader.ReadLine();
            }
            fileData.privateKeyBlob =
              new PinnedArray<byte>(Util.FromBase64(privateKeyString));

            /* read MAC */
            line = reader.ReadLine();
            pair = line.Split(delimArray, 2);
            if (pair[0] != privateMACKey) {
              fileData.isHMAC = false;
              if (pair[0] != privateHashKey || fileData.ppkFileVersion != Version.V1) {
            throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                            privateMACKey + " expected");
              }
            } else {
              fileData.isHMAC = true;
            }
            string privateMACString = pair[1].Trim();
            fileData.privateMAC = Util.FromHex(privateMACString);

            /* get passphrase and decrypt private key if required */
            if (fileData.privateKeyAlgorithm != PrivateKeyAlgorithm.None) {
              if (GetPassphraseCallbackMethod == null) {
            throw new CallbackNullException();
              }
              fileData.passphrase = GetPassphraseCallbackMethod.Invoke(fileData.comment);
              DecryptPrivateKey(ref fileData);
            }

            VerifyIntegrity(fileData);

            AsymmetricCipherKeyPair cipherKeyPair =
              CreateCipherKeyPair(fileData.publicKeyAlgorithm,
              fileData.publicKeyBlob, fileData.privateKeyBlob.Data);
            SshKey key = new SshKey(SshVersion.SSH2, cipherKeyPair, fileData.comment);
            return key;

              } catch (PpkFormatterException) {
            throw;
              } catch (CallbackNullException) {
            throw;
              } catch (Exception ex) {
            throw new PpkFormatterException(
            PpkFormatterException.PpkErrorType.FileFormat,
            "See inner exception.", ex);
              } finally {
            if (fileData.publicKeyBlob != null) {
              Array.Clear(fileData.publicKeyBlob, 0, fileData.publicKeyBlob.Length);
            }
            if (fileData.privateKeyBlob != null) {
              fileData.privateKeyBlob.Dispose();
            }
            if (fileData.privateMAC != null) {
              Array.Clear(fileData.privateMAC, 0, fileData.privateMAC.Length);
            }
            reader.Close();
              }
        }
Beispiel #11
0
        public static SshKey CreateKey(SshVersion version,
      PublicKeyAlgorithm algorithm, string comment = "")
        {
            if (version == SshVersion.SSH1 &&
            algorithm != PublicKeyAlgorithm.SSH_RSA) {
            throw new Exception("unsupported version/algorithm combination");
              }

              switch (algorithm) {
            case PublicKeyAlgorithm.SSH_RSA:
              KeyGenerationParameters keyGenParam =
            new KeyGenerationParameters(secureRandom, 512);

              RsaKeyPairGenerator rsaKeyPairGen = new RsaKeyPairGenerator();
              rsaKeyPairGen.Init(keyGenParam);
              AsymmetricCipherKeyPair keyPair = rsaKeyPairGen.GenerateKeyPair();
              var rsaKey = new SshKey(version, keyPair);
              rsaKey.Comment = comment;
              return rsaKey;

            case PublicKeyAlgorithm.SSH_DSS:
              DsaParametersGenerator dsaParamGen = new DsaParametersGenerator();
              dsaParamGen.Init(512, 10, secureRandom);
              DsaParameters dsaParam = dsaParamGen.GenerateParameters();
              DsaKeyGenerationParameters dsaKeyGenParam =
            new DsaKeyGenerationParameters(secureRandom, dsaParam);
              DsaKeyPairGenerator dsaKeyPairGen = new DsaKeyPairGenerator();
              dsaKeyPairGen.Init(dsaKeyGenParam);
              keyPair = dsaKeyPairGen.GenerateKeyPair();
              var dsaKey = new SshKey(SshVersion.SSH2, keyPair);
              dsaKey.Comment = comment;
              return dsaKey;

            case PublicKeyAlgorithm.ECDSA_SHA2_NISTP256:
              X9ECParameters ecdsa256X9Params =
            SecNamedCurves.GetByName("secp256r1");
              ECDomainParameters ecdsa256DomainParams =
            new ECDomainParameters(ecdsa256X9Params.Curve, ecdsa256X9Params.G,
              ecdsa256X9Params.N, ecdsa256X9Params.H);
              ECKeyGenerationParameters ecdsa256GenParams =
            new ECKeyGenerationParameters(ecdsa256DomainParams, secureRandom);
              ECKeyPairGenerator ecdsa256Gen = new ECKeyPairGenerator();
              ecdsa256Gen.Init(ecdsa256GenParams);
              keyPair = ecdsa256Gen.GenerateKeyPair();
              var ecdsa256Key = new SshKey(SshVersion.SSH2, keyPair);
              ecdsa256Key.Comment = comment;
              return ecdsa256Key;

            case PublicKeyAlgorithm.ECDSA_SHA2_NISTP384:
              X9ECParameters ecdsa384X9Params =
            SecNamedCurves.GetByName("secp384r1");
              ECDomainParameters ecdsa384DomainParams =
            new ECDomainParameters(ecdsa384X9Params.Curve, ecdsa384X9Params.G,
              ecdsa384X9Params.N, ecdsa384X9Params.H);
              ECKeyGenerationParameters ecdsa384GenParams =
            new ECKeyGenerationParameters(ecdsa384DomainParams, secureRandom);
              ECKeyPairGenerator ecdsa384Gen = new ECKeyPairGenerator();
              ecdsa384Gen.Init(ecdsa384GenParams);
              keyPair = ecdsa384Gen.GenerateKeyPair();
              var ecdsa384Key = new SshKey(SshVersion.SSH2, keyPair);
              ecdsa384Key.Comment = comment;
              return ecdsa384Key;

            case PublicKeyAlgorithm.ECDSA_SHA2_NISTP521:
              X9ECParameters ecdsa521X9Params =
            SecNamedCurves.GetByName("secp521r1");
              ECDomainParameters ecdsa521DomainParams =
            new ECDomainParameters(ecdsa521X9Params.Curve, ecdsa521X9Params.G,
              ecdsa521X9Params.N, ecdsa521X9Params.H);
              ECKeyGenerationParameters ecdsa521GenParams =
            new ECKeyGenerationParameters(ecdsa521DomainParams, secureRandom);
              ECKeyPairGenerator ecdsa521Gen = new ECKeyPairGenerator();
              ecdsa521Gen.Init(ecdsa521GenParams);
              keyPair = ecdsa521Gen.GenerateKeyPair();
              var ecdsa521Key = new SshKey(SshVersion.SSH2, keyPair);
              ecdsa521Key.Comment = comment;
              return ecdsa521Key;

            case PublicKeyAlgorithm.ED25519:
              var privateKeySeed = secureRandom.GenerateSeed(Ed25519.PrivateKeySeedSizeInBytes);
              var publicKeyBytes = new byte[Ed25519.PublicKeySizeInBytes];
              var privateKeyBytes = new byte[Ed25519.ExpandedPrivateKeySizeInBytes];
              Ed25519.KeyPairFromSeed(out publicKeyBytes, out privateKeyBytes, privateKeySeed);
              var publicKey = new Ed25519PublicKeyParameter(publicKeyBytes);
              var privateKey = new Ed25519PrivateKeyParameter(privateKeyBytes);
              var ed25519Key = new SshKey(SshVersion.SSH2, publicKey, privateKey, comment);
              return ed25519Key;

            default:
              throw new Exception("unsupported algorithm");
              }
        }
Beispiel #12
0
 public SshKey Clone()
 {
     AsymmetricCipherKeyPair keyPair = new AsymmetricCipherKeyPair(
     GetPublicKeyParameters(), GetPrivateKeyParameters());
       SshKey newKey = new SshKey(Version, keyPair, Comment);
       newKey.Source = Source;
       foreach (Agent.KeyConstraint constraint in keyConstraints) {
     newKey.AddConstraint(constraint);
       }
       return newKey;
 }
Beispiel #13
0
        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);
            }
        }
Beispiel #14
0
    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();
    }
Beispiel #15
0
    public void TestOnKeyListChanged()
    {
      Agent agent = new TestAgent();

      /* test that key with lifetime constraint is automatically removed *
       * after lifetime expires */

      AsymmetricCipherKeyPair keyPair =
        new AsymmetricCipherKeyPair(rsaKey.GetPublicKeyParameters(),
          rsaKey.GetPrivateKeyParameters());
      ISshKey key = new SshKey(SshVersion.SSH2, keyPair);
      Agent.KeyConstraint constraint = new Agent.KeyConstraint();
      constraint.Type = Agent.KeyConstraintType.SSH_AGENT_CONSTRAIN_LIFETIME;
      constraint.Data = (UInt32)1;
      key.AddConstraint(constraint);
      agent.AddKey(key);
      Thread.Sleep(500);
      Assert.That(agent.GetAllKeys().Count, Is.EqualTo(1));
      Thread.Sleep(1000);
      Assert.That(agent.GetAllKeys().Count, Is.EqualTo(0));
    }
Beispiel #16
0
        /// <summary>
        /// Parses the data from a PuTTY Private Key (.ppk) file.
        /// </summary>
        /// <param name="data">The data to parse.</param>
        /// <exception cref="dlech.SshAgentLib.PpkFormatterException">
        /// there was a problem parsing the file data
        /// </exception>
        /// <exception cref="CallBackNullException">
        /// data is encrypted and passphrase callback is null
        /// </exception>
        public override object Deserialize(Stream aStream)
        {
            FileData fileData = new FileData();

            /* check for required parameters */
            if (aStream == null)
            {
                throw new ArgumentNullException("aStream");
            }

            string line;

            string[] pair = new string[2];
            int      lineCount, i;

            StreamReader reader = new StreamReader(aStream, Encoding.GetEncoding(1252));

            char[] delimArray = { cDelimeter };

            try {
                /* read file version */
                line = reader.ReadLine();
                pair = line.Split(delimArray, 2);
                if (!pair[0].StartsWith(puttyUserKeyFileKey))
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                    puttyUserKeyFileKey + " expected");
                }
                string ppkFileVersion = pair[0].Remove(0, puttyUserKeyFileKey.Length);
                if (!ppkFileVersion.TryParseVersion(ref fileData.ppkFileVersion))
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileVersion);
                }
                if (fileData.ppkFileVersion == Version.V1)
                {
                    if (WarnOldFileFormatCallbackMethod != null)
                    {
                        WarnOldFileFormatCallbackMethod.Invoke();
                    }
                }

                /* read public key encryption algorithm type */
                string algorithm = pair[1].Trim();
                if (!algorithm.TryParsePublicKeyAlgorithm(ref fileData.publicKeyAlgorithm))
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.PublicKeyEncryption);
                }

                /* read private key encryption algorithm type */
                line = reader.ReadLine();
                pair = line.Split(delimArray, 2);
                if (pair[0] != privateKeyEncryptionKey)
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                    privateKeyEncryptionKey + " expected");
                }
                algorithm = pair[1].Trim();
                if (!algorithm.TryParsePrivateKeyAlgorithm(ref fileData.privateKeyAlgorithm))
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.PrivateKeyEncryption);
                }

                /* read comment */
                line = reader.ReadLine();
                pair = line.Split(delimArray, 2);
                if (pair[0] != commentKey)
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                    commentKey + " expected");
                }
                fileData.comment = pair[1].Trim();

                /* read public key */
                line = reader.ReadLine();
                pair = line.Split(delimArray, 2);
                if (pair[0] != publicKeyLinesKey)
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                    publicKeyLinesKey + " expected");
                }
                if (!int.TryParse(pair[1], out lineCount))
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                    "integer expected");
                }
                string publicKeyString = string.Empty;
                for (i = 0; i < lineCount; i++)
                {
                    publicKeyString += reader.ReadLine();
                }
                fileData.publicKeyBlob = Util.FromBase64(publicKeyString);

                /* read private key */
                line = reader.ReadLine();
                pair = line.Split(delimArray, 2);
                if (pair[0] != privateKeyLinesKey)
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                    privateKeyLinesKey + " expected");
                }
                if (!int.TryParse(pair[1], out lineCount))
                {
                    throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                    "integer expected");
                }
                string privateKeyString = string.Empty;
                for (i = 0; i < lineCount; i++)
                {
                    privateKeyString += reader.ReadLine();
                }
                fileData.privateKeyBlob =
                    new PinnedArray <byte>(Util.FromBase64(privateKeyString));

                /* read MAC */
                line = reader.ReadLine();
                pair = line.Split(delimArray, 2);
                if (pair[0] != privateMACKey)
                {
                    fileData.isHMAC = false;
                    if (pair[0] != privateHashKey || fileData.ppkFileVersion != Version.V1)
                    {
                        throw new PpkFormatterException(PpkFormatterException.PpkErrorType.FileFormat,
                                                        privateMACKey + " expected");
                    }
                }
                else
                {
                    fileData.isHMAC = true;
                }
                string privateMACString = pair[1].Trim();
                fileData.privateMAC = Util.FromHex(privateMACString);


                /* get passphrase and decrypt private key if required */
                if (fileData.privateKeyAlgorithm != PrivateKeyAlgorithm.None)
                {
                    if (GetPassphraseCallbackMethod == null)
                    {
                        throw new CallbackNullException();
                    }
                    fileData.passphrase = GetPassphraseCallbackMethod.Invoke(fileData.comment);
                    DecryptPrivateKey(ref fileData);
                }

                VerifyIntegrity(fileData);

                AsymmetricCipherKeyPair cipherKeyPair =
                    CreateCipherKeyPair(fileData.publicKeyAlgorithm,
                                        fileData.publicKeyBlob, fileData.privateKeyBlob.Data);
                SshKey key = new SshKey(SshVersion.SSH2, cipherKeyPair, fileData.comment);
                return(key);
            } catch (PpkFormatterException) {
                throw;
            } catch (CallbackNullException) {
                throw;
            } catch (Exception ex) {
                throw new PpkFormatterException(
                          PpkFormatterException.PpkErrorType.FileFormat,
                          "See inner exception.", ex);
            } finally {
                if (fileData.publicKeyBlob != null)
                {
                    Array.Clear(fileData.publicKeyBlob, 0, fileData.publicKeyBlob.Length);
                }
                if (fileData.privateKeyBlob != null)
                {
                    fileData.privateKeyBlob.Dispose();
                }
                if (fileData.privateMAC != null)
                {
                    Array.Clear(fileData.privateMAC, 0, fileData.privateMAC.Length);
                }
                reader.Close();
            }
        }