/// <summary> /// Detect file format of a SSH private key file. /// </summary> /// <returns>format type</returns> /// <exception cref="IOException">File I/O error</exception> public PrivateKeyFileFormat ProbeFormat() { if (keyFile == null) { return(PrivateKeyFileFormat.UNKNOWN); } if (ByteArrayUtil.ByteArrayStartsWith(keyFile, Encoding.ASCII.GetBytes(PrivateKeyFileHeader.SSH1_HEADER))) { return(PrivateKeyFileFormat.SSH1); } if (ByteArrayUtil.ByteArrayStartsWith(keyFile, Encoding.ASCII.GetBytes(PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_RSA)) || ByteArrayUtil.ByteArrayStartsWith(keyFile, Encoding.ASCII.GetBytes(PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_DSA))) { return(PrivateKeyFileFormat.SSH2_OPENSSH); } if (ByteArrayUtil.ByteArrayStartsWith(keyFile, Encoding.ASCII.GetBytes(PrivateKeyFileHeader.SSH2_SSHCOM_HEADER))) { return(PrivateKeyFileFormat.SSH2_SSHCOM); } if (ByteArrayUtil.ByteArrayStartsWith(keyFile, Encoding.ASCII.GetBytes(PrivateKeyFileHeader.SSH2_PUTTY_HEADER_1)) || ByteArrayUtil.ByteArrayStartsWith(keyFile, Encoding.ASCII.GetBytes(PrivateKeyFileHeader.SSH2_PUTTY_HEADER_2))) { return(PrivateKeyFileFormat.SSH2_PUTTY); } return(PrivateKeyFileFormat.UNKNOWN); }
/// <summary> /// Read PuTTY SSH2 private key parameters. /// </summary> /// <param name="passphrase">passphrase for decrypt the key file</param> /// <param name="keyPair">key pair</param> /// <param name="comment">comment or empty if it didn't exist</param> public void Load(string passphrase, out KeyPair keyPair, out string comment) { if (keyFile == null) { throw new SSHException("A key file is not loaded yet"); } int version; string keyTypeName; KeyType keyType; string encryptionName; CipherAlgorithm?encryption; byte[] publicBlob; byte[] privateBlob; string privateMac; string privateHash; using (StreamReader sreader = GetStreamReader()) { //*** Read header and key type ReadHeaderLine(sreader, out version, out keyTypeName); if (keyTypeName == "ssh-rsa") { keyType = KeyType.RSA; } else if (keyTypeName == "ssh-dss") { keyType = KeyType.DSA; } else { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected key type)"); } //*** Read encryption ReadItemLine(sreader, "Encryption", out encryptionName); if (encryptionName == "aes256-cbc") { encryption = CipherAlgorithm.AES256; } else if (encryptionName == "none") { encryption = null; } else { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected encryption)"); } //*** Read comment ReadItemLine(sreader, "Comment", out comment); //*** Read public lines string publicLinesStr; ReadItemLine(sreader, "Public-Lines", out publicLinesStr); int publicLines; if (!Int32.TryParse(publicLinesStr, out publicLines) || publicLines < 0) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid public lines)"); } ReadBlob(sreader, publicLines, out publicBlob); //*** Read private lines string privateLinesStr; ReadItemLine(sreader, "Private-Lines", out privateLinesStr); int privateLines; if (!Int32.TryParse(privateLinesStr, out privateLines) || privateLines < 0) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid private lines)"); } ReadBlob(sreader, privateLines, out privateBlob); //*** Read private MAC ReadPrivateMACLine(sreader, version, out privateMac, out privateHash); } if (encryption.HasValue) { byte[] key = PuTTYPassphraseToKey(passphrase); byte[] iv = new byte[16]; Cipher cipher = CipherFactory.CreateCipher(SSHProtocol.SSH2, encryption.Value, key, iv); if (privateBlob.Length % cipher.BlockSize != 0) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid key data size)"); } cipher.Decrypt(privateBlob, 0, privateBlob.Length, privateBlob, 0); } bool verified = Verify(version, privateMac, privateHash, passphrase, keyTypeName, encryptionName, comment, publicBlob, privateBlob); if (!verified) { if (encryption.HasValue) { throw new SSHException(Strings.GetString("WrongPassphrase")); } else { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (HMAC verification failed)"); } } if (keyType == KeyType.RSA) { SSH2DataReader reader = new SSH2DataReader(publicBlob); byte[] magic = reader.ReadString(); if (!ByteArrayUtil.AreEqual(magic, Encoding.ASCII.GetBytes("ssh-rsa"))) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing magic)"); } BigInteger e = reader.ReadMPInt(); BigInteger n = reader.ReadMPInt(); reader = new SSH2DataReader(privateBlob); BigInteger d = reader.ReadMPInt(); BigInteger p = reader.ReadMPInt(); BigInteger q = reader.ReadMPInt(); BigInteger iqmp = reader.ReadMPInt(); BigInteger u = p.modInverse(q); keyPair = new RSAKeyPair(e, d, n, u, p, q); } else if (keyType == KeyType.DSA) { SSH2DataReader reader = new SSH2DataReader(publicBlob); byte[] magic = reader.ReadString(); if (!ByteArrayUtil.AreEqual(magic, Encoding.ASCII.GetBytes("ssh-dss"))) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing magic)"); } BigInteger p = reader.ReadMPInt(); BigInteger q = reader.ReadMPInt(); BigInteger g = reader.ReadMPInt(); BigInteger y = reader.ReadMPInt(); reader = new SSH2DataReader(privateBlob); BigInteger x = reader.ReadMPInt(); keyPair = new DSAKeyPair(p, g, q, y, x); } else { throw new SSHException("Unknown file type. This should not happen."); } }