//init ciphers private void InitCipher(byte[] session_key) { _tCipher = CipherFactory.CreateCipher(SSHProtocol.SSH1, _cInfo._algorithmForTransmittion, session_key); Cipher rc = CipherFactory.CreateCipher(SSHProtocol.SSH1, _cInfo._algorithmForReception, session_key); _packetBuilder.SetCipher(rc, _param.CheckMACError); }
/// <summary> /// constructs from file /// </summary> /// <param name="path">file name</param> /// <param name="passphrase">passphrase or empty string if passphrase is not required</param> public SSH1UserAuthKey(string path, string passphrase) { var fileTask = StorageFile.GetFileFromPathAsync(path); fileTask.AsTask().Wait(); var fileObject = fileTask.AsTask().Result; var openTask = fileObject.OpenReadAsync(); openTask.AsTask().Wait(); Stream s = openTask.AsTask().Result.AsStream(); byte[] header = new byte[32]; s.Read(header, 0, header.Length); if (Encoding.UTF8.GetString(header, 0, header.Length) != "SSH PRIVATE KEY FILE FORMAT 1.1\n") { //throw new Exception(String.Format(Strings.GetString("BrokenKeyFile"), path)); throw new Exception(String.Format(Strings.GetString("BrokenKeyFile"), path)); } SSH1DataReader reader = new SSH1DataReader(ReadAll(s)); s.Dispose(); byte[] cipher = reader.Read(2); //first 2 bytes indicates algorithm and next 8 bytes is space reader.Read(8); _modulus = reader.ReadMPInt(); _publicExponent = reader.ReadMPInt(); byte[] comment = reader.ReadString(); byte[] prvt = reader.ReadAll(); //必要なら復号 CipherAlgorithm algo = (CipherAlgorithm)cipher[1]; if (algo != 0) { Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH1, algo, ConvertToKey(passphrase)); byte[] buf = new byte[prvt.Length]; c.Decrypt(prvt, 0, prvt.Length, buf, 0); prvt = buf; } SSH1DataReader prvtreader = new SSH1DataReader(prvt); byte[] mark = prvtreader.Read(4); if (mark[0] != mark[2] || mark[1] != mark[3]) { //throw new Exception(Strings.GetString("WrongPassphrase")); throw new Exception(Strings.GetString("WrongPassphrase")); } _privateExponent = prvtreader.ReadMPInt(); _crtCoefficient = prvtreader.ReadMPInt(); _primeP = prvtreader.ReadMPInt(); _primeQ = prvtreader.ReadMPInt(); }
/// <summary> /// constructs from file /// </summary> /// <param name="path">file name</param> /// <param name="passphrase">passphrase or empty string if passphrase is not required</param> public SSH1UserAuthKey(string path, string passphrase) { #if PODEROSA_KEYFORMAT PrivateKeyLoader loader = new PrivateKeyLoader(path); loader.LoadSSH1PrivateKey( passphrase, out _modulus, out _publicExponent, out _privateExponent, out _primeP, out _primeQ, out _crtCoefficient, out _comment); #else Stream s = File.Open(path, FileMode.Open); byte[] header = new byte[32]; s.Read(header, 0, header.Length); if (Encoding.ASCII.GetString(header) != "SSH PRIVATE KEY FILE FORMAT 1.1\n") { throw new SSHException(String.Format(Strings.GetString("BrokenKeyFile"), path)); } SSH1DataReader reader = new SSH1DataReader(ReadAll(s)); s.Close(); byte[] cipher = reader.Read(2); //first 2 bytes indicates algorithm and next 8 bytes is space reader.Read(8); _modulus = reader.ReadMPInt(); _publicExponent = reader.ReadMPInt(); _comment = reader.ReadString(); byte[] prvt = reader.GetRemainingDataView().GetBytes(); //必要なら復号 CipherAlgorithm algo = (CipherAlgorithm)cipher[1]; if (algo != 0) { Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH1, algo, ConvertToKey(passphrase)); byte[] buf = new byte[prvt.Length]; c.Decrypt(prvt, 0, prvt.Length, buf, 0); prvt = buf; } SSH1DataReader prvtreader = new SSH1DataReader(prvt); byte[] mark = prvtreader.Read(4); if (mark[0] != mark[2] || mark[1] != mark[3]) { throw new SSHException(Strings.GetString("WrongPassphrase")); } _privateExponent = prvtreader.ReadMPInt(); _crtCoefficient = prvtreader.ReadMPInt(); _primeP = prvtreader.ReadMPInt(); _primeQ = prvtreader.ReadMPInt(); #endif }
/// <summary> /// Read SSH1 private key parameters. /// </summary> /// <param name="passphrase">passphrase for decrypt the key file</param> /// <param name="modulus">private key parameter</param> /// <param name="publicExponent">private key parameter</param> /// <param name="privateExponent">private key parameter</param> /// <param name="primeP">private key parameter</param> /// <param name="primeQ">private key parameter</param> /// <param name="crtCoefficient">private key parameter</param> /// <exception cref="SSHException">failed to parse</exception> public void Load( string passphrase, out BigInteger modulus, out BigInteger publicExponent, out BigInteger privateExponent, out BigInteger primeP, out BigInteger primeQ, out BigInteger crtCoefficient) { if (keyFile == null) { throw new SSHException("A key file is not loaded yet"); } byte[] hdr = Encoding.ASCII.GetBytes(PrivateKeyFileHeader.SSH1_HEADER); if (!ByteArrayUtil.ByteArrayStartsWith(keyFile, hdr)) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile")); } SSH1DataReader reader = new SSH1DataReader(keyFile); reader.Read(hdr.Length); byte[] cipher = reader.Read(2); //first 2 bytes indicates algorithm and next 8 bytes is space reader.Read(8); modulus = reader.ReadMPInt(); publicExponent = reader.ReadMPInt(); byte[] comment = reader.ReadString(); byte[] prvt = reader.ReadAll(); //必要なら復号 CipherAlgorithm algo = (CipherAlgorithm)cipher[1]; if (algo != 0) { Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH1, algo, SSH1PassphraseToKey(passphrase)); byte[] buf = new byte[prvt.Length]; c.Decrypt(prvt, 0, prvt.Length, buf, 0); prvt = buf; } SSH1DataReader prvtreader = new SSH1DataReader(prvt); byte[] mark = prvtreader.Read(4); if (mark[0] != mark[2] || mark[1] != mark[3]) { throw new SSHException(Strings.GetString("WrongPassphrase")); } privateExponent = prvtreader.ReadMPInt(); crtCoefficient = prvtreader.ReadMPInt(); primeP = prvtreader.ReadMPInt(); primeQ = prvtreader.ReadMPInt(); }
/// <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."); } }
/// <summary> /// Read OpenSSH 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"); } KeyType keyType; String base64Text; bool encrypted = false; CipherAlgorithm?encryption = null; byte[] iv = null; int keySize = 0; int ivSize = 0; using (StreamReader sreader = GetStreamReader()) { string line = sreader.ReadLine(); if (line == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected eof)"); } if (line == PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_RSA) { keyType = KeyType.RSA; } else if (line == PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_DSA) { keyType = KeyType.DSA; } else { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected key type)"); } string footer = line.Replace("BEGIN", "END"); StringBuilder buf = new StringBuilder(); comment = String.Empty; while (true) { line = sreader.ReadLine(); if (line == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected eof)"); } if (line == footer) { break; } if (line.IndexOf(':') >= 0) { if (line.StartsWith("Proc-Type:")) { string[] w = line.Substring("Proc-Type:".Length).Trim().Split(','); if (w.Length < 1) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid Proc-Type)"); } if (w[0] != "4") { throw new SSHException(Strings.GetString("UnsupportedPrivateKeyFormat") + " (" + Strings.GetString("Reason_UnsupportedProcType") + ")"); } if (w.Length >= 2 && w[1] == "ENCRYPTED") { encrypted = true; } } else if (line.StartsWith("DEK-Info:")) { string[] w = line.Substring("DEK-Info:".Length).Trim().Split(','); if (w.Length < 2) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid DEK-Info)"); } switch (w[0]) { case "DES-EDE3-CBC": encryption = CipherAlgorithm.TripleDES; ivSize = 8; keySize = 24; break; case "AES-128-CBC": encryption = CipherAlgorithm.AES128; ivSize = 16; keySize = 16; break; default: throw new SSHException(Strings.GetString("UnsupportedPrivateKeyFormat") + " (" + Strings.GetString("Reason_UnsupportedEncryptionType") + ")"); } iv = HexToByteArray(w[1]); if (iv == null || iv.Length != ivSize) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid IV)"); } } } else { buf.Append(line); } } base64Text = buf.ToString(); } byte[] keydata = Base64.Decode(Encoding.ASCII.GetBytes(base64Text)); if (encrypted) { if (!encryption.HasValue || iv == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing encryption type or IV)"); } byte[] key = OpenSSHPassphraseToKey(passphrase, iv, keySize); Cipher cipher = CipherFactory.CreateCipher(SSHProtocol.SSH2, encryption.Value, key, iv); if (keydata.Length % cipher.BlockSize != 0) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid key data size)"); } cipher.Decrypt(keydata, 0, keydata.Length, keydata, 0); } using (MemoryStream keyDataStream = new MemoryStream(keydata, false)) { BERReader reader = new BERReader(keyDataStream); if (!reader.ReadSequence()) { throw new SSHException(Strings.GetString("WrongPassphrase")); } if (keyType == KeyType.RSA) { /* from OpenSSL rsa_asn1.c * * ASN1_SIMPLE(RSA, version, LONG), * ASN1_SIMPLE(RSA, n, BIGNUM), * ASN1_SIMPLE(RSA, e, BIGNUM), * ASN1_SIMPLE(RSA, d, BIGNUM), * ASN1_SIMPLE(RSA, p, BIGNUM), * ASN1_SIMPLE(RSA, q, BIGNUM), * ASN1_SIMPLE(RSA, dmp1, BIGNUM), * ASN1_SIMPLE(RSA, dmq1, BIGNUM), * ASN1_SIMPLE(RSA, iqmp, BIGNUM) */ BigInteger v, n, e, d, p, q, dmp1, dmq1, iqmp; if (!reader.ReadInteger(out v) || !reader.ReadInteger(out n) || !reader.ReadInteger(out e) || !reader.ReadInteger(out d) || !reader.ReadInteger(out p) || !reader.ReadInteger(out q) || !reader.ReadInteger(out dmp1) || !reader.ReadInteger(out dmq1) || !reader.ReadInteger(out iqmp)) { throw new SSHException(Strings.GetString("WrongPassphrase")); } BigInteger u = p.modInverse(q); // inverse of p mod q keyPair = new RSAKeyPair(e, d, n, u, p, q); } else if (keyType == KeyType.DSA) { /* from OpenSSL dsa_asn1.c * * ASN1_SIMPLE(DSA, version, LONG), * ASN1_SIMPLE(DSA, p, BIGNUM), * ASN1_SIMPLE(DSA, q, BIGNUM), * ASN1_SIMPLE(DSA, g, BIGNUM), * ASN1_SIMPLE(DSA, pub_key, BIGNUM), * ASN1_SIMPLE(DSA, priv_key, BIGNUM) */ BigInteger v, p, q, g, y, x; if (!reader.ReadInteger(out v) || !reader.ReadInteger(out p) || !reader.ReadInteger(out q) || !reader.ReadInteger(out g) || !reader.ReadInteger(out y) || !reader.ReadInteger(out x)) { throw new SSHException(Strings.GetString("WrongPassphrase")); } keyPair = new DSAKeyPair(p, g, q, y, x); } else { throw new SSHException("Unknown file type. This should not happen."); } } }
/// <summary> /// Read SSH.com 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> /// <exception cref="SSHException">failed to parse</exception> public void Load(string passphrase, out KeyPair keyPair, out string comment) { if (keyFile == null) { throw new SSHException("A key file is not loaded yet"); } String base64Text; using (StreamReader sreader = GetStreamReader()) { string line = sreader.ReadLine(); if (line == null || line != PrivateKeyFileHeader.SSH2_SSHCOM_HEADER) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing header)"); } StringBuilder buf = new StringBuilder(); comment = String.Empty; while (true) { line = sreader.ReadLine(); if (line == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected eof)"); } if (line == PrivateKeyFileHeader.SSH2_SSHCOM_FOOTER) { break; } if (line.IndexOf(':') >= 0) { if (line.StartsWith("Comment: ")) { comment = line.Substring("Comment: ".Length); } } else if (line[line.Length - 1] == '\\') { buf.Append(line, 0, line.Length - 1); } else { buf.Append(line); } } base64Text = buf.ToString(); } byte[] keydata = Base64.Decode(Encoding.ASCII.GetBytes(base64Text)); //Debug.WriteLine(DebugUtil.DumpByteArray(keydata)); SSH2DataReader reader = new SSH2DataReader(keydata); int magic = reader.ReadInt32(); if (magic != MAGIC) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (magic code unmatched)"); } int privateKeyLen = reader.ReadInt32(); string type = Encoding.ASCII.GetString(reader.ReadString()); string ciphername = Encoding.ASCII.GetString(reader.ReadString()); int bufLen = reader.ReadInt32(); if (ciphername != "none") { CipherAlgorithm algo = CipherFactory.SSH2NameToAlgorithm(ciphername); byte[] key = SSH2UserAuthKey.PassphraseToKey(passphrase, CipherFactory.GetKeySize(algo)); Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH2, algo, key); byte[] tmp = new Byte[reader.Image.Length - reader.Offset]; c.Decrypt(reader.Image, reader.Offset, reader.Image.Length - reader.Offset, tmp, 0); reader = new SSH2DataReader(tmp); } int parmLen = reader.ReadInt32(); if (parmLen < 0 || parmLen > reader.Rest) { throw new SSHException(Strings.GetString("WrongPassphrase")); } if (type.IndexOf("if-modn") != -1) { //mindterm mistaken this order of BigIntegers BigInteger e = reader.ReadBigIntWithBits(); BigInteger d = reader.ReadBigIntWithBits(); BigInteger n = reader.ReadBigIntWithBits(); BigInteger u = reader.ReadBigIntWithBits(); BigInteger p = reader.ReadBigIntWithBits(); BigInteger q = reader.ReadBigIntWithBits(); keyPair = new RSAKeyPair(e, d, n, u, p, q); } else if (type.IndexOf("dl-modp") != -1) { if (reader.ReadInt32() != 0) { throw new SSHException(Strings.GetString("UnsupportedPrivateKeyFormat") + " (" + Strings.GetString("Reason_UnsupportedDSAKeyFormat") + ")"); } BigInteger p = reader.ReadBigIntWithBits(); BigInteger g = reader.ReadBigIntWithBits(); BigInteger q = reader.ReadBigIntWithBits(); BigInteger y = reader.ReadBigIntWithBits(); BigInteger x = reader.ReadBigIntWithBits(); keyPair = new DSAKeyPair(p, g, q, y, x); } else { throw new SSHException(Strings.GetString("UnsupportedAuthenticationMethod")); } }
public void WritePrivatePartInSECSHStyleFile(Stream dest, string comment, string passphrase) { //step1 key body SSH2DataWriter wr = new SSH2DataWriter(); wr.Write(0); //this field is filled later if (_keypair.Algorithm == PublicKeyAlgorithm.RSA) { RSAKeyPair rsa = (RSAKeyPair)_keypair; RSAPublicKey pub = (RSAPublicKey)_keypair.PublicKey; wr.WriteBigIntWithBits(pub.Exponent); wr.WriteBigIntWithBits(rsa.D); wr.WriteBigIntWithBits(pub.Modulus); wr.WriteBigIntWithBits(rsa.U); wr.WriteBigIntWithBits(rsa.P); wr.WriteBigIntWithBits(rsa.Q); } else { DSAKeyPair dsa = (DSAKeyPair)_keypair; DSAPublicKey pub = (DSAPublicKey)_keypair.PublicKey; wr.Write(0); wr.WriteBigIntWithBits(pub.P); wr.WriteBigIntWithBits(pub.G); wr.WriteBigIntWithBits(pub.Q); wr.WriteBigIntWithBits(pub.Y); wr.WriteBigIntWithBits(dsa.X); } int padding_len = 0; if (passphrase != null) { padding_len = 8 - (int)wr.Length % 8; wr.Write(new byte[padding_len]); } byte[] encrypted_body = wr.ToByteArray(); SSHUtil.WriteIntToByteArray(encrypted_body, 0, encrypted_body.Length - padding_len - 4); //encrypt if necessary if (passphrase != null) { Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH2, CipherAlgorithm.TripleDES, PassphraseToKey(passphrase, 24)); Debug.Assert(encrypted_body.Length % 8 == 0); byte[] tmp = new Byte[encrypted_body.Length]; c.Encrypt(encrypted_body, 0, encrypted_body.Length, tmp, 0); encrypted_body = tmp; } //step2 make binary key data wr = new SSH2DataWriter(); wr.Write(MAGIC_VAL); wr.Write(0); //for total size wr.Write(_keypair.Algorithm == PublicKeyAlgorithm.RSA? "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}" : "dl-modp{sign{dsa-nist-sha1},dh{plain}}"); wr.Write(passphrase == null? "none" : "3des-cbc"); wr.WriteAsString(encrypted_body); byte[] rawdata = wr.ToByteArray(); SSHUtil.WriteIntToByteArray(rawdata, 4, rawdata.Length); //fix total length //step3 write final data StreamWriter sw = new StreamWriter(dest, Encoding.ASCII); sw.WriteLine("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"); if (comment != null) { WriteKeyFileBlock(sw, "Comment: " + comment, true); } WriteKeyFileBlock(sw, Encoding.ASCII.GetString(Base64.Encode(rawdata)), false); sw.WriteLine("---- END SSH2 ENCRYPTED PRIVATE KEY ----"); sw.Close(); }
/* * Format style note * ---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- * Comment: ******* * <base64-encoded body> * ---- END SSH2 ENCRYPTED PRIVATE KEY ---- * * body = MAGIC_VAL || body-length || type(string) || encryption-algorithm-name(string) || encrypted-body(string) * encrypted-body = array of BigInteger(algorithm-specific) */ public static SSH2UserAuthKey FromSECSHStyleStream(Stream strm, string passphrase) { StreamReader r = new StreamReader(strm, Encoding.ASCII); string l = r.ReadLine(); if (l == null || l != "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----") { throw new SSHException("Wrong key format"); } l = r.ReadLine(); StringBuilder buf = new StringBuilder(); while (l != "---- END SSH2 ENCRYPTED PRIVATE KEY ----") { if (l.IndexOf(':') == -1) { buf.Append(l); } else { while (l.EndsWith("\\")) { l = r.ReadLine(); } } l = r.ReadLine(); if (l == null) { throw new SSHException("Key is broken"); } } r.Close(); byte[] keydata = Base64.Decode(Encoding.ASCII.GetBytes(buf.ToString())); //Debug.WriteLine(DebugUtil.DumpByteArray(keydata)); /* * FileStream fs = new FileStream("C:\\IOPort\\keydata1.bin", FileMode.Create); * fs.Write(keydata, 0, keydata.Length); * fs.Close(); */ SSH2DataReader re = new SSH2DataReader(keydata); int magic = re.ReadInt32(); if (magic != MAGIC_VAL) { throw new SSHException("key file is broken"); } int privateKeyLen = re.ReadInt32(); string type = Encoding.ASCII.GetString(re.ReadString()); string ciphername = Encoding.ASCII.GetString(re.ReadString()); int bufLen = re.ReadInt32(); if (ciphername != "none") { CipherAlgorithm algo = CipherFactory.SSH2NameToAlgorithm(ciphername); byte[] key = PassphraseToKey(passphrase, CipherFactory.GetKeySize(algo)); Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH2, algo, key); byte[] tmp = new Byte[re.Image.Length - re.Offset]; c.Decrypt(re.Image, re.Offset, re.Image.Length - re.Offset, tmp, 0); re = new SSH2DataReader(tmp); } int parmLen = re.ReadInt32(); if (parmLen < 0 || parmLen > re.Rest) { throw new SSHException(Strings.GetString("WrongPassphrase")); } if (type.IndexOf("if-modn") != -1) { //mindterm mistaken this order of BigIntegers BigInteger e = re.ReadBigIntWithBits(); BigInteger d = re.ReadBigIntWithBits(); BigInteger n = re.ReadBigIntWithBits(); BigInteger u = re.ReadBigIntWithBits(); BigInteger p = re.ReadBigIntWithBits(); BigInteger q = re.ReadBigIntWithBits(); return(new SSH2UserAuthKey(new RSAKeyPair(e, d, n, u, p, q))); } else if (type.IndexOf("dl-modp") != -1) { if (re.ReadInt32() != 0) { throw new SSHException("DSS Private Key File is broken"); } BigInteger p = re.ReadBigIntWithBits(); BigInteger g = re.ReadBigIntWithBits(); BigInteger q = re.ReadBigIntWithBits(); BigInteger y = re.ReadBigIntWithBits(); BigInteger x = re.ReadBigIntWithBits(); return(new SSH2UserAuthKey(new DSAKeyPair(p, g, q, y, x))); } else { throw new SSHException("unknown authentication method " + type); } }
public byte[] ToByteArray(string passphrase) { //step1 key body SSH2DataWriter wr = new SSH2DataWriter(); wr.Write(0); //this field is filled later if (_keypair.Algorithm == PublicKeyAlgorithm.RSA) { RSAKeyPair rsa = (RSAKeyPair)_keypair; RSAPublicKey pub = (RSAPublicKey)_keypair.PublicKey; wr.WriteBigIntWithBits(pub.Exponent); wr.WriteBigIntWithBits(rsa.D); wr.WriteBigIntWithBits(pub.Modulus); wr.WriteBigIntWithBits(rsa.U); wr.WriteBigIntWithBits(rsa.P); wr.WriteBigIntWithBits(rsa.Q); } else { DSAKeyPair dsa = (DSAKeyPair)_keypair; DSAPublicKey pub = (DSAPublicKey)_keypair.PublicKey; wr.Write(0); wr.WriteBigIntWithBits(pub.P); wr.WriteBigIntWithBits(pub.G); wr.WriteBigIntWithBits(pub.Q); wr.WriteBigIntWithBits(pub.Y); wr.WriteBigIntWithBits(dsa.X); } int padding_len = 0; if (passphrase != null) { padding_len = 8 - (int)wr.Length % 8; wr.Write(new byte[padding_len]); } byte[] encrypted_body = wr.ToByteArray(); SSHUtil.WriteIntToByteArray(encrypted_body, 0, encrypted_body.Length - padding_len - 4); //encrypt if necessary if (passphrase != null) { Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH2, CipherAlgorithm.TripleDES, PassphraseToKey(passphrase, 24)); Debug.Assert(encrypted_body.Length % 8 == 0); byte[] tmp = new Byte[encrypted_body.Length]; c.Encrypt(encrypted_body, 0, encrypted_body.Length, tmp, 0); encrypted_body = tmp; } //step2 make binary key data wr = new SSH2DataWriter(); wr.Write(MAGIC_VAL); wr.Write(0); //for total size wr.Write(_keypair.Algorithm == PublicKeyAlgorithm.RSA ? "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}" : "dl-modp{sign{dsa-nist-sha1},dh{plain}}"); wr.Write(passphrase == null ? "none" : "3des-cbc"); wr.WriteAsString(encrypted_body); byte[] rawdata = wr.ToByteArray(); SSHUtil.WriteIntToByteArray(rawdata, 4, rawdata.Length); //fix total length return(rawdata); }
public static SSH2UserAuthKey FromByteArray(byte[] keydata, string passphrase) { SSH2DataReader re = new SSH2DataReader(keydata); int magic = re.ReadInt32(); if (magic != MAGIC_VAL) { throw new SSHException("key file is broken"); } int privateKeyLen = re.ReadInt32(); string type = Encoding.ASCII.GetString(re.ReadString()); string ciphername = Encoding.ASCII.GetString(re.ReadString()); int bufLen = re.ReadInt32(); if (ciphername != "none") { CipherAlgorithm algo = CipherFactory.SSH2NameToAlgorithm(ciphername); byte[] key = PassphraseToKey(passphrase, CipherFactory.GetKeySize(algo)); Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH2, algo, key); byte[] tmp = new Byte[re.Image.Length - re.Offset]; c.Decrypt(re.Image, re.Offset, re.Image.Length - re.Offset, tmp, 0); re = new SSH2DataReader(tmp); } int parmLen = re.ReadInt32(); if (parmLen < 0 || parmLen > re.Rest) { throw new SSHException(Strings.GetString("WrongPassphrase")); } if (type.IndexOf("if-modn") != -1) { //mindterm mistaken this order of BigIntegers BigInteger e = re.ReadBigIntWithBits(); BigInteger d = re.ReadBigIntWithBits(); BigInteger n = re.ReadBigIntWithBits(); BigInteger u = re.ReadBigIntWithBits(); BigInteger p = re.ReadBigIntWithBits(); BigInteger q = re.ReadBigIntWithBits(); return(new SSH2UserAuthKey(new RSAKeyPair(e, d, n, u, p, q))); } else if (type.IndexOf("dl-modp") != -1) { if (re.ReadInt32() != 0) { throw new SSHException("DSS Private Key File is broken"); } BigInteger p = re.ReadBigIntWithBits(); BigInteger g = re.ReadBigIntWithBits(); BigInteger q = re.ReadBigIntWithBits(); BigInteger y = re.ReadBigIntWithBits(); BigInteger x = re.ReadBigIntWithBits(); return(new SSH2UserAuthKey(new DSAKeyPair(p, g, q, y, x))); } else { throw new SSHException("unknown authentication method " + type); } }
/// <summary> /// Read private key parameters with OpenSSH format. /// </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) { // Note: // The structure of the private key format is described in "PROTOCOL.key" file in OpenSSH sources. String base64Text; using (StreamReader sreader = GetStreamReader()) { string line = sreader.ReadLine(); if (line == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected eof)"); } if (line != PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_OPENSSH) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected key type)"); } string footer = line.Replace("BEGIN", "END"); StringBuilder buf = new StringBuilder(); while (true) { line = sreader.ReadLine(); if (line == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected eof)"); } if (line == footer) { break; } buf.Append(line); } base64Text = buf.ToString(); } byte[] blob = Base64.Decode(Encoding.ASCII.GetBytes(base64Text)); int numKeys; // number of keys byte[][] publicKeyBlobs; byte[] privateKeyData; bool decrypted; using (var blobStream = new MemoryStream(blob, false)) { if (!CheckMagic(blobStream)) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unsupported format)"); } string cipherName = ReadString(blobStream); CipherAlgorithm?cipherAlgorithm; int cipherKeySize; int cipherIVSize; switch (cipherName) { case "none": cipherAlgorithm = null; cipherKeySize = 0; cipherIVSize = 0; break; case "aes128-cbc": cipherAlgorithm = CipherAlgorithm.AES128; cipherKeySize = 16; cipherIVSize = 16; // use block size break; case "aes192-cbc": cipherAlgorithm = CipherAlgorithm.AES192; cipherKeySize = 24; cipherIVSize = 16; // use block size break; case "aes256-cbc": cipherAlgorithm = CipherAlgorithm.AES256; cipherKeySize = 32; cipherIVSize = 16; // use block size break; default: throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unsupported cipher)"); } string kdfName = ReadString(blobStream); if (kdfName != "bcrypt" && kdfName != "none") { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unsupported kdf)"); } if ((cipherName == "none") != (kdfName == "none")) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid cipher)"); } byte[] kdfOptions = ReadBytes(blobStream); if (kdfOptions == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing kdf options)"); } byte[] kdfSalt; uint kdfRounds; byte[] key; byte[] iv; if (kdfName == "none") { kdfSalt = null; kdfRounds = 0; key = null; iv = null; } else { if (!ReadKdfOptions(kdfOptions, out kdfSalt, out kdfRounds)) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid kdf options)"); } if (passphrase == null || passphrase.Length == 0) { throw new SSHException(Strings.GetString("WrongPassphrase")); } // prepare decryption Bcrypt bcrypt = new Bcrypt(); byte[] tmpkey = bcrypt.BcryptPbkdf(passphrase, kdfSalt, kdfRounds, cipherKeySize + cipherIVSize); if (tmpkey == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid kdf options)"); } key = new byte[cipherKeySize]; Buffer.BlockCopy(tmpkey, 0, key, 0, cipherKeySize); iv = new byte[cipherIVSize]; Buffer.BlockCopy(tmpkey, cipherKeySize, iv, 0, cipherIVSize); } if (!ReadInt32(blobStream, out numKeys) || numKeys < 0) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing keys)"); } publicKeyBlobs = new byte[numKeys][]; for (int i = 0; i < numKeys; ++i) { byte[] data = ReadBytes(blobStream); if (data == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing public keys)"); } publicKeyBlobs[i] = data; } privateKeyData = ReadBytes(blobStream); if (privateKeyData == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing private keys)"); } if (cipherAlgorithm.HasValue && key != null && iv != null) { // decrypt private keys Cipher cipher = CipherFactory.CreateCipher(SSHProtocol.SSH2, cipherAlgorithm.Value, key, iv); cipher.Decrypt(privateKeyData, 0, privateKeyData.Length, privateKeyData, 0); decrypted = true; } else { decrypted = false; } } using (var privateKeysStream = new MemoryStream(privateKeyData, false)) { uint check1, check2; if (!ReadUInt32(privateKeysStream, out check1) || !ReadUInt32(privateKeysStream, out check2)) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid private keys)"); } if (check1 != check2) { throw new SSHException(decrypted ? Strings.GetString("WrongPassphrase") : Strings.GetString("NotValidPrivateKeyFile")); } for (int i = 0; i < numKeys; ++i) { string privateKeyType = ReadString(privateKeysStream); using (var publicKeyBlobStream = new MemoryStream(publicKeyBlobs[i], false)) { string publicKeyType = ReadString(publicKeyBlobStream); if (publicKeyType != privateKeyType) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (key type unmatched)"); } switch (privateKeyType) { case "ssh-ed25519": { byte[] pk = ReadBytes(privateKeysStream); if (pk == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile")); } byte[] sk = ReadBytes(privateKeysStream); if (sk == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile")); } string cmnt = ReadString(privateKeysStream); // comment if (cmnt == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile")); } byte[] publicKey = ReadBytes(publicKeyBlobStream); if (publicKey == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile")); } // sanity check if (!AreEqual(publicKey, pk)) { throw new SSHException(Strings.GetString("WrongPassphrase")); } // first 32 bytes of secret key is used as a private key for ed25519 byte[] privateKey = new byte[32]; if (sk.Length < privateKey.Length) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile")); } Buffer.BlockCopy(sk, 0, privateKey, 0, privateKey.Length); var curve = EdwardsCurve.FindByAlgorithm(PublicKeyAlgorithm.ED25519); if (curve != null) { var kp = new EDDSAKeyPair(curve, new EDDSAPublicKey(curve, publicKey), privateKey); if (!kp.CheckKeyConsistency()) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile")); } keyPair = kp; comment = cmnt; return; } } break; default: // unsupported key type; check the next key. break; } } } } throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (supported private key was not found)"); }
/// <summary> /// Read 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) { PEMKeyType keyType; String base64Text; bool encrypted = false; CipherAlgorithm?encryption = null; byte[] iv = null; int keySize = 0; int ivSize = 0; using (StreamReader sreader = GetStreamReader()) { string line = sreader.ReadLine(); if (line == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected eof)"); } if (line == PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_RSA) { keyType = PEMKeyType.RSA; } else if (line == PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_DSA) { keyType = PEMKeyType.DSA; } else if (line == PrivateKeyFileHeader.SSH2_OPENSSH_HEADER_ECDSA) { keyType = PEMKeyType.ECDSA; } else { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected key type)"); } string footer = line.Replace("BEGIN", "END"); StringBuilder buf = new StringBuilder(); comment = String.Empty; while (true) { line = sreader.ReadLine(); if (line == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (unexpected eof)"); } if (line == footer) { break; } if (line.IndexOf(':') >= 0) { if (line.StartsWith("Proc-Type:")) { string[] w = line.Substring("Proc-Type:".Length).Trim().Split(','); if (w.Length < 1) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid Proc-Type)"); } if (w[0] != "4") { throw new SSHException(Strings.GetString("UnsupportedPrivateKeyFormat") + " (" + Strings.GetString("Reason_UnsupportedProcType") + ")"); } if (w.Length >= 2 && w[1] == "ENCRYPTED") { encrypted = true; } } else if (line.StartsWith("DEK-Info:")) { string[] w = line.Substring("DEK-Info:".Length).Trim().Split(','); if (w.Length < 2) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid DEK-Info)"); } switch (w[0]) { case "DES-EDE3-CBC": encryption = CipherAlgorithm.TripleDES; ivSize = 8; keySize = 24; break; case "AES-128-CBC": encryption = CipherAlgorithm.AES128; ivSize = 16; keySize = 16; break; default: throw new SSHException(Strings.GetString("UnsupportedPrivateKeyFormat") + " (" + Strings.GetString("Reason_UnsupportedEncryptionType") + ")"); } iv = HexToByteArray(w[1]); if (iv == null || iv.Length != ivSize) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid IV)"); } } } else { buf.Append(line); } } base64Text = buf.ToString(); } byte[] keydata = Base64.Decode(Encoding.ASCII.GetBytes(base64Text)); if (encrypted) { if (!encryption.HasValue || iv == null) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (missing encryption type or IV)"); } byte[] key = PassphraseToKey(passphrase, iv, keySize); Cipher cipher = CipherFactory.CreateCipher(SSHProtocol.SSH2, encryption.Value, key, iv); if (keydata.Length % cipher.BlockSize != 0) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid key data size)"); } cipher.Decrypt(keydata, 0, keydata.Length, keydata, 0); } using (MemoryStream keyDataStream = new MemoryStream(keydata, false)) { BERReader reader = new BERReader(keyDataStream); if (!reader.ReadSequence()) { throw new SSHException(Strings.GetString("WrongPassphrase")); } if (keyType == PEMKeyType.RSA) { /* from OpenSSL rsa_asn1.c * * ASN1_SIMPLE(RSA, version, LONG), * ASN1_SIMPLE(RSA, n, BIGNUM), * ASN1_SIMPLE(RSA, e, BIGNUM), * ASN1_SIMPLE(RSA, d, BIGNUM), * ASN1_SIMPLE(RSA, p, BIGNUM), * ASN1_SIMPLE(RSA, q, BIGNUM), * ASN1_SIMPLE(RSA, dmp1, BIGNUM), * ASN1_SIMPLE(RSA, dmq1, BIGNUM), * ASN1_SIMPLE(RSA, iqmp, BIGNUM) */ BigInteger v, n, e, d, p, q, dmp1, dmq1, iqmp; if (!reader.ReadInteger(out v) || !reader.ReadInteger(out n) || !reader.ReadInteger(out e) || !reader.ReadInteger(out d) || !reader.ReadInteger(out p) || !reader.ReadInteger(out q) || !reader.ReadInteger(out dmp1) || !reader.ReadInteger(out dmq1) || !reader.ReadInteger(out iqmp)) { throw new SSHException(Strings.GetString("WrongPassphrase")); } BigInteger u = p.ModInverse(q); // inverse of p mod q keyPair = new RSAKeyPair(e, d, n, u, p, q); } else if (keyType == PEMKeyType.DSA) { /* from OpenSSL dsa_asn1.c * * ASN1_SIMPLE(DSA, version, LONG), * ASN1_SIMPLE(DSA, p, BIGNUM), * ASN1_SIMPLE(DSA, q, BIGNUM), * ASN1_SIMPLE(DSA, g, BIGNUM), * ASN1_SIMPLE(DSA, pub_key, BIGNUM), * ASN1_SIMPLE(DSA, priv_key, BIGNUM) */ BigInteger v, p, q, g, y, x; if (!reader.ReadInteger(out v) || !reader.ReadInteger(out p) || !reader.ReadInteger(out q) || !reader.ReadInteger(out g) || !reader.ReadInteger(out y) || !reader.ReadInteger(out x)) { throw new SSHException(Strings.GetString("WrongPassphrase")); } keyPair = new DSAKeyPair(p, g, q, y, x); } else if (keyType == PEMKeyType.ECDSA) { /* from OpenSSL ec_asn1.c * * ASN1_SIMPLE(EC_PRIVATEKEY, version, LONG), * ASN1_SIMPLE(EC_PRIVATEKEY, privateKey, ASN1_OCTET_STRING), * ASN1_EXP_OPT(EC_PRIVATEKEY, parameters, ECPKPARAMETERS, 0), * ------ * ASN1_SIMPLE(ECPKPARAMETERS, value.named_curve, ASN1_OBJECT), * ------ * ASN1_EXP_OPT(EC_PRIVATEKEY, publicKey, ASN1_BIT_STRING, 1) */ int len; byte[] privateKey; byte[] publicKey; string namedCurve; BigInteger v; if (!reader.ReadInteger(out v) || !reader.ReadOctetString(out privateKey) || !reader.ReadTag(BERReader.TagClass.ContextSpecific, true, 0, out len) || !reader.ReadObjectIdentifier(out namedCurve) || !reader.ReadTag(BERReader.TagClass.ContextSpecific, true, 1, out len) || !reader.ReadBitString(out publicKey)) { throw new SSHException(Strings.GetString("WrongPassphrase")); } EllipticCurve curve = EllipticCurve.FindByOID(namedCurve); if (curve == null) { throw new SSHException(Strings.GetString("UnsupportedEllipticCurveInKeyPair")); } ECPoint ecPublicKeyPoint; if (!ECPoint.Parse(publicKey, curve, out ecPublicKeyPoint)) { throw new SSHException(Strings.GetString("KeysAreBroken")); } var ecKeyPair = new ECDSAKeyPair(curve, new ECDSAPublicKey(curve, ecPublicKeyPoint), new BigInteger(privateKey)); if (!ecKeyPair.CheckKeyConsistency()) { throw new SSHException(Strings.GetString("KeysAreBroken")); } keyPair = ecKeyPair; } else { throw new SSHException("Unknown file type. This should not happen."); } } }
/// <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 if (keyTypeName.StartsWith("ecdsa-sha2-")) { keyType = KeyType.ECDSA; } else if (keyTypeName == "ssh-ed25519") { keyType = KeyType.ED25519; } 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; passphrase = ""; // prevent HMAC error } 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); string magic = reader.ReadString(); if (magic != "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); string magic = reader.ReadString(); if (magic != "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 if (keyType == KeyType.ECDSA) { SSH2DataReader reader = new SSH2DataReader(publicBlob); string algorithmName = reader.ReadString(); string curveName = reader.ReadString(); byte[] publicKeyPt = reader.ReadByteString(); reader = new SSH2DataReader(privateBlob); BigInteger privateKey = reader.ReadMPInt(); EllipticCurve curve = EllipticCurve.FindByName(curveName); if (curve == null) { throw new SSHException(Strings.GetString("UnsupportedEllipticCurve") + " : " + curveName); } ECPoint publicKey; if (!ECPoint.Parse(publicKeyPt, curve, out publicKey)) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (parsing public key failed)"); } keyPair = new ECDSAKeyPair(curve, new ECDSAPublicKey(curve, publicKey), privateKey); if (!((ECDSAKeyPair)keyPair).CheckKeyConsistency()) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid key pair)"); } } else if (keyType == KeyType.ED25519) { SSH2DataReader reader = new SSH2DataReader(publicBlob); string algorithmName = reader.ReadString(); byte[] publicKey = reader.ReadByteString(); reader = new SSH2DataReader(privateBlob); byte[] privateKey = reader.ReadByteString(); EdwardsCurve curve = EdwardsCurve.FindByAlgorithm(PublicKeyAlgorithm.ED25519); if (curve == null) { throw new SSHException(Strings.GetString("UnsupportedEllipticCurve")); } keyPair = new EDDSAKeyPair(curve, new EDDSAPublicKey(curve, publicKey), privateKey); if (!((EDDSAKeyPair)keyPair).CheckKeyConsistency()) { throw new SSHException(Strings.GetString("NotValidPrivateKeyFile") + " (invalid key pair)"); } } else { throw new SSHException("Unknown file type. This should not happen."); } }
/* * Format style note * ---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ---- * Comment: ******* * <base64-encoded body> * ---- END SSH2 ENCRYPTED PRIVATE KEY ---- * * body = MAGIC_VAL || body-length || type(string) || encryption-algorithm-name(string) || encrypted-body(string) * encrypted-body = array of BigInteger(algorithm-specific) */ internal static SSH2UserAuthKey FromSECSHStyleStream(StreamReader r, string passphrase) { string l = r.ReadLine(); StringBuilder buf = new StringBuilder(); while (l != "---- END SSH2 ENCRYPTED PRIVATE KEY ----") { if (l.IndexOf(':') == -1) { buf.Append(l); } else { while (l.EndsWith("\\")) { l = r.ReadLine(); } } l = r.ReadLine(); if (l == null) { throw new Exception(resLoader.GetString("BrokenKeyFile")); } } //r.Close(); r.Dispose(); byte[] keydata = Base64.Decode(Encoding.UTF8.GetBytes(buf.ToString())); SSH2DataReader re = new SSH2DataReader(keydata); int magic = re.ReadInt32(); if (magic != SSH_COM_MAGIC_VAL) { throw new Exception(resLoader.GetString("BrokenKeyFile")); } int privateKeyLen = re.ReadInt32(); byte[] tmpstr = re.ReadString(); string type = Encoding.UTF8.GetString(tmpstr, 0, tmpstr.Length); tmpstr = re.ReadString(); string ciphername = Encoding.UTF8.GetString(tmpstr, 0, tmpstr.Length); int bufLen = re.ReadInt32(); if (ciphername != "none") { CipherAlgorithm algo = CipherFactory.SSH2NameToAlgorithm(ciphername); byte[] key = PassphraseToKey(passphrase, CipherFactory.GetKeySize(algo)); Cipher c = CipherFactory.CreateCipher(SSHProtocol.SSH2, algo, key); byte[] tmp = new Byte[re.Image.Length - re.Offset]; c.Decrypt(re.Image, re.Offset, re.Image.Length - re.Offset, tmp, 0); re = new SSH2DataReader(tmp); } int parmLen = re.ReadInt32(); if (parmLen < 0 || parmLen > re.Rest) { throw new Exception(resLoader.GetString("WrongPassphrase")); } if (type.IndexOf("if-modn") != -1) { //mindterm mistaken this order of BigIntegers BigInteger e = re.ReadBigIntWithBits(); BigInteger d = re.ReadBigIntWithBits(); BigInteger n = re.ReadBigIntWithBits(); BigInteger u = re.ReadBigIntWithBits(); BigInteger p = re.ReadBigIntWithBits(); BigInteger q = re.ReadBigIntWithBits(); return(new SSH2UserAuthKey(new RSAKeyPair(e, d, n, u, p, q))); } else if (type.IndexOf("dl-modp") != -1) { if (re.ReadInt32() != 0) { throw new Exception(resLoader.GetString("BrokenKeyFile")); } BigInteger p = re.ReadBigIntWithBits(); BigInteger g = re.ReadBigIntWithBits(); BigInteger q = re.ReadBigIntWithBits(); BigInteger y = re.ReadBigIntWithBits(); BigInteger x = re.ReadBigIntWithBits(); return(new SSH2UserAuthKey(new DSAKeyPair(p, g, q, y, x))); } else { throw new Exception(resLoader.GetString("KeyFormatUnknown")); } }