From 647ef86cd72d5646eda0901f59a68e3bd4878a9f Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 2 Feb 2012 14:14:16 +0100 Subject: Ssh daemon handles RSA host keys Solves OTP-7677 --- lib/ssh/src/ssh.hrl | 3 +- lib/ssh/src/ssh_connection_handler.erl | 47 ++++++++++++++--- lib/ssh/src/ssh_file.erl | 9 ++-- lib/ssh/src/ssh_transport.erl | 16 +++--- lib/ssh/test/ssh_basic_SUITE.erl | 60 +++++++++++++--------- .../test/ssh_basic_SUITE_data/ssh_host_rsa_key.pub | 5 ++ lib/ssh/test/ssh_sftpd_SUITE.erl | 5 +- lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl | 10 ++-- lib/ssh/test/ssh_test_lib.erl | 52 +++++++++++++++++++ lib/ssh/test/ssh_to_openssh_SUITE.erl | 1 - .../ssh_to_openssh_SUITE_data/ssh_host_rsa_key | 16 ------ 11 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 lib/ssh/test/ssh_basic_SUITE_data/ssh_host_rsa_key.pub delete mode 100644 lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key (limited to 'lib') diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 7cebec1c74..da5750b6c3 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -126,7 +126,8 @@ userauth_quiet_mode, % boolean() userauth_supported_methods , % userauth_methods, - userauth_preference + userauth_preference, + available_host_keys }). -record(alg, diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 00b30e5434..a9bf23953d 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -637,16 +637,18 @@ init_ssh(client = Role, Vsn, Version, Options, Socket) -> {ok, PeerAddr} = inet:peername(Socket), PeerName = proplists:get_value(host, Options), + KeyCb = proplists:get_value(key_cb, Options, ssh_file), #ssh{role = Role, c_vsn = Vsn, c_version = Version, - key_cb = proplists:get_value(key_cb, Options, ssh_file), + key_cb = KeyCb, io_cb = IOCb, userauth_quiet_mode = proplists:get_value(quiet_mode, Options, false), opts = Options, userauth_supported_methods = AuthMethods, - peer = {PeerName, PeerAddr} + peer = {PeerName, PeerAddr}, + available_host_keys = supported_host_keys(Role, KeyCb, Options) }; init_ssh(server = Role, Vsn, Version, Options, Socket) -> @@ -654,17 +656,50 @@ init_ssh(server = Role, Vsn, Version, Options, Socket) -> AuthMethods = proplists:get_value(auth_methods, Options, ?SUPPORTED_AUTH_METHODS), {ok, PeerAddr} = inet:peername(Socket), - + KeyCb = proplists:get_value(key_cb, Options, ssh_file), + #ssh{role = Role, s_vsn = Vsn, s_version = Version, - key_cb = proplists:get_value(key_cb, Options, ssh_file), + key_cb = KeyCb, io_cb = proplists:get_value(io_cb, Options, ssh_io), opts = Options, userauth_supported_methods = AuthMethods, - peer = {undefined, PeerAddr} + peer = {undefined, PeerAddr}, + available_host_keys = supported_host_keys(Role, KeyCb, Options) }. +supported_host_keys(client, _, _) -> + ["ssh-rsa", "ssh-dss"]; +supported_host_keys(server, KeyCb, Options) -> + lists:foldl(fun(Type, Acc) -> + case available_host_key(KeyCb, Type, Options) of + {error, _} -> + Acc; + Alg -> + [Alg | Acc] + end + end, [], + %% Prefered alg last so no need to reverse + ["ssh-dss", "ssh-rsa"]). + +available_host_key(KeyCb, "ssh-dss"= Alg, Opts) -> + Scope = proplists:get_value(key_scope, Opts, system), + case KeyCb:private_host_dsa_key(Scope, Opts) of + {ok, _} -> + Alg; + Other -> + Other + end; +available_host_key(KeyCb, "ssh-rsa" = Alg, Opts) -> + Scope = proplists:get_value(key_scope, Opts, system), + case KeyCb:private_host_rsa_key(Scope, Opts) of + {ok, _} -> + Alg; + Other -> + Other + end. + send_msg(Msg, #state{socket = Socket, transport_cb = Transport}) -> Transport:send(Socket, Msg). diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index 49106ccdb3..d5cacc3294 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -30,7 +30,8 @@ -export([public_host_dsa_key/2,private_host_dsa_key/2, public_host_rsa_key/2,private_host_rsa_key/2, - public_host_key/2,private_host_key/2, + %%public_host_key/2, + private_host_key/2, lookup_host_key/3, add_host_key/3, lookup_user_key/4, ssh_dir/2, file_name/3]). @@ -62,9 +63,9 @@ private_host_rsa_key(Type, Opts) -> Password = proplists:get_value(password, Opts, ignore), decode(File, Password). -public_host_key(Type, Opts) -> - File = file_name(Type, "ssh_host_key", Opts), - decode(File, public_key). +%% public_host_key(Type, Opts) -> +%% File = file_name(Type, "ssh_host_key", Opts), +%% decode(File, public_key). private_host_key(Type, Opts) -> File = file_name(Type, "ssh_host_key", Opts), diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index 3fef42a1ac..f99b9c9ca7 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -203,24 +203,24 @@ key_exchange_init_msg(Ssh0) -> {SshPacket, Ssh} = ssh_packet(Msg, Ssh0), {Msg, SshPacket, Ssh}. -kex_init(#ssh{role = Role, opts = Opts}) -> +kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) -> Random = ssh_bits:random(16), Compression = case proplists:get_value(compression, Opts, none) of zlib -> ["zlib", "none"]; none -> ["none", "zlib"] end, - kexinit_messsage(Role, Random, Compression). + kexinit_messsage(Role, Random, Compression, HostKeyAlgs). key_init(client, Ssh, Value) -> Ssh#ssh{c_keyinit = Value}; key_init(server, Ssh, Value) -> Ssh#ssh{s_keyinit = Value}. -kexinit_messsage(client, Random, Compression) -> +kexinit_messsage(client, Random, Compression, HostKeyAlgs) -> #ssh_msg_kexinit{ cookie = Random, kex_algorithms = ["diffie-hellman-group1-sha1"], - server_host_key_algorithms = ["ssh-rsa", "ssh-dss"], + server_host_key_algorithms = HostKeyAlgs, encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"], encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"], mac_algorithms_client_to_server = ["hmac-sha1"], @@ -231,11 +231,11 @@ kexinit_messsage(client, Random, Compression) -> languages_server_to_client = [] }; -kexinit_messsage(server, Random, Compression) -> +kexinit_messsage(server, Random, Compression, HostKeyAlgs) -> #ssh_msg_kexinit{ cookie = Random, kex_algorithms = ["diffie-hellman-group1-sha1"], - server_host_key_algorithms = ["ssh-dss"], + server_host_key_algorithms = HostKeyAlgs, encryption_algorithms_client_to_server = ["aes128-cbc","3des-cbc"], encryption_algorithms_server_to_client = ["aes128-cbc","3des-cbc"], mac_algorithms_client_to_server = ["hmac-sha1"], @@ -426,8 +426,8 @@ get_host_key(SSH) -> Error -> exit(Error) end; - _ -> - exit({error, bad_key_type}) + Foo -> + exit({error, {Foo, bad_key_type}}) end. sign_host_key(_Ssh, #'RSAPrivateKey'{} = Private, H) -> diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index b44afe79c7..ebbf044a8b 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -39,12 +39,8 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - test_server:format("Init per suite", []), case catch crypto:start() of ok -> - DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - ssh_test_lib:setup_dsa(DataDir, PrivDir), Config; _Else -> {skip, "Crypto could not be started!"} @@ -56,10 +52,7 @@ init_per_suite(Config) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- -end_per_suite(Config) -> - test_server:format("End per suite", []), - PrivDir = ?config(priv_dir, Config), - ssh_test_lib:clean_dsa(PrivDir), +end_per_suite(_Config) -> ssh:stop(), crypto:stop(), ok. @@ -93,9 +86,9 @@ init_per_testcase(_TestCase, Config) -> end_per_testcase(TestCase, Config) when TestCase == server_password_option; TestCase == server_userpassword_option -> UserDir = filename:join(?config(priv_dir, Config), nopubkey), - file:del_dir(UserDir), + ssh_test_lib:del_dirs(UserDir), end_per_testcase(Config); -end_per_testcase(_TestCase, Config) -> +end_per_testcase(TestCase, Config) -> end_per_testcase(Config). end_per_testcase(_Config) -> ssh:stop(), @@ -110,18 +103,39 @@ end_per_testcase(_Config) -> %% Description: Returns a list of all test cases in this test suite %%-------------------------------------------------------------------- all() -> - [exec, exec_compressed, shell, daemon_already_started, - server_password_option, server_userpassword_option, - known_hosts]. + [{group, dsa_key}, + {group, rsa_key}, + daemon_already_started, + server_password_option, server_userpassword_option]. groups() -> - []. + [{dsa_key, [], [exec, exec_compressed, shell, known_hosts]}, + {rsa_key, [], [exec, exec_compressed, shell, known_hosts]} + ]. -init_per_group(_GroupName, Config) -> - Config. +init_per_group(dsa_key, Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + Config; +init_per_group(rsa_key, Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_rsa(DataDir, PrivDir), + Config; +init_per_group(_, Config) -> + Config. -end_per_group(_GroupName, Config) -> - Config. +end_per_group(dsa_key, Config) -> + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:clean_dsa(PrivDir), + Config; +end_per_group(rsa_key, Config) -> + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:clean_rsa(PrivDir), + Config; +end_per_group(_, Config) -> + Config. %% Test cases starts here. %%-------------------------------------------------------------------- @@ -133,7 +147,7 @@ exec(suite) -> exec(Config) when is_list(Config) -> process_flag(trap_exit, true), - SystemDir = ?config(data_dir, Config), + SystemDir = filename:join(?config(priv_dir, Config), system), UserDir = ?config(priv_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, @@ -179,7 +193,7 @@ exec_compressed(suite) -> exec_compressed(Config) when is_list(Config) -> process_flag(trap_exit, true), - SystemDir = ?config(data_dir, Config), + SystemDir = filename:join(?config(priv_dir, Config), system), UserDir = ?config(priv_dir, Config), {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, @@ -213,7 +227,7 @@ shell(suite) -> shell(Config) when is_list(Config) -> process_flag(trap_exit, true), - SystemDir = ?config(data_dir, Config), + SystemDir = filename:join(?config(priv_dir, Config), system), UserDir = ?config(priv_dir, Config), {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},{user_dir, UserDir}, @@ -378,10 +392,10 @@ known_hosts(doc) -> known_hosts(suite) -> []; known_hosts(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), + SystemDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), - {Pid, Host, Port} = ssh_test_lib:daemon([{user_dir, PrivDir},{system_dir, DataDir}, + {Pid, Host, Port} = ssh_test_lib:daemon([{user_dir, PrivDir},{system_dir, SystemDir}, {failfun, fun ssh_test_lib:failfun/2}]), KnownHosts = filename:join(PrivDir, "known_hosts"), diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_rsa_key.pub new file mode 100644 index 0000000000..75d2025c71 --- /dev/null +++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_rsa_key.pub @@ -0,0 +1,5 @@ +---- BEGIN SSH2 PUBLIC KEY ---- +AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8 +semM4q843337zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RW +RWzjaxSB6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4Q== +---- END SSH2 PUBLIC KEY ---- diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 37a1c63b6e..4af4c28ecf 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -100,9 +100,10 @@ init_per_testcase(TestCase, Config) -> prep(Config), PrivDir = ?config(priv_dir, Config), ClientUserDir = filename:join(PrivDir, nopubkey), - SysDir = ?config(data_dir, Config), + SystemDir = filename:join(?config(priv_dir, Config), system), + {ok, Sftpd} = - ssh_sftpd:listen(?SFPD_PORT, [{system_dir, SysDir}, + ssh_sftpd:listen(?SFPD_PORT, [{system_dir, SystemDir}, {user_dir, PrivDir}, {user_passwords,[{?USER, ?PASSWD}]}, {pwdfun, fun(_,_) -> true end}]), diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index ca189d4b02..4c469ed5f7 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -90,7 +90,7 @@ end_per_suite(Config) -> init_per_testcase(TestCase, Config) -> ssh:start(), PrivDir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), + SystemDir = filename:join(PrivDir, system), Options = case atom_to_list(TestCase) of @@ -98,7 +98,7 @@ init_per_testcase(TestCase, Config) -> Spec = ssh_sftpd:subsystem_spec([{file_handler, ssh_sftpd_file_alt}]), - [{system_dir, DataDir}, + [{system_dir, SystemDir}, {user_dir, PrivDir}, {subsystems, [Spec]}]; "root_dir" -> @@ -106,19 +106,19 @@ init_per_testcase(TestCase, Config) -> Root = filename:join(Privdir, root), file:make_dir(Root), Spec = ssh_sftpd:subsystem_spec([{root,Root}]), - [{system_dir, DataDir}, + [{system_dir, SystemDir}, {user_dir, PrivDir}, {subsystems, [Spec]}]; "list_dir_limited" -> Spec = ssh_sftpd:subsystem_spec([{max_files,1}]), - [{system_dir, DataDir}, + [{system_dir, SystemDir}, {user_dir, PrivDir}, {subsystems, [Spec]}]; _ -> [{user_dir, PrivDir}, - {system_dir, DataDir}] + {system_dir, SystemDir}] end, {Sftpd, Host, Port} = ssh_test_lib:daemon(Options), diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl index abe65700c5..d8dd4cc0ac 100644 --- a/lib/ssh/test/ssh_test_lib.erl +++ b/lib/ssh/test/ssh_test_lib.erl @@ -220,19 +220,44 @@ known_hosts(BR) -> setup_dsa(DataDir, UserDir) -> file:copy(filename:join(DataDir, "id_dsa"), filename:join(UserDir, "id_dsa")), + System = filename:join(UserDir, "system"), + file:make_dir(System), + file:copy(filename:join(DataDir, "ssh_host_dsa_key"), filename:join(System, "ssh_host_dsa_key")), + file:copy(filename:join(DataDir, "ssh_host_dsa_key.pub"), filename:join(System, "ssh_host_dsa_key.pub")), setup_dsa_known_host(DataDir, UserDir), setup_dsa_auth_keys(DataDir, UserDir). +setup_rsa(DataDir, UserDir) -> + file:copy(filename:join(DataDir, "id_rsa"), filename:join(UserDir, "id_rsa")), + System = filename:join(UserDir, "system"), + file:make_dir(System), + file:copy(filename:join(DataDir, "ssh_host_rsa_key"), filename:join(System, "ssh_host_rsa_key")), + file:copy(filename:join(DataDir, "ssh_host_rsa_key"), filename:join(System, "ssh_host_rsa_key.pub")), + setup_rsa_known_host(DataDir, UserDir), + setup_rsa_auth_keys(DataDir, UserDir). + clean_dsa(UserDir) -> + del_dirs(filename:join(UserDir, "system")), file:delete(filename:join(UserDir,"id_dsa")), file:delete(filename:join(UserDir,"known_hosts")), file:delete(filename:join(UserDir,"authorized_keys")). +clean_rsa(UserDir) -> + del_dirs(filename:join(UserDir, "system")), + file:delete(filename:join(UserDir,"id_rsa")), + file:delete(filename:join(UserDir,"known_hosts")), + file:delete(filename:join(UserDir,"authorized_keys")). + setup_dsa_known_host(SystemDir, UserDir) -> {ok, SshBin} = file:read_file(filename:join(SystemDir, "ssh_host_dsa_key.pub")), [{Key, _}] = public_key:ssh_decode(SshBin, public_key), setup_known_hosts(Key, UserDir). +setup_rsa_known_host(SystemDir, UserDir) -> + {ok, SshBin} = file:read_file(filename:join(SystemDir, "ssh_host_rsa_key.pub")), + [{Key, _}] = public_key:ssh_decode(SshBin, public_key), + setup_known_hosts(Key, UserDir). + setup_known_hosts(Key, UserDir) -> {ok, Hostname} = inet:gethostname(), {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet), @@ -253,7 +278,34 @@ setup_dsa_auth_keys(Dir, UserDir) -> Dss = #'Dss-Parms'{p=P, q=Q, g=G}, setup_auth_keys([{{PKey, Dss}, [{comment, "Test"}]}], UserDir). +setup_rsa_auth_keys(Dir, UserDir) -> + {ok, Pem} = file:read_file(filename:join(Dir, "id_rsa")), + RSA = public_key:pem_entry_decode(hd(public_key:pem_decode(Pem))), + #'RSAPrivateKey'{publicExponent = E, modulus = N} = RSA, + PKey = #'RSAPublicKey'{publicExponent = E, modulus = N}, + setup_auth_keys([{ PKey, [{comment, "Test"}]}], UserDir). + setup_auth_keys(Keys, Dir) -> AuthKeys = public_key:ssh_encode(Keys, auth_keys), AuthKeysFile = filename:join(Dir, "authorized_keys"), file:write_file(AuthKeysFile, AuthKeys). + + +del_dirs(Dir) -> + case file:list_dir(Dir) of + {ok, []} -> + file:del_dir(Dir); + {ok, Files} -> + lists:foreach(fun(File) -> + FullPath = filename:join(Dir,File), + case filelib:is_dir(FullPath) of + true -> + del_dirs(FullPath), + file:del_dir(FullPath); + false -> + file:delete(FullPath) + end + end, Files); + _ -> + ok + end. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index 9fb24ba3f3..dfe526564d 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -76,7 +76,6 @@ end_per_suite(_Config) -> %% Description: Initialization before each test case %%-------------------------------------------------------------------- init_per_testcase(_TestCase, Config) -> - test_server:format("Init per test case~n", []), ssh:start(), Config. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key deleted file mode 100644 index 79968bdd7d..0000000000 --- a/lib/ssh/test/ssh_to_openssh_SUITE_data/ssh_host_rsa_key +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337 -zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB -6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB -AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW -NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++ -udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW -WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt -n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5 -sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY -+SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt -64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB -m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT -tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR ------END RSA PRIVATE KEY----- - -- cgit v1.2.3