#ssh-key #ssh-agent #private-key #ssh #encryption-key #ssh-client #decryption

russh-keys

处理SSH密钥:加载它们,解密它们,调用SSH代理

32个版本 (12个重大变更)

0.46.0-beta.3 2024年8月20日
0.45.0 2024年8月14日
0.44.0 2024年7月18日
0.43.0 2024年3月21日
0.22.0-beta.12022年3月13日

#611密码学 中排名

Download history 9116/week @ 2024-05-03 12783/week @ 2024-05-10 9879/week @ 2024-05-17 9218/week @ 2024-05-24 9065/week @ 2024-05-31 10858/week @ 2024-06-07 10212/week @ 2024-06-14 13197/week @ 2024-06-21 9140/week @ 2024-06-28 10537/week @ 2024-07-05 11951/week @ 2024-07-12 10643/week @ 2024-07-19 10334/week @ 2024-07-26 8860/week @ 2024-08-02 11105/week @ 2024-08-09 12332/week @ 2024-08-16

44,643 每月下载量
19 crate中使用 17 个直接使用

Apache-2.0

195KB
4.5K SLoC

该crate包含处理SSH密钥的方法,如crate Russh中定义的。这包括打开密钥文件、解密加密密钥以及处理代理的各种函数。

以下示例显示了如何在单个示例中完成所有这些操作:启动SSH代理服务器,用客户端连接到它,解密加密的私钥(密码是 b"blabla"),将其发送到代理,并要求代理对数据片段进行签名(以下 b"Please sign this")。

 use russh_keys::*;
 use futures::Future;

 #[derive(Clone)]
 struct X{}
 impl agent::server::Agent for X {
     fn confirm(self, _: std::sync::Arc<key::KeyPair>) -> Box<dyn Future<Output = (Self, bool)> + Send + Unpin> {
         Box::new(futures::future::ready((self, true)))
     }
 }

 const PKCS8_ENCRYPTED: &'static str = "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQITo1O0b8YrS0CAggA\nMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBBtLH4T1KOfo1GGr7salhR8BIIE\n0KN9ednYwcTGSX3hg7fROhTw7JAJ1D4IdT1fsoGeNu2BFuIgF3cthGHe6S5zceI2\nMpkfwvHbsOlDFWMUIAb/VY8/iYxhNmd5J6NStMYRC9NC0fVzOmrJqE1wITqxtORx\nIkzqkgFUbaaiFFQPepsh5CvQfAgGEWV329SsTOKIgyTj97RxfZIKA+TR5J5g2dJY\nj346SvHhSxJ4Jc0asccgMb0HGh9UUDzDSql0OIdbnZW5KzYJPOx+aDqnpbz7UzY/\nP8N0w/pEiGmkdkNyvGsdttcjFpOWlLnLDhtLx8dDwi/sbEYHtpMzsYC9jPn3hnds\nTcotqjoSZ31O6rJD4z18FOQb4iZs3MohwEdDd9XKblTfYKM62aQJWH6cVQcg+1C7\njX9l2wmyK26Tkkl5Qg/qSfzrCveke5muZgZkFwL0GCcgPJ8RixSB4GOdSMa/hAMU\nkvFAtoV2GluIgmSe1pG5cNMhurxM1dPPf4WnD+9hkFFSsMkTAuxDZIdDk3FA8zof\nYhv0ZTfvT6V+vgH3Hv7Tqcxomy5Qr3tj5vvAqqDU6k7fC4FvkxDh2mG5ovWvc4Nb\nXv8sed0LGpYitIOMldu6650LoZAqJVv5N4cAA2Edqldf7S2Iz1QnA/usXkQd4tLa\nZ80+sDNv9eCVkfaJ6kOVLk/ghLdXWJYRLenfQZtVUXrPkaPpNXgD0dlaTN8KuvML\nUw/UGa+4ybnPsdVflI0YkJKbxouhp4iB4S5ACAwqHVmsH5GRnujf10qLoS7RjDAl\no/wSHxdT9BECp7TT8ID65u2mlJvH13iJbktPczGXt07nBiBse6OxsClfBtHkRLzE\nQF6UMEXsJnIIMRfrZQnduC8FUOkfPOSXc8r9SeZ3GhfbV/DmWZvFPCpjzKYPsM5+\nN8Bw/iZ7NIH4xzNOgwdp5BzjH9hRtCt4sUKVVlWfEDtTnkHNOusQGKu7HkBF87YZ\nRN/Nd3gvHob668JOcGchcOzcsqsgzhGMD8+G9T9oZkFCYtwUXQU2XjMN0R4VtQgZ\nrAxWyQau9xXMGyDC67gQ5xSn+oqMK0HmoW8jh2LG/cUowHFAkUxdzGadnjGhMOI2\nzwNJPIjF93eDF/+zW5E1l0iGdiYyHkJbWSvcCuvTwma9FIDB45vOh5mSR+YjjSM5\nnq3THSWNi7Cxqz12Q1+i9pz92T2myYKBBtu1WDh+2KOn5DUkfEadY5SsIu/Rb7ub\n5FBihk2RN3y/iZk+36I69HgGg1OElYjps3D+A9AjVby10zxxLAz8U28YqJZm4wA/\nT0HLxBiVw+rsHmLP79KvsT2+b4Diqih+VTXouPWC/W+lELYKSlqnJCat77IxgM9e\nYIhzD47OgWl33GJ/R10+RDoDvY4koYE+V5NLglEhbwjloo9Ryv5ywBJNS7mfXMsK\n/uf+l2AscZTZ1mhtL38efTQCIRjyFHc3V31DI0UdETADi+/Omz+bXu0D5VvX+7c6\nb1iVZKpJw8KUjzeUV8yOZhvGu3LrQbhkTPVYL555iP1KN0Eya88ra+FUKMwLgjYr\nJkUx4iad4dTsGPodwEP/Y9oX/Qk3ZQr+REZ8lg6IBoKKqqrQeBJ9gkm1jfKE6Xkc\nCog3JMeTrb3LiPHgN6gU2P30MRp6L1j1J/MtlOAr5rux\n-----END ENCRYPTED PRIVATE KEY-----\n";

 #[cfg(unix)]
 fn main() {
    env_logger::try_init().unwrap_or(());
    let dir = tempdir::TempDir::new("russh").unwrap();
    let agent_path = dir.path().join("agent");

    let mut core = tokio::runtime::Runtime::new().unwrap();
    let agent_path_ = agent_path.clone();
    // Starting a server
    core.spawn(async move {
        let mut listener = tokio::net::UnixListener::bind(&agent_path_)
            .unwrap();
        russh_keys::agent::server::serve(tokio_stream::wrappers::UnixListenerStream::new(listener), X {}).await
    });
    let key = decode_secret_key(PKCS8_ENCRYPTED, Some("blabla")).unwrap();
    let public = key.clone_public_key().unwrap();
    core.block_on(async move {
        let stream = tokio::net::UnixStream::connect(&agent_path).await?;
        let mut client = agent::client::AgentClient::connect(stream);
        client.add_identity(&key, &[agent::Constraint::KeyLifetime { seconds: 60 }]).await?;
        client.request_identities().await?;
        let buf = b"signed message";
        let sig = client.sign_request(&public, russh_cryptovec::CryptoVec::from_slice(&buf[..])).await.1.unwrap();
        // Here, `sig` is encoded in a format usable internally by the SSH protocol.
        Ok::<(), Error>(())
    }).unwrap()
 }

 #[cfg(not(unix))]
 fn main() {}

依赖关系

~13–43MB
~673K SLoC