Skip to main content

rust_pdf_box/crypto/
pkcs7.rs

1use crate::crypto::asn1::DerElement;
2use crate::crypto::x509::SigningKey;
3use crate::error::PdfResult;
4use chrono::Utc;
5use rsa::pkcs1v15::SigningKey as RsaSigningKey;
6use rsa::signature::{SignatureEncoding, Signer as RsaSignerTrait};
7use sha2::Sha256;
8
9/// Generates a detached PKCS#7 / CMS SignedData structure for PDF digital signing.
10pub fn generate_pkcs7_detached(key: &SigningKey, byte_range_sha256: &[u8]) -> PdfResult<Vec<u8>> {
11    let id_data = vec![1, 2, 840, 113549, 1, 7, 1];
12    let id_signed_data = vec![1, 2, 840, 113549, 1, 7, 2];
13    let id_sha256 = vec![2, 16, 840, 1, 101, 3, 4, 2, 1];
14    let rsa_encryption = vec![1, 2, 840, 113549, 1, 1, 1];
15    let id_content_type = vec![1, 2, 840, 113549, 1, 9, 3];
16    let id_message_digest = vec![1, 2, 840, 113549, 1, 9, 4];
17    let id_signing_time = vec![1, 2, 840, 113549, 1, 9, 5];
18
19    // 1. Signed Attributes (SET OF Attribute)
20    // Attribute 1: contentType -> id-data
21    let attr_content_type = DerElement::Sequence(vec![
22        DerElement::ObjectIdentifier(id_content_type),
23        DerElement::Set(vec![DerElement::ObjectIdentifier(id_data.clone())]),
24    ]);
25
26    // Attribute 2: messageDigest -> hash of PDF byte ranges
27    let attr_message_digest = DerElement::Sequence(vec![
28        DerElement::ObjectIdentifier(id_message_digest),
29        DerElement::Set(vec![DerElement::OctetString(byte_range_sha256.to_vec())]),
30    ]);
31
32    // Attribute 3: signingTime
33    let now = Utc::now();
34    let time_str = now.format("%y%m%d%H%M%SZ").to_string();
35    let attr_signing_time = DerElement::Sequence(vec![
36        DerElement::ObjectIdentifier(id_signing_time),
37        DerElement::Set(vec![DerElement::UtcTime(time_str)]),
38    ]);
39
40    // In DER, attributes in a SET must be sorted by DER encoded bytes
41    let mut raw_attrs = vec![attr_content_type, attr_message_digest, attr_signing_time];
42    raw_attrs.sort_by_key(|a| a.to_der());
43
44    let signed_attrs_set = DerElement::Set(raw_attrs.clone());
45    let signed_attrs_der = signed_attrs_set.to_der();
46
47    // 2. Sign the signed attributes DER bytes
48    let signer = RsaSigningKey::<Sha256>::new(key.private_key.clone());
49    let signature = signer.sign(&signed_attrs_der);
50
51    // 3. SignerIdentifier: IssuerAndSerialNumber
52    let issuer_seq = DerElement::Sequence(vec![DerElement::Set(vec![DerElement::Sequence(vec![
53        DerElement::ObjectIdentifier(vec![2, 5, 4, 3]),
54        DerElement::Utf8String("Techwasti Infotech".to_string()),
55    ])])]);
56
57    let issuer_and_serial = DerElement::Sequence(vec![
58        issuer_seq,
59        DerElement::integer_from_bytes(&key.serial_number),
60    ]);
61
62    // 4. SignerInfo
63    // [0] tag for authenticatedAttributes in SignerInfo
64    let auth_attrs_tagged =
65        DerElement::ContextSpecific(0, Box::new(DerElement::Set(raw_attrs)), true);
66
67    let signer_info = DerElement::Sequence(vec![
68        DerElement::integer_from_u64(1), // version = 1
69        issuer_and_serial,
70        DerElement::Sequence(vec![
71            DerElement::ObjectIdentifier(id_sha256.clone()),
72            DerElement::Null,
73        ]),
74        auth_attrs_tagged,
75        DerElement::Sequence(vec![
76            DerElement::ObjectIdentifier(rsa_encryption),
77            DerElement::Null,
78        ]),
79        DerElement::OctetString(signature.to_vec()),
80    ]);
81
82    // 5. EncapsulatedContentInfo: id-data, no content (detached)
83    let encap_content_info = DerElement::Sequence(vec![DerElement::ObjectIdentifier(id_data)]);
84
85    // 6. Certificates [0] IMPLICIT
86    // We embed the raw DER bytes of the signer's certificate directly
87    let cert_tagged = DerElement::ContextSpecific(
88        0,
89        Box::new(DerElement::OctetString(key.certificate_der.clone())),
90        true,
91    );
92
93    // 7. SignedData
94    let signed_data = DerElement::Sequence(vec![
95        DerElement::integer_from_u64(1), // version = 1
96        DerElement::Set(vec![DerElement::Sequence(vec![
97            DerElement::ObjectIdentifier(id_sha256),
98            DerElement::Null,
99        ])]),
100        encap_content_info,
101        cert_tagged,
102        DerElement::Set(vec![signer_info]),
103    ]);
104
105    // 8. Outer ContentInfo
106    let content_info = DerElement::Sequence(vec![
107        DerElement::ObjectIdentifier(id_signed_data),
108        DerElement::ContextSpecific(0, Box::new(signed_data), true),
109    ]);
110
111    Ok(content_info.to_der())
112}