Exemple #1
0
        /// <summary>
        /// Decrypts encrypted private key file data.
        /// </summary>
        /// <param name="cipherInfo">The cipher info.</param>
        /// <param name="cipherData">Encrypted data.</param>
        /// <param name="passPhrase">Decryption pass phrase.</param>
        /// <param name="binarySalt">Decryption binary salt.</param>
        /// <returns>Decrypted byte array.</returns>
        /// <exception cref="System.ArgumentNullException">cipherInfo</exception>
        /// <exception cref="ArgumentNullException"><paramref name="cipherInfo" />, <paramref name="cipherData" />, <paramref name="passPhrase" /> or <paramref name="binarySalt" /> is null.</exception>
        private static byte[] DecryptKey(CipherInfo cipherInfo, byte[] cipherData, string passPhrase, byte[] binarySalt)
        {
            if (cipherInfo == null)
            {
                throw new ArgumentNullException("cipherInfo");
            }

            if (cipherData == null)
            {
                throw new ArgumentNullException("cipherData");
            }

            if (binarySalt == null)
            {
                throw new ArgumentNullException("binarySalt");
            }

            List <byte> cipherKey = new List <byte>();

            using (var md5 = new MD5Hash())
            {
                var passwordBytes = Encoding.UTF8.GetBytes(passPhrase);

                //  Use 8 bytes binary salkt
                var initVector = passwordBytes.Concat(binarySalt.Take(8));

                var hash = md5.ComputeHash(initVector.ToArray()).AsEnumerable();

                cipherKey.AddRange(hash);

                while (cipherKey.Count < cipherInfo.KeySize / 8)
                {
                    hash = hash.Concat(initVector);

                    hash = md5.ComputeHash(hash.ToArray());

                    cipherKey.AddRange(hash);
                }
            }

            var cipher = cipherInfo.Cipher(cipherKey.ToArray(), binarySalt);

            return(cipher.Decrypt(cipherData));
        }
Exemple #2
0
        private void Open(Stream privateKey, string passPhrase)
        {
            if (privateKey == null)
            {
                throw new ArgumentNullException("privateKey");
            }

            Match privateKeyMatch = null;

            using (StreamReader sr = new StreamReader(privateKey))
            {
                var text = sr.ReadToEnd();
                privateKeyMatch = _privateKeyRegex.Match(text);
            }

            if (!privateKeyMatch.Success)
            {
                throw new SshException("Invalid private key file.");
            }

            var keyName    = privateKeyMatch.Result("${keyName}");
            var cipherName = privateKeyMatch.Result("${cipherName}");
            var salt       = privateKeyMatch.Result("${salt}");
            var data       = privateKeyMatch.Result("${data}");

            var binaryData = System.Convert.FromBase64String(data);

            byte[] decryptedData = null;

            if (!string.IsNullOrEmpty(cipherName) && !string.IsNullOrEmpty(salt))
            {
                if (string.IsNullOrEmpty(passPhrase))
                {
                    throw new SshPassPhraseNullOrEmptyException("Private key is encrypted but passphrase is empty.");
                }

                byte[] binarySalt = new byte[salt.Length / 2];
                for (int i = 0; i < binarySalt.Length; i++)
                {
                    binarySalt[i] = Convert.ToByte(salt.Substring(i * 2, 2), 16);
                }

                CipherInfo cipher = null;
                switch (cipherName)
                {
                case "DES-EDE3-CBC":
                    cipher = new CipherInfo(192, (key, iv) => { return(new TripleDesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); });
                    break;

                case "DES-EDE3-CFB":
                    cipher = new CipherInfo(192, (key, iv) => { return(new TripleDesCipher(key, new CfbCipherMode(iv), new PKCS7Padding())); });
                    break;

                case "DES-CBC":
                    cipher = new CipherInfo(64, (key, iv) => { return(new DesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); });
                    break;

                case "AES-128-CBC":
                    cipher = new CipherInfo(128, (key, iv) => { return(new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); });
                    break;

                case "AES-192-CBC":
                    cipher = new CipherInfo(192, (key, iv) => { return(new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); });
                    break;

                case "AES-256-CBC":
                    cipher = new CipherInfo(256, (key, iv) => { return(new AesCipher(key, new CbcCipherMode(iv), new PKCS7Padding())); });
                    break;

                default:
                    throw new SshException(string.Format(CultureInfo.CurrentCulture, "Private key cipher \"{0}\" is not supported.", cipherName));
                }

                decryptedData = DecryptKey(cipher, binaryData, passPhrase, binarySalt);
            }
            else
            {
                decryptedData = binaryData;
            }

            switch (keyName)
            {
            case "RSA":
                this._key    = new RsaKey(decryptedData.ToArray());
                this.HostKey = new KeyHostAlgorithm("ssh-rsa", this._key);
                break;

            case "DSA":
                this._key    = new DsaKey(decryptedData.ToArray());
                this.HostKey = new KeyHostAlgorithm("ssh-dss", this._key);
                break;

            case "SSH2 ENCRYPTED":
                var reader      = new SshDataReader(decryptedData);
                var magicNumber = reader.ReadUInt32();
                if (magicNumber != 0x3f6ff9eb)
                {
                    throw new SshException("Invalid SSH2 private key.");
                }

                var totalLength    = reader.ReadUInt32();  //  Read total bytes length including magic number
                var keyType        = reader.ReadString();
                var ssh2CipherName = reader.ReadString();
                var blobSize       = (int)reader.ReadUInt32();

                byte[] keyData = null;
                if (ssh2CipherName == "none")
                {
                    keyData = reader.ReadBytes(blobSize);
                }
                //else if (ssh2CipherName == "3des-cbc")
                //{
                //    var key = GetCipherKey(passPhrase, 192 / 8);
                //    var ssh2Сipher = new TripleDesCipher(key, null, null);
                //    keyData = ssh2Сipher.Decrypt(reader.ReadBytes(blobSize));
                //}
                else
                {
                    throw new SshException(string.Format("Cipher method '{0}' is not supported.", cipherName));
                }

                //  TODO:   Create two specific data types to avoid using SshDataReader class

                reader = new SshDataReader(keyData);

                var decryptedLength = reader.ReadUInt32();

                if (decryptedLength + 4 != blobSize)
                {
                    throw new SshException("Invalid passphrase.");
                }

                if (keyType == "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}")
                {
                    var exponent = reader.ReadBigIntWithBits(); //e
                    var d        = reader.ReadBigIntWithBits(); //d
                    var modulus  = reader.ReadBigIntWithBits(); //n
                    var inverseQ = reader.ReadBigIntWithBits(); //u
                    var q        = reader.ReadBigIntWithBits(); //p
                    var p        = reader.ReadBigIntWithBits(); //q
                    this._key    = new RsaKey(modulus, exponent, d, p, q, inverseQ);
                    this.HostKey = new KeyHostAlgorithm("ssh-rsa", this._key);
                }
                else if (keyType == "dl-modp{sign{dsa-nist-sha1},dh{plain}}")
                {
                    var zero = reader.ReadUInt32();
                    if (zero != 0)
                    {
                        throw new SshException("Invalid private key");
                    }
                    var p = reader.ReadBigIntWithBits();
                    var g = reader.ReadBigIntWithBits();
                    var q = reader.ReadBigIntWithBits();
                    var y = reader.ReadBigIntWithBits();
                    var x = reader.ReadBigIntWithBits();
                    this._key    = new DsaKey(p, q, g, y, x);
                    this.HostKey = new KeyHostAlgorithm("ssh-dss", this._key);
                }
                else
                {
                    throw new NotSupportedException(string.Format("Key type '{0}' is not supported.", keyType));
                }
                break;

            default:
                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Key '{0}' is not supported.", keyName));
            }
        }