#x509 #客户端证书 #pem #der #http-传输 #http #pkcs7

x509-client

基于Reqwest的异步X509证书传输和解序列化客户端。传输方式:HTTP/S、文件。格式:DER、PEM、PKCS7。

2个稳定版本

2.0.1 2023年8月24日
1.1.0 2023年8月19日
1.0.3 2023年8月19日

470HTTP客户端

每月 下载 23
用于 x509-path-finder

Apache-2.0

45KB
898

X509 Client

X509 Client 是 Rust 的异步 X509 证书传输和解序列化工具。

CI Status

概述

支持的传输方式

  • HTTP/S
  • 文件

支持的编码格式

  • CER - 单个 DER 编码的证书
  • PEM - 一组 PEM 编码的证书
  • PKCS7 - DER 编码的 PKCS7 证书包

用法

默认提供基于 RustCrypto 的 DefaultX509Iterator 实现方式。

[dependencies]
x509_client = { version = "1" }

启用 openssl 功能,以访问基于 OpenSSL 的 OpenSSLX509Iterator 解序列化器。

[dependencies]
x509_client = { version = "1", features = ["openssl"] }

X509 客户端与数据模型无关。在构建客户端时,使用 turbofish 表达式选择解序列化器实现。

use x509_client::{X509Client, X509ClientConfiguration};
use x509_client::provided::default::DefaultX509Iterator;

#[tokio::test]
async fn test() {    
    // default X509 Client with the default DefaultX509Iterator 
    let client = X509Client::<DefaultX509Iterator>::default();    
    assert!(client.get(&url::Url::parse("https://127.0.0.1")?).await.is_ok());
    
    // Configured X509 Client with the default DefaultX509Iterator
    let client = X509Client::<DefaultX509Iterator>::new(X509ClientConfiguration::default());
    assert!(client.get_all(&url::Url::parse("https://127.0.0.1")?)?.into_inter().len() >= 0);
}

示例

使用默认的 DefaultX509Iterator 实现方式传输和解析单个证书和多个证书。

use cms::cert::x509::Certificate;
use x509_client::{X509Client, X509ClientConfiguration, X509ClientResult};
use x509_client::provided::default::DefaultX509Iterator;
use x509_client::reqwest::ClientBuilder;

async fn get_first_certificate(url: &url::Url) -> X509ClientResult<Certificate> {
    // default X509 Client with the default DefaultX509Iterator 
    let client = X509Client::<DefaultX509Iterator>::default();    
    Ok(client.get(&url).await?)    
}

async fn get_all_certificates(url: &url::Url) -> X509ClientResult<Vec<Certificate>> {
    // configure reqwest
    let config = X509ClientConfiguration {
        strict: true,
        files: false,
        limit: None,
        http_client: Some(
            ClientBuilder::new()
            .redirect(reqwest::redirect::Policy::limited(2))
            .build()?,
        ),
    };
        
    // Configured X509 Client with the default DefaultX509Iterator
    let client = X509Client::<DefaultX509Iterator>::new(config);
            
    // HTTP GET and parse all certificates, returning all
    Ok(client.get_all(&url).await?.into_iter().collect())    
}

实例化和配置

可以使用 crate::X509Client::default 特性实现来实例化默认的 X509 客户端。

let client = X509Client::<DefaultX509Iterator>::default();

可以通过传递 X509ClientConfiguration 到客户端构造函数 crate::X509Client::new 来配置 X509 客户端。

let client = X509Client::<DefaultX509Iterator>::new(config);

X509ClientConfiguration 结构体定义为

// Default configuration
X509ClientConfiguration {
    strict: false,
    files: false,
    limit: None,
    http_client: None
};

pub struct X509ClientConfiguration {
    /// If true, only attempt parse once.
    /// Use either filename extension or http header to determine type.
    /// If false, attempt to parse from all known formats before returning error.
    pub strict: bool,

    /// If true, allow `File` transport scheme.
    /// If false, transport attempts will fail for `File` scheme.
    pub files: bool,

    /// Limits max transfer size in bytes. If None, apply no limit.
    pub limit: Option<usize>,

    /// Optional Reqwest client.
    /// If None, a default Reqwest client will be instantiated.
    pub http_client: Option<x509_client::reqwest::Client>,
}

传输和解序列化

X509Client::get 方法传输并解析第一个证书,在空时返回错误。

X509Client::get_all 方法传输并解析所有证书。

解序列化

在解析之前,客户端将尝试确定远程证书的编码。

如果启用严格配置,客户端将只尝试解析一次。如果无法确定编码类型,客户端将立即返回错误。

如果禁用严格配置(默认),客户端将在返回错误之前尝试解析所有已知格式(从最佳猜测开始)。

某些反序列化实现可能返回一个空迭代器。PKIX(PEM)文本编码规范RFC 7468指出

解析器必须优雅地处理不符合规范的数据。

并且

文件可以包含多个文本编码实例。例如,当文件包含多个证书时,就会使用这种方法。

这意味着“空”PEM文件是有效的。因此,当禁用严格配置时,X509客户端始终尝试最后解析PEM。

对于HTTP传输,证书类型由Content-Type http头确定

  • application/pkix-cert : CER
  • application/pem-certificate-chain : PEM
  • application/pkcs7-mime : PKCS7

对于File方案,证书类型由文件扩展名(.ext)确定

  • .cer : CER
  • .pem : PEM
  • .p7c : PKCS7

API

X509客户端不关心数据模型 - 使用X509Iterator特性行定义反序列化接口。

use std::fmt::{Debug, Display};

/// X509 Deserializer API
pub trait X509Iterator: IntoIterator
where
    Self: Sized,
{
    /// Error type
    type X509IteratorError: X509IteratorError;

    /// Attempt to deserialize, assume input is a single DER-encoded certificate
    fn from_cer<T: AsRef<[u8]>>(src: T) -> Result<Self, Self::X509IteratorError>;
    /// Attempt to deserialize, assume input is a stack of zero or more PEM-encoded certificates
    fn from_pem<T: AsRef<[u8]>>(src: T) -> Result<Self, Self::X509IteratorError>;
    /// Attempt to deserialize, assume input is a DER-encoded PKCS7 certificate bundle
    fn from_pkcs7<T: AsRef<[u8]>>(src: T) -> Result<Self, Self::X509IteratorError>;
}

/// Error type bounds
pub trait X509IteratorError: Display + Debug {}

错误处理

X509Iterator实现可以返回由X509Iterator::X509IteratorError关联类型定义的任何错误类型,该类型由X509IteratorError特性行绑定。本身X509IteratorError特性行只由Display + Debug绑定。

迭代器错误将作为X509ClientError::X509IteratorError变体呈现给调用者。

错误转换的实现如下

use std::fmt::{Debug, Display, Formatter};
use x509_client::X509ClientError;
use x509_client::api::X509IteratorError;

#[derive(Debug)]
struct MyX509IteratorError;

impl Display for MyX509IteratorError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "")
    }
}

impl X509IteratorError for MyX509IteratorError {}

impl From<MyX509IteratorError> for X509ClientError {
    fn from(e: MyX509IteratorError) -> Self {
        Self::X509IteratorError(Box::new(e))
    }
}

实现

默认

如果启用默认功能,则可提供基于RustCryptoDefaultX509Iterator实现。

OpenSSL

如果启用OpenSSL功能,则可提供基于OpenSSLX509Iterator的实现。

调试

调试实现DebugX509Iterator始终可用。它将服务器返回的字节复制到Once<bytes::Bytes>迭代器。

依赖项

~6–23MB
~308K SLoC