#web #http #signatures

http-signatures

HTTP签名RFC的实现

15个版本 (7个破坏性更新)

0.8.0 2019年1月18日
0.6.0 2018年12月22日
0.5.2 2018年11月21日
0.1.3 2018年1月15日
0.1.2 2017年12月24日

#1936 in 密码学

Download history • Rust 包仓库 22/week @ 2023-10-26 • Rust 包仓库 9/week @ 2023-11-02 • Rust 包仓库 3/week @ 2023-11-09 • Rust 包仓库 4/week @ 2023-11-16 • Rust 包仓库 30/week @ 2023-11-23 • Rust 包仓库 48/week @ 2023-11-30 • Rust 包仓库 1/week @ 2023-12-07 • Rust 包仓库 18/week @ 2023-12-14 • Rust 包仓库 30/week @ 2023-12-21 • Rust 包仓库 15/week @ 2023-12-28 • Rust 包仓库 1/week @ 2024-01-04 • Rust 包仓库 17/week @ 2024-01-18 • Rust 包仓库 16/week @ 2024-01-25 • Rust 包仓库 31/week @ 2024-02-01 • Rust 包仓库 16/week @ 2024-02-08 • Rust 包仓库

每月80次下载

GPL-3.0许可证

88KB
1.5K SLoC

HTTP签名

此crate用于创建和验证HTTP签名,定义如下:此处。它支持Actix Web、Hyper、Rocket和Reqwest类型。未来,我也可能支持Iron中间件进行验证。

crates.io 文档

运行示例

每个示例都位于其自己的crate中,在示例文件夹

Hyper示例默认配置为相互通信。服务器运行在端口3000上,客户端在端口3000上POST。它们还使用Signature头进行请求的签名和验证。

actix-web服务器和客户端示例监听并POST到端口5000,可以相互测试。它们使用Signature头进行请求的签名和验证。

rocket 4服务器监听在端口8000上,reqwest客户端可以与之交互。这些示例使用Authorization头进行请求的签名和验证。

用法

使用Actix Web

将其添加到您的Cargo.toml

[dependencies.http-signatures]
version = "0.7"
features = ["use_actix_web"]
服务器使用

像这样在您的应用程序中使用它。

const PHRASE: &str = "Hewwo, Mr. Obama???";

#[derive(Debug, Fail)]
#[fail(display = "Error verifying signature, {}", _0)]
pub struct Error(#[cause] http_signatures::Error);

impl ResponseError for Error {}

#[derive(Clone)]
pub struct MyState {
    arc: Arc<Vec<u8>>,
}

fn index((req, state): (HttpRequest<MyState>, State<MyState>)) -> impl Responder {
    let verified = req.verify_signature_header(VerifyKey::unchecked_from_slice(&state.arc));

    verified.map_err(|e| Error(e.into())).map(|_| {
        HttpResponse::Ok()
            .content_length(PHRASE.len() as u64)
            .content_type("text/plain")
            .force_close()
            .body(format!("{}", PHRASE))
    })
}

fn main() -> Result<(), Box<dyn Fail> {
    let mut key_file = File::open("../../tests/assets/public.der")?;
    let mut key_file_vec = Vec::new();
    key_file.read_to_end(&mut key_file_vec)?;
    let key_arc = Arc::new(key_file_vec);

    let state = MyState { arc: key_arc };

    server::new(move || App::with_state(state.clone()).resource("/", |r| r.with(index)))
        .bind("127.0.0.1:5000")
        .expect("Can not bind to port 5000")
        .run();

    Ok(())
}
客户端使用
let key_id = "some-username-or-something";
let json = r#"{"library":"actix-web"}"#;

let mut req = post("https://127.0.0.1:5000")
    .content_length(json.len() as u64)
    .content_type("application/json")
    .body(json)?;

req.with_signature_header(
    key_id.to_owned(),
    CreateKey::rsa(private_key, ShaSize::SHA256),
)?;

actix_web::actix::run(move || {
    req.send().map_err(|_| ()).map(|res| {
        println!("POST: {}", res.status());
        actix_web::actix::System::current().stop();
    })
});

使用Hyper

将其添加到您的Cargo.toml

[dependencies.http-signatures]
version = "0.7"
features = ["use_hyper"]
客户端

按照以下方式构建请求时使用它。

let client = Client::new();

let json = r#"{"library":"hyper"}"#;
let mut req = Request::post("https://127.0.0.1:3000")
    .body(Body::from(json))?;
req.headers_mut().insert(
    CONTENT_TYPE,
    HeaderValue::from_str("application/json")?,
);
req.headers_mut().insert(
    CONTENT_LENGTH,
    HeaderValue::from_str(&format!("{}", json.len()))?,
);

let key_id = "some-username-or-something";
// Add the HTTP Signature
req.with_signature_header(
    key_id.into(),
    CreateKey::rsa(private_key, ShaSize::SHA256),
)?;

let post = client.request(req).and_then(|res| {
    println!("POST: {}", res.status());

    res.into_body().concat2()
});

tokio::run(post.map(|_| ()).map_err(|_| ()));
服务器

这是一个非常基础的示例服务器轮廓,应该能给您一个如何设置验证HTTP签名的Hyper服务器的总体概念。这不是实际可运行的代码。

const PHRASE: &str = "Hewwo, Mr. Obama???";

fn main() -> Result<(), Box<dyn Error> {
    let mut key_file = File::open("../../tests/assets/public.der")?;
    let mut key_file_vec = Vec::new();
    key_file.read_to_end(&mut key_file_vec)?;
    let key_arc = Arc::new(key_file_vec);

    let service = move || {
        let key = Arc::clone(&key_arc);

        service_fn(move |req: Request<Body>| {
            let verified = req.verify_signature_header(VerifyKey::unchecked_from_slice(&key));

            verified
                .into_future()
                .map_err(|e| format!("{:?}", e))
                .and_then(|_| {
                    println!("Succesfully verified request!");
                    Response::builder()
                        .header(CONTENT_LENGTH, PHRASE.len() as u64)
                        .body(Body::from(PHRASE))
                        .map_err(|e| format!("{:?}", e))
                })
        })
    };

    let addr = "127.0.0.1:3000".parse()?;
    let server = Server::bind(&addr)
        .serve(service)
        .map_err(|e| eprintln!("server error: {}", e));
    rt::run(server);

    Ok(())
}

使用Reqwest

将其添加到您的Cargo.toml

[dependencies.http-signatures]
version = "0.7"
default-features = false
features = ["use_reqwest"]

在您的代码中,按照以下方式构建请求时使用它。

let key_id = "some-username-or-something".into();

let client = Client::new();
let mut req = client.get("https://127.0.0.1:8000").build()?;

req.with_authorization_header(key_id, CreateKey::rsa(private_key, ShaSize::SHA512))?;

let res = client.execute(req)?;

使用Rocket

将其添加到您的Cargo.toml

[dependencies.http-signatures]
version = "0.7"
default-features = false
features = ["use_rocket"]

在您的代码中,像这样在路由中使用它

struct Verified;

impl<'a, 'r> FromRequest<'a, 'r> for Verified {
    type Error = ();

    fn from_request(request: &'a Request<'r>) -> Outcome<Verified, ()> {
        let res = request
            .guard::<State<Vec<u8>>>()
            .succeeded()
            .ok_or(())
            .and_then(|key| {
                request
                    .verify_authorization_header(VerifyKey::unchecked_from_slice(&key))
                    .map_err(|e| println!("Error: {:?}", e))?;

                Ok(Verified)
            });

        match res {
            Ok(verified) => Success(verified),
            Err(fail) => Failure((Status::Forbidden, fail)),
        }
    }
}

#[get("/")]
fn index(_verified: Verified) -> &'static str {
    "Successfully verified request"
}

fn main() -> Result<(), Box<dyn Error> {
    let mut key_file = File::open("../../tests/assets/public.der")?;
    let mut key_vec = Vec::new();
    key_file.read_to_end(&mut key_vec)?;

    rocket::ignite()
        .mount("/", routes![index])
        .manage(key_vec)
        .launch();

    Ok(())
}

贡献

请注意,所有贡献给此项目的代码都将根据GPL版本3进行许可。

许可证

HTTP Signatures是自由软件:您可以在自由软件基金会发布的GNU通用公共许可证的条款下重新分发和/或修改它,许可证版本为3,或(根据您的选择)许可证的任何较新版本。

HTTP Signatures是根据GNU通用公共许可证分发的,希望它将是有用的,但没有任何保证;甚至没有关于适销性或适用于特定目的的暗示保证。有关详细信息,请参阅GNU通用公共许可证。此文件是HTTP Signatures的一部分

您应该已随HTTP Signatures一起收到GNU通用公共许可证的副本。如果没有,请参阅https://gnu.ac.cn/licenses/

依赖项

~6–12MB
~322K SLoC