Skip to main content

xdoc/signature/
key.rs

1use std::fs;
2use std::path::Path;
3
4use openssl::asn1::Asn1Time;
5use openssl::x509::{X509NameRef, X509};
6use rsa::{
7    pkcs1::DecodeRsaPrivateKey,
8    pkcs1v15::{Signature as RsaPkcs1v15Signature, SigningKey, VerifyingKey},
9    pkcs8::DecodePrivateKey,
10    RsaPrivateKey, RsaPublicKey,
11};
12use sha2::{Digest, Sha256};
13use signature::{SignatureEncoding, Signer, Verifier};
14
15use crate::core::{ErrorKind, XmlError, XmlResult};
16
17use super::base64::decode_standard_base64;
18use super::validation_data::StaticValidationDataProvider;
19use super::SignatureAlgorithm;
20
21/// Private key, signing certificate, and certificate chain extracted from a
22/// PKCS#12/PFX container.
23///
24/// The structure is byte-oriented by design. It does not validate trust,
25/// revocation, certificate policies, or legal identity; callers can feed these
26/// bytes into a `SigningProvider` or a stricter validation layer.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct Pkcs12Credential {
29    private_key_der: Vec<u8>,
30    certificate_der: Vec<u8>,
31    certificate_chain_der: Vec<Vec<u8>>,
32}
33
34impl Pkcs12Credential {
35    pub fn from_file(path: impl AsRef<Path>, password: &str) -> XmlResult<Self> {
36        let path = path.as_ref();
37        let pkcs12_der = read_file_bytes(path, "cannot read PKCS#12/PFX file")?;
38        Self::from_der(&pkcs12_der, password)
39    }
40
41    pub fn from_der(pkcs12_der: &[u8], password: &str) -> XmlResult<Self> {
42        let pfx = openssl::pkcs12::Pkcs12::from_der(pkcs12_der)
43            .map_err(|error| credential_error("invalid PKCS#12/PFX DER", error))?;
44        let parsed = pfx
45            .parse2(password)
46            .map_err(|error| credential_error("cannot decrypt PKCS#12/PFX", error))?;
47        let private_key = parsed.pkey.ok_or_else(|| {
48            XmlError::new(
49                ErrorKind::Signature,
50                "PKCS#12/PFX does not contain a private key",
51            )
52        })?;
53        let certificate = parsed.cert.ok_or_else(|| {
54            XmlError::new(
55                ErrorKind::Signature,
56                "PKCS#12/PFX does not contain an X.509 certificate",
57            )
58        })?;
59
60        let private_key_der = private_key
61            .private_key_to_der()
62            .map_err(|error| credential_error("cannot export PKCS#12/PFX private key", error))?;
63        let certificate_der = certificate
64            .to_der()
65            .map_err(|error| credential_error("cannot export PKCS#12/PFX certificate", error))?;
66        let mut certificate_chain_der = vec![certificate_der.clone()];
67        if let Some(ca) = parsed.ca {
68            for certificate in ca {
69                certificate_chain_der.push(certificate.to_der().map_err(|error| {
70                    credential_error("cannot export PKCS#12/PFX CA certificate", error)
71                })?);
72            }
73        }
74
75        Ok(Self {
76            private_key_der,
77            certificate_der,
78            certificate_chain_der,
79        })
80    }
81
82    pub fn private_key_der(&self) -> &[u8] {
83        &self.private_key_der
84    }
85
86    pub fn certificate_der(&self) -> &[u8] {
87        &self.certificate_der
88    }
89
90    pub fn certificate_chain_der(&self) -> &[Vec<u8>] {
91        &self.certificate_chain_der
92    }
93}
94
95/// Generic certificate metadata used by XMLDSig/XAdES.
96///
97/// The DER bytes are authoritative for digesting and `KeyInfo` output. Issuer
98/// and serial are supplied by the caller or credential backend; this type does
99/// not validate legal trust, revocation, or certificate policies.
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct CertificateDetails {
102    der: Vec<u8>,
103    issuer_name: Option<String>,
104    serial_number: Option<String>,
105}
106
107impl CertificateDetails {
108    pub fn new(der: impl Into<Vec<u8>>) -> Self {
109        Self {
110            der: der.into(),
111            issuer_name: None,
112            serial_number: None,
113        }
114    }
115
116    pub fn with_issuer_serial(
117        mut self,
118        issuer_name: impl Into<String>,
119        serial_number: impl Into<String>,
120    ) -> Self {
121        self.issuer_name = Some(issuer_name.into());
122        self.serial_number = Some(serial_number.into());
123        self
124    }
125
126    pub fn der(&self) -> &[u8] {
127        &self.der
128    }
129
130    pub fn issuer_name(&self) -> Option<&str> {
131        self.issuer_name.as_deref()
132    }
133
134    pub fn serial_number(&self) -> Option<&str> {
135        self.serial_number.as_deref()
136    }
137
138    pub fn issuer_serial(&self) -> Option<(&str, &str)> {
139        Some((self.issuer_name()?, self.serial_number()?))
140    }
141}
142
143/// Supplies certificates and signs canonical XMLDSig bytes.
144pub trait SigningProvider {
145    fn certificate_der(&self) -> XmlResult<Vec<u8>>;
146
147    fn certificate_details(&self) -> XmlResult<CertificateDetails> {
148        Ok(CertificateDetails::new(self.certificate_der()?))
149    }
150
151    fn certificate_chain_der(&self) -> XmlResult<Vec<Vec<u8>>> {
152        Ok(vec![self.certificate_der()?])
153    }
154
155    fn certificate_chain_details(&self) -> XmlResult<Vec<CertificateDetails>> {
156        Ok(self
157            .certificate_chain_der()?
158            .into_iter()
159            .map(CertificateDetails::new)
160            .collect())
161    }
162
163    /// Validates that the signing certificate is valid at a UNIX timestamp.
164    ///
165    /// Providers that do not expose a real X.509 certificate can keep the
166    /// default no-op implementation. Real certificate-backed providers should
167    /// check `NotBefore` and `NotAfter`.
168    fn ensure_certificate_valid_at(&self, _unix_timestamp: i64) -> XmlResult<()> {
169        Ok(())
170    }
171
172    fn sign(&self, algorithm: SignatureAlgorithm, data: &[u8]) -> XmlResult<Vec<u8>>;
173
174    fn verify(
175        &self,
176        algorithm: SignatureAlgorithm,
177        data: &[u8],
178        signature: &[u8],
179    ) -> XmlResult<bool> {
180        Ok(self.sign(algorithm, data)? == signature)
181    }
182}
183
184/// RSA-SHA256 provider backed by in-memory key and certificate bytes.
185///
186/// The provider does not read files, environment variables, network resources,
187/// or secret stores. Callers are responsible for loading credentials explicitly
188/// and passing private-key PEM/DER, PKCS#12/PFX DER, and certificate DER bytes
189/// into this API.
190#[derive(Debug, Clone)]
191pub struct RsaSha256SigningProvider {
192    private_key: RsaPrivateKey,
193    public_key: RsaPublicKey,
194    certificate_details: CertificateDetails,
195    certificate_chain_details: Vec<CertificateDetails>,
196}
197
198impl RsaSha256SigningProvider {
199    pub fn from_private_key(
200        private_key: RsaPrivateKey,
201        certificate_der: impl Into<Vec<u8>>,
202    ) -> Self {
203        let public_key = RsaPublicKey::from(&private_key);
204        Self::from_key_pair(private_key, public_key, certificate_der)
205    }
206
207    pub fn from_key_pair(
208        private_key: RsaPrivateKey,
209        public_key: RsaPublicKey,
210        certificate_der: impl Into<Vec<u8>>,
211    ) -> Self {
212        Self {
213            private_key,
214            public_key,
215            certificate_details: CertificateDetails::new(certificate_der),
216            certificate_chain_details: Vec::new(),
217        }
218    }
219
220    pub fn from_private_key_der(
221        private_key_der: &[u8],
222        certificate_der: impl Into<Vec<u8>>,
223    ) -> XmlResult<Self> {
224        let private_key = RsaPrivateKey::from_pkcs8_der(private_key_der)
225            .or_else(|_| RsaPrivateKey::from_pkcs1_der(private_key_der))
226            .map_err(|error| credential_error("invalid RSA private key DER", error))?;
227
228        Ok(Self::from_private_key(private_key, certificate_der))
229    }
230
231    pub fn from_private_key_pem(
232        private_key_pem: &str,
233        certificate_der: impl Into<Vec<u8>>,
234    ) -> XmlResult<Self> {
235        let private_key = RsaPrivateKey::from_pkcs8_pem(private_key_pem)
236            .or_else(|_| RsaPrivateKey::from_pkcs1_pem(private_key_pem))
237            .map_err(|error| credential_error("invalid RSA private key PEM", error))?;
238
239        Ok(Self::from_private_key(private_key, certificate_der))
240    }
241
242    pub fn from_pkcs12_der(pkcs12_der: &[u8], password: &str) -> XmlResult<Self> {
243        let credential = Pkcs12Credential::from_der(pkcs12_der, password)?;
244        let provider = Self::from_private_key_der(
245            credential.private_key_der(),
246            credential.certificate_der().to_vec(),
247        )?;
248        Ok(provider.with_certificate_chain_der(credential.certificate_chain_der().to_vec()))
249    }
250
251    pub fn from_pkcs12_file(path: impl AsRef<Path>, password: &str) -> XmlResult<Self> {
252        Ok(Pkcs12SigningCredentials::from_file(path, password)?.into_provider())
253    }
254
255    pub fn with_certificate_details(mut self, certificate_details: CertificateDetails) -> Self {
256        if self
257            .certificate_chain_details
258            .first()
259            .is_some_and(|first| first.der() == self.certificate_details.der())
260        {
261            self.certificate_chain_details[0] = certificate_details.clone();
262        }
263        self.certificate_details = certificate_details;
264        self
265    }
266
267    pub fn with_certificate_issuer_serial(
268        self,
269        issuer_name: impl Into<String>,
270        serial_number: impl Into<String>,
271    ) -> Self {
272        let certificate_details = self
273            .certificate_details
274            .clone()
275            .with_issuer_serial(issuer_name, serial_number);
276        self.with_certificate_details(certificate_details)
277    }
278
279    pub fn with_certificate_chain_der(mut self, certificate_chain_der: Vec<Vec<u8>>) -> Self {
280        self.certificate_chain_details = certificate_chain_der
281            .into_iter()
282            .map(CertificateDetails::new)
283            .collect();
284        self
285    }
286
287    pub fn with_certificate_chain_details(
288        mut self,
289        certificate_chain_details: Vec<CertificateDetails>,
290    ) -> Self {
291        self.certificate_chain_details = certificate_chain_details;
292        self
293    }
294
295    pub fn public_key(&self) -> &RsaPublicKey {
296        &self.public_key
297    }
298}
299
300impl SigningProvider for RsaSha256SigningProvider {
301    fn certificate_der(&self) -> XmlResult<Vec<u8>> {
302        Ok(self.certificate_details.der.clone())
303    }
304
305    fn certificate_details(&self) -> XmlResult<CertificateDetails> {
306        Ok(self.certificate_details.clone())
307    }
308
309    fn certificate_chain_der(&self) -> XmlResult<Vec<Vec<u8>>> {
310        if self.certificate_chain_details.is_empty() {
311            Ok(vec![self.certificate_details.der.clone()])
312        } else {
313            Ok(self
314                .certificate_chain_details
315                .iter()
316                .map(|certificate| certificate.der.clone())
317                .collect())
318        }
319    }
320
321    fn certificate_chain_details(&self) -> XmlResult<Vec<CertificateDetails>> {
322        if self.certificate_chain_details.is_empty() {
323            Ok(vec![self.certificate_details.clone()])
324        } else {
325            Ok(self.certificate_chain_details.clone())
326        }
327    }
328
329    fn ensure_certificate_valid_at(&self, unix_timestamp: i64) -> XmlResult<()> {
330        ensure_certificate_der_valid_at(self.certificate_details.der(), unix_timestamp)
331    }
332
333    fn sign(&self, algorithm: SignatureAlgorithm, data: &[u8]) -> XmlResult<Vec<u8>> {
334        algorithm.ensure_allowed_for_generation()?;
335
336        match algorithm {
337            SignatureAlgorithm::RsaSha256 => {
338                let signing_key = SigningKey::<Sha256>::new(self.private_key.clone());
339                Ok(signing_key.sign(data).to_vec())
340            }
341            SignatureAlgorithm::RsaSha1 => Err(XmlError::new(
342                ErrorKind::Signature,
343                "RSA-SHA1 signatures are not allowed for generation",
344            )),
345        }
346    }
347
348    fn verify(
349        &self,
350        algorithm: SignatureAlgorithm,
351        data: &[u8],
352        signature: &[u8],
353    ) -> XmlResult<bool> {
354        match algorithm {
355            SignatureAlgorithm::RsaSha256 => {
356                let signature = RsaPkcs1v15Signature::try_from(signature).map_err(|error| {
357                    credential_error("invalid RSA-SHA256 signature value", error)
358                })?;
359                let verifying_key = VerifyingKey::<Sha256>::new(self.public_key.clone());
360
361                Ok(verifying_key.verify(data, &signature).is_ok())
362            }
363            SignatureAlgorithm::RsaSha1 => Ok(false),
364        }
365    }
366}
367
368/// Ergonomic PKCS#12/PFX signing credentials backed by the RSA-SHA256 provider.
369///
370/// The API reads files only when callers provide an explicit path. It does not
371/// validate trust, revocation, certificate policies, or legal identity.
372#[derive(Debug, Clone)]
373pub struct Pkcs12SigningCredentials {
374    provider: RsaSha256SigningProvider,
375    certificate_details: CertificateDetails,
376    additional_certificate_der: Vec<Vec<u8>>,
377}
378
379impl Pkcs12SigningCredentials {
380    pub fn from_file(path: impl AsRef<Path>, password: &str) -> XmlResult<Self> {
381        Self::from_credential(Pkcs12Credential::from_file(path, password)?)
382    }
383
384    pub fn from_der(pkcs12_der: &[u8], password: &str) -> XmlResult<Self> {
385        Self::from_credential(Pkcs12Credential::from_der(pkcs12_der, password)?)
386    }
387
388    pub fn from_credential(credential: Pkcs12Credential) -> XmlResult<Self> {
389        let certificate_details = certificate_details_from_der(credential.certificate_der())?;
390        let provider = RsaSha256SigningProvider::from_private_key_der(
391            credential.private_key_der(),
392            credential.certificate_der().to_vec(),
393        )?
394        .with_certificate_details(certificate_details.clone());
395        let additional_certificate_der = credential
396            .certificate_chain_der()
397            .iter()
398            .skip(1)
399            .cloned()
400            .collect();
401
402        Ok(Self {
403            provider,
404            certificate_details,
405            additional_certificate_der,
406        }
407        .with_refreshed_chain())
408    }
409
410    pub fn with_issuer_serial(
411        mut self,
412        issuer_name: impl Into<String>,
413        serial_number: impl Into<String>,
414    ) -> Self {
415        self.certificate_details = self
416            .certificate_details
417            .clone()
418            .with_issuer_serial(issuer_name, serial_number);
419        self.provider = self
420            .provider
421            .clone()
422            .with_certificate_details(self.certificate_details.clone());
423        self.with_refreshed_chain()
424    }
425
426    pub fn with_certificate_details(mut self, certificate_details: CertificateDetails) -> Self {
427        self.certificate_details = certificate_details;
428        self.provider = self
429            .provider
430            .clone()
431            .with_certificate_details(self.certificate_details.clone());
432        self.with_refreshed_chain()
433    }
434
435    pub fn with_additional_certificate_der(mut self, certificate_der: impl Into<Vec<u8>>) -> Self {
436        self.additional_certificate_der.push(certificate_der.into());
437        self.with_refreshed_chain()
438    }
439
440    pub fn with_additional_certificate_file(self, path: impl AsRef<Path>) -> XmlResult<Self> {
441        Ok(self.with_additional_certificate_der(read_certificate_der_file(path)?))
442    }
443
444    pub fn with_additional_certificate_files<I, P>(mut self, paths: I) -> XmlResult<Self>
445    where
446        I: IntoIterator<Item = P>,
447        P: AsRef<Path>,
448    {
449        for path in paths {
450            self = self.with_additional_certificate_file(path)?;
451        }
452        Ok(self)
453    }
454
455    pub fn provider(&self) -> &RsaSha256SigningProvider {
456        &self.provider
457    }
458
459    pub fn certificate_details(&self) -> &CertificateDetails {
460        &self.certificate_details
461    }
462
463    pub fn certificate_der(&self) -> &[u8] {
464        self.certificate_details.der()
465    }
466
467    pub fn additional_certificate_der(&self) -> &[Vec<u8>] {
468        &self.additional_certificate_der
469    }
470
471    pub fn has_additional_certificate_chain(&self) -> bool {
472        !self.additional_certificate_der.is_empty()
473    }
474
475    pub fn validation_data_provider(&self) -> StaticValidationDataProvider {
476        let mut provider =
477            StaticValidationDataProvider::new().with_certificate(self.certificate_der().to_vec());
478        for certificate in &self.additional_certificate_der {
479            provider = provider.with_certificate(certificate.clone());
480        }
481        provider
482    }
483
484    pub fn into_provider(self) -> RsaSha256SigningProvider {
485        self.provider
486    }
487
488    fn with_refreshed_chain(mut self) -> Self {
489        let chain = if self.additional_certificate_der.is_empty() {
490            Vec::new()
491        } else {
492            let mut chain = Vec::with_capacity(self.additional_certificate_der.len() + 1);
493            chain.push(self.certificate_details.clone());
494            chain.extend(
495                self.additional_certificate_der
496                    .iter()
497                    .cloned()
498                    .map(CertificateDetails::new),
499            );
500            chain
501        };
502        self.provider = self.provider.clone().with_certificate_chain_details(chain);
503        self
504    }
505}
506
507impl SigningProvider for Pkcs12SigningCredentials {
508    fn certificate_der(&self) -> XmlResult<Vec<u8>> {
509        self.provider.certificate_der()
510    }
511
512    fn certificate_details(&self) -> XmlResult<CertificateDetails> {
513        self.provider.certificate_details()
514    }
515
516    fn certificate_chain_der(&self) -> XmlResult<Vec<Vec<u8>>> {
517        self.provider.certificate_chain_der()
518    }
519
520    fn certificate_chain_details(&self) -> XmlResult<Vec<CertificateDetails>> {
521        self.provider.certificate_chain_details()
522    }
523
524    fn ensure_certificate_valid_at(&self, unix_timestamp: i64) -> XmlResult<()> {
525        self.provider.ensure_certificate_valid_at(unix_timestamp)
526    }
527
528    fn sign(&self, algorithm: SignatureAlgorithm, data: &[u8]) -> XmlResult<Vec<u8>> {
529        self.provider.sign(algorithm, data)
530    }
531
532    fn verify(
533        &self,
534        algorithm: SignatureAlgorithm,
535        data: &[u8],
536        signature: &[u8],
537    ) -> XmlResult<bool> {
538        self.provider.verify(algorithm, data, signature)
539    }
540}
541
542/// Deterministic provider for tests and golden fixtures.
543///
544/// This provider is not cryptographic. It creates reproducible bytes so XMLDSig
545/// structure, canonicalization, reference digests, and verification flow can be
546/// tested before adding real key backends.
547#[derive(Debug, Clone, PartialEq, Eq)]
548pub struct DeterministicSigningProvider {
549    certificate_details: CertificateDetails,
550    certificate_chain_details: Vec<CertificateDetails>,
551    secret: Vec<u8>,
552}
553
554impl DeterministicSigningProvider {
555    pub fn new(certificate_der: impl Into<Vec<u8>>, secret: impl Into<Vec<u8>>) -> Self {
556        Self {
557            certificate_details: CertificateDetails::new(certificate_der),
558            certificate_chain_details: Vec::new(),
559            secret: secret.into(),
560        }
561    }
562
563    pub fn with_certificate_issuer_serial(
564        self,
565        issuer_name: impl Into<String>,
566        serial_number: impl Into<String>,
567    ) -> Self {
568        let certificate_details = self
569            .certificate_details
570            .clone()
571            .with_issuer_serial(issuer_name, serial_number);
572        Self {
573            certificate_details,
574            ..self
575        }
576    }
577
578    pub fn with_certificate_chain_details(
579        mut self,
580        certificate_chain_details: Vec<CertificateDetails>,
581    ) -> Self {
582        self.certificate_chain_details = certificate_chain_details;
583        self
584    }
585}
586
587impl SigningProvider for DeterministicSigningProvider {
588    fn certificate_der(&self) -> XmlResult<Vec<u8>> {
589        Ok(self.certificate_details.der.clone())
590    }
591
592    fn certificate_details(&self) -> XmlResult<CertificateDetails> {
593        Ok(self.certificate_details.clone())
594    }
595
596    fn certificate_chain_der(&self) -> XmlResult<Vec<Vec<u8>>> {
597        if self.certificate_chain_details.is_empty() {
598            Ok(vec![self.certificate_details.der.clone()])
599        } else {
600            Ok(self
601                .certificate_chain_details
602                .iter()
603                .map(|certificate| certificate.der.clone())
604                .collect())
605        }
606    }
607
608    fn certificate_chain_details(&self) -> XmlResult<Vec<CertificateDetails>> {
609        if self.certificate_chain_details.is_empty() {
610            Ok(vec![self.certificate_details.clone()])
611        } else {
612            Ok(self.certificate_chain_details.clone())
613        }
614    }
615
616    fn sign(&self, algorithm: SignatureAlgorithm, data: &[u8]) -> XmlResult<Vec<u8>> {
617        algorithm.ensure_allowed_for_generation()?;
618
619        let mut hasher = Sha256::new();
620        hasher.update(algorithm.uri().as_bytes());
621        hasher.update([0]);
622        hasher.update(&self.secret);
623        hasher.update([0]);
624        hasher.update(self.certificate_details.der());
625        hasher.update([0]);
626        hasher.update(data);
627        Ok(hasher.finalize().to_vec())
628    }
629}
630
631fn credential_error(context: &str, error: impl std::fmt::Display) -> XmlError {
632    XmlError::new(ErrorKind::Signature, format!("{context}: {error}"))
633}
634
635fn ensure_certificate_der_valid_at(certificate_der: &[u8], unix_timestamp: i64) -> XmlResult<()> {
636    let certificate = X509::from_der(certificate_der)
637        .map_err(|error| credential_error("invalid X.509 certificate DER", error))?;
638    let signing_time = Asn1Time::from_unix(unix_timestamp as _)
639        .map_err(|error| credential_error("invalid certificate validation time", error))?;
640    let not_before = certificate.not_before();
641    let not_after = certificate.not_after();
642
643    if not_before
644        .compare(&signing_time)
645        .map_err(|error| credential_error("cannot compare certificate NotBefore", error))?
646        == std::cmp::Ordering::Greater
647    {
648        return Err(XmlError::new(
649            ErrorKind::Signature,
650            format!(
651                "XAdES SigningTime is before certificate NotBefore: signing_time={}, not_before={}",
652                signing_time.as_ref(),
653                not_before
654            ),
655        ));
656    }
657
658    if not_after
659        .compare(&signing_time)
660        .map_err(|error| credential_error("cannot compare certificate NotAfter", error))?
661        == std::cmp::Ordering::Less
662    {
663        return Err(XmlError::new(
664            ErrorKind::Signature,
665            format!(
666                "XAdES SigningTime is after certificate NotAfter: signing_time={}, not_after={}",
667                signing_time.as_ref(),
668                not_after
669            ),
670        ));
671    }
672
673    Ok(())
674}
675
676fn certificate_details_from_der(certificate_der: &[u8]) -> XmlResult<CertificateDetails> {
677    let certificate = X509::from_der(certificate_der)
678        .map_err(|error| credential_error("invalid X.509 certificate DER", error))?;
679    let issuer_name = x509_name_to_rfc4514_like_string(certificate.issuer_name())?;
680    let serial_number = certificate
681        .serial_number()
682        .to_bn()
683        .map_err(|error| credential_error("cannot read X.509 certificate serial", error))?
684        .to_dec_str()
685        .map_err(|error| credential_error("cannot format X.509 certificate serial", error))?
686        .to_string();
687
688    Ok(CertificateDetails::new(certificate_der.to_vec())
689        .with_issuer_serial(issuer_name, serial_number))
690}
691
692fn x509_name_to_rfc4514_like_string(name: &X509NameRef) -> XmlResult<String> {
693    let mut parts = Vec::new();
694
695    for entry in name.entries() {
696        let key = entry
697            .object()
698            .nid()
699            .short_name()
700            .map_err(|error| credential_error("cannot read X.509 name attribute", error))?;
701        let value = entry
702            .data()
703            .as_utf8()
704            .map_err(|error| credential_error("cannot read X.509 name value", error))?
705            .to_string();
706        parts.push(format!(
707            "{key}={}",
708            escape_x509_name_value_for_rfc4514(&value)
709        ));
710    }
711
712    Ok(parts.join(","))
713}
714
715fn escape_x509_name_value_for_rfc4514(value: &str) -> String {
716    let chars: Vec<char> = value.chars().collect();
717    let mut escaped = String::new();
718
719    for (index, ch) in chars.iter().copied().enumerate() {
720        let is_first = index == 0;
721        let is_last = index + 1 == chars.len();
722        if matches!(ch, ',' | '+' | '"' | '\\' | '<' | '>' | ';')
723            || (is_first && (ch == ' ' || ch == '#'))
724            || (is_last && ch == ' ')
725        {
726            escaped.push('\\');
727        }
728        escaped.push(ch);
729    }
730
731    escaped
732}
733
734fn read_file_bytes(path: &Path, context: &str) -> XmlResult<Vec<u8>> {
735    fs::read(path).map_err(|error| {
736        XmlError::new(
737            ErrorKind::Signature,
738            format!("{context} `{}`: {error}", path.display()),
739        )
740    })
741}
742
743fn read_certificate_der_file(path: impl AsRef<Path>) -> XmlResult<Vec<u8>> {
744    let path = path.as_ref();
745    certificate_der_from_pem_or_der(read_file_bytes(path, "cannot read certificate file")?)
746}
747
748fn certificate_der_from_pem_or_der(bytes: Vec<u8>) -> XmlResult<Vec<u8>> {
749    if let Ok(text) = std::str::from_utf8(&bytes) {
750        if text.contains("-----BEGIN CERTIFICATE-----") {
751            return decode_pem_block(text, "CERTIFICATE");
752        }
753    }
754    Ok(bytes)
755}
756
757fn decode_pem_block(text: &str, label: &str) -> XmlResult<Vec<u8>> {
758    let begin = format!("-----BEGIN {label}-----");
759    let end = format!("-----END {label}-----");
760    let start = text
761        .find(&begin)
762        .ok_or_else(|| XmlError::new(ErrorKind::Signature, "missing PEM begin marker"))?
763        + begin.len();
764    let finish = text[start..]
765        .find(&end)
766        .ok_or_else(|| XmlError::new(ErrorKind::Signature, "missing PEM end marker"))?
767        + start;
768    let encoded: String = text[start..finish]
769        .chars()
770        .filter(|ch| !ch.is_whitespace())
771        .collect();
772    decode_standard_base64(&encoded)
773}
774
775#[cfg(test)]
776mod tests {
777    use std::fs;
778
779    use crate::parser::parse_str;
780    use crate::signature::{
781        sign_enveloped, sign_xades_bes_enveloped, verify_enveloped, verify_xades_bes_enveloped,
782        XadesConfig, XadesValidationDataProvider, XmlDsigConfig,
783    };
784    use crate::writer::to_string_compact;
785    use openssl::asn1::Asn1Time;
786    use openssl::bn::BigNum;
787    use openssl::hash::MessageDigest;
788    use openssl::pkcs12::Pkcs12;
789    use openssl::pkey::PKey;
790    use openssl::x509::{X509NameBuilder, X509};
791
792    use super::*;
793
794    const RSA_PRIVATE_KEY_PEM: &str = r#"-----BEGIN PRIVATE KEY-----
795MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDhxyLXp0r/ySjk
796ZRA/uP+qvy3PtpmHBawMcqhqkcTULTA0VN09cISj/9NUQeDooamqIDQQwGuK30R/
797MW/23gkDuyrtzeouICJafWNIj1g0QBuq351pais4DKl8sRj9lSp1Vl5EVDAlfw6D
798k/I/i5XGOauH61CST7IB2WqVppMbNq7j6nEYor21E3/rRE0pqrhPAPfeGskqOJQ9
799NhaaeQt0EPqc9tnlws6ZHo44nTlegYv83/kWV9HOX1l6GmF7QMTzmi6wnkmojKKh
800wM5tGEv1VG/5owv7IDneeouK+oosg3aX2B6aMbxnLoQpgjUPjXVP6O1Oo/H4ucua
8014nQ+vaJXAgMBAAECggEAToM3RJLya7yCMdpLKj2k+rV8ssym2uNIDxQYKOcD8Vy9
802dJVGUkU8euNNk/FMytjc7UJBmMmxHBvD8A87Bjf0Ho4JwaRnmR6nk5wi2Gqwm4rn
803lCYq0SuQV+9fSPM044npt+AO6+fyzjc+zta12Q8rSEFputxDqn14Q1hdziic40sb
804Zk2+t8tYe1ntFVAW+rp5RktW1o/9B/+pi/PrOb8lP9c5eoK5HIfDxm6aC1b6uEvk
805FhBEGDC04TENR7qWpmXDgGUANQC9WvvnhYOO/7S+J1NyCT2WPcTQtKudJ4PiBfuS
806mr2OklkmXLQi6Kydol+Ri1pfX1UZZ/l9eGKmhV2t0QKBgQD77ofCY0yfs1WVIfsy
807OjE9p70mt3b52AwCPVLap5AO9W6lPVJkUmHMaeixHu9MnW3u59bdbpc3JUNct29r
8083aWziZ9ezyMgtrdDnOJn9d/mI+tlXcVTkRbnKtyx/JbQab3GyCN8brhYcZtaLIoc
809JM5tBWCBEKQZEfzQ0YE40lEFowKBgQDlbHzBBfdUgGDKa9vZjNregY9hP7mg/wrJ
810XC8Wrnl59McHMeY+c9lsPoFpVW1sncaSyxLVguYtf9zOw4Xi4/EuanIlBuBXvpML
811vn49c3isrMuVnuc6s6VG0qk4Nxj2x6yhbfoJ5XkoZ1CIw1lk4sedhbtSlt6cnO1m
812bDCEAs8zvQKBgEK0+Rt+gYZzzMBtO/8jq3Ag3xPGVml3TE63gB3Hanybfg2gvBU2
813PxEKJgPoJgLKWJZ+qsT9CGsgocKGC6mrCboNQbav8CQ0XTg47TYLw5pDfovblWk4
814LLFPBxrVv/U1Wnus+MB07Lj2c+Ufj/49vK7fUps6FclRmviL0MSD49IzAoGBALTK
8152kDN78sCY8QAXT7B9hRT6uZK7oCFmz10zJLGKWpGz7TGyNc8OgFH/HlCXbmzV7GE
816IDJrNfJzCVFS2SYkVIIsVgkBszbSMlSV6CuK3HTOspwUnckvmjYGel2XZa/LSCnq
817XZkA4YpKaDduIfsTjxR+N1DtHT4zmA0Xgt/0ys3NAoGBAIyaAw4uHkcv/ukB7C61
818vNxNKlzQGmIFENaFZnV1xIybIEXRYpU/W5FJPn0mexSE7rcW21s3XhPSirez4ctV
819Rqp4dm3rUuCm0un6gc3ARoj7VKIE8EIBI6f6l12CdNd7NMDaP7uhO8RHCuC5NW4A
82075CcGa3kvBm08ghSr6XTg21R
821-----END PRIVATE KEY-----"#;
822
823    fn real_crypto_provider() -> XmlResult<RsaSha256SigningProvider> {
824        RsaSha256SigningProvider::from_private_key_pem(
825            RSA_PRIVATE_KEY_PEM,
826            b"fixture-cert-der".to_vec(),
827        )
828    }
829
830    fn pkcs12_fixture(password: &str) -> Vec<u8> {
831        let now = std::time::SystemTime::now()
832            .duration_since(std::time::UNIX_EPOCH)
833            .expect("system clock after UNIX epoch")
834            .as_secs() as i64;
835        pkcs12_fixture_with_validity(password, now, now + 30 * 86_400)
836    }
837
838    fn pkcs12_fixture_with_validity(
839        password: &str,
840        not_before_unix: i64,
841        not_after_unix: i64,
842    ) -> Vec<u8> {
843        let pkey = PKey::private_key_from_pem(RSA_PRIVATE_KEY_PEM.as_bytes()).expect("fixture key");
844        let mut name = X509NameBuilder::new().expect("x509 name builder");
845        name.append_entry_by_text("CN", "xdoc test")
846            .expect("x509 cn");
847        let name = name.build();
848
849        let mut certificate = X509::builder().expect("x509 builder");
850        let serial = BigNum::from_dec_str("42")
851            .expect("serial")
852            .to_asn1_integer()
853            .expect("asn1 serial");
854        certificate.set_version(2).expect("x509 version");
855        certificate.set_serial_number(&serial).expect("x509 serial");
856        certificate.set_subject_name(&name).expect("x509 subject");
857        certificate.set_issuer_name(&name).expect("x509 issuer");
858        certificate.set_pubkey(&pkey).expect("x509 public key");
859        certificate
860            .set_not_before(
861                Asn1Time::from_unix(not_before_unix as _)
862                    .expect("not before")
863                    .as_ref(),
864            )
865            .expect("x509 not before");
866        certificate
867            .set_not_after(
868                Asn1Time::from_unix(not_after_unix as _)
869                    .expect("not after")
870                    .as_ref(),
871            )
872            .expect("x509 not after");
873        certificate
874            .sign(&pkey, MessageDigest::sha256())
875            .expect("x509 sign");
876        let certificate = certificate.build();
877
878        Pkcs12::builder()
879            .name("xdoc-test")
880            .pkey(&pkey)
881            .cert(&certificate)
882            .build2(password)
883            .expect("PFX fixture")
884            .to_der()
885            .expect("PFX DER")
886    }
887
888    fn temp_path(name: &str) -> std::path::PathBuf {
889        std::env::temp_dir().join(format!("xdoc-{name}-{}", std::process::id()))
890    }
891
892    #[test]
893    fn signature_provider_signs_deterministically() -> XmlResult<()> {
894        let provider = DeterministicSigningProvider::new(b"cert".to_vec(), b"secret".to_vec());
895
896        let first = provider.sign(SignatureAlgorithm::RsaSha256, b"payload")?;
897        let second = provider.sign(SignatureAlgorithm::RsaSha256, b"payload")?;
898
899        assert_eq!(first, second);
900        assert!(provider.verify(SignatureAlgorithm::RsaSha256, b"payload", &first)?);
901        assert!(!provider.verify(SignatureAlgorithm::RsaSha256, b"tampered", &first)?);
902
903        Ok(())
904    }
905
906    #[test]
907    fn signature_provider_rejects_legacy_generation_algorithm() {
908        let provider = DeterministicSigningProvider::new(b"cert".to_vec(), b"secret".to_vec());
909
910        assert!(provider
911            .sign(SignatureAlgorithm::RsaSha1, b"payload")
912            .is_err());
913    }
914
915    #[test]
916    fn real_crypto_provider_signs_and_verifies_rsa_sha256() -> XmlResult<()> {
917        let provider = real_crypto_provider()?;
918        let signature = provider.sign(SignatureAlgorithm::RsaSha256, b"payload")?;
919
920        assert!(provider.verify(SignatureAlgorithm::RsaSha256, b"payload", &signature)?);
921        assert!(!provider.verify(SignatureAlgorithm::RsaSha256, b"tampered", &signature)?);
922
923        Ok(())
924    }
925
926    #[test]
927    fn real_crypto_provider_rejects_invalid_pem() {
928        let error = RsaSha256SigningProvider::from_private_key_pem("not a key", b"cert".to_vec())
929            .expect_err("invalid PEM should fail");
930
931        assert_eq!(error.kind(), &ErrorKind::Signature);
932        assert!(error.message().contains("invalid RSA private key PEM"));
933    }
934
935    #[test]
936    fn real_crypto_provider_rejects_legacy_generation_algorithm() -> XmlResult<()> {
937        let provider = real_crypto_provider()?;
938
939        let error = provider
940            .sign(SignatureAlgorithm::RsaSha1, b"payload")
941            .expect_err("RSA-SHA1 should not be generated");
942
943        assert_eq!(error.kind(), &ErrorKind::Signature);
944        Ok(())
945    }
946
947    #[test]
948    fn real_crypto_provider_exposes_certificate_chain() -> XmlResult<()> {
949        let provider = real_crypto_provider()?
950            .with_certificate_chain_der(vec![b"leaf".to_vec(), b"issuer".to_vec()]);
951
952        assert_eq!(
953            provider.certificate_chain_der()?,
954            vec![b"leaf".to_vec(), b"issuer".to_vec()]
955        );
956
957        Ok(())
958    }
959
960    #[test]
961    fn pkcs12_credential_extracts_private_key_certificate_and_chain() -> XmlResult<()> {
962        let pfx = pkcs12_fixture("secret");
963        let credential = Pkcs12Credential::from_der(&pfx, "secret")?;
964
965        assert!(!credential.private_key_der().is_empty());
966        assert!(!credential.certificate_der().is_empty());
967        assert_eq!(credential.certificate_chain_der().len(), 1);
968
969        Ok(())
970    }
971
972    #[test]
973    fn pkcs12_signing_credentials_extract_x509_issuer_serial() -> XmlResult<()> {
974        let pfx = pkcs12_fixture("secret");
975        let credentials = Pkcs12SigningCredentials::from_der(&pfx, "secret")?;
976
977        assert_eq!(
978            credentials.certificate_details().issuer_serial(),
979            Some(("CN=xdoc test", "42"))
980        );
981
982        Ok(())
983    }
984
985    #[test]
986    fn pkcs12_credential_rejects_wrong_password() {
987        let pfx = pkcs12_fixture("secret");
988        let error =
989            Pkcs12Credential::from_der(&pfx, "wrong").expect_err("wrong password must fail");
990
991        assert_eq!(error.kind(), &ErrorKind::Signature);
992        assert!(error.message().contains("PKCS#12/PFX"));
993    }
994
995    #[test]
996    fn real_crypto_provider_loads_pkcs12_and_signs() -> XmlResult<()> {
997        let pfx = pkcs12_fixture("secret");
998        let provider = RsaSha256SigningProvider::from_pkcs12_der(&pfx, "secret")?;
999        let signature = provider.sign(SignatureAlgorithm::RsaSha256, b"payload")?;
1000
1001        assert!(provider.verify(SignatureAlgorithm::RsaSha256, b"payload", &signature)?);
1002        assert_eq!(provider.certificate_chain_der()?.len(), 1);
1003
1004        Ok(())
1005    }
1006
1007    #[test]
1008    fn pkcs12_signing_credentials_load_file_and_delegate_provider() -> XmlResult<()> {
1009        let path = temp_path("credential.pfx");
1010        fs::write(&path, pkcs12_fixture("secret")).expect("write PFX fixture");
1011
1012        let credentials = Pkcs12SigningCredentials::from_file(&path, "secret")?
1013            .with_additional_certificate_der(b"issuer-cert".to_vec());
1014        let signature = credentials.sign(SignatureAlgorithm::RsaSha256, b"payload")?;
1015        let validation_data = credentials.validation_data_provider();
1016
1017        assert!(credentials.verify(SignatureAlgorithm::RsaSha256, b"payload", &signature)?);
1018        assert_eq!(
1019            credentials.certificate_details().issuer_serial(),
1020            Some(("CN=xdoc test", "42"))
1021        );
1022        assert!(credentials.has_additional_certificate_chain());
1023        assert_eq!(credentials.additional_certificate_der().len(), 1);
1024        assert_eq!(credentials.certificate_chain_der()?.len(), 2);
1025        assert_eq!(validation_data.certificate_values()?.len(), 2);
1026
1027        let _ = fs::remove_file(path);
1028        Ok(())
1029    }
1030
1031    #[test]
1032    fn xades_signing_time_is_validated_against_pkcs12_certificate() -> XmlResult<()> {
1033        let signing_time = 1_800_000_000;
1034        let pfx = pkcs12_fixture_with_validity("secret", signing_time - 60, signing_time + 60);
1035        let credentials = Pkcs12SigningCredentials::from_der(&pfx, "secret")?;
1036        let document = parse_str(r#"<Root Id="doc-1"><Item>value</Item></Root>"#)?;
1037        let config = XadesConfig::new().with_signing_time_unix_timestamp(signing_time);
1038
1039        let signed = sign_xades_bes_enveloped(&document, &credentials, &config)?;
1040        let report = verify_xades_bes_enveloped(&signed, &credentials, &config)?;
1041        let xml = to_string_compact(&signed)?;
1042
1043        assert!(report.valid);
1044        assert!(xml.contains("<xades:SigningTime>2027-01-15T08:00:00Z</xades:SigningTime>"));
1045        Ok(())
1046    }
1047
1048    #[test]
1049    fn xades_signing_time_before_certificate_validity_is_rejected() -> XmlResult<()> {
1050        let signing_time = 1_800_000_000;
1051        let pfx = pkcs12_fixture_with_validity("secret", signing_time + 60, signing_time + 120);
1052        let credentials = Pkcs12SigningCredentials::from_der(&pfx, "secret")?;
1053        let document = parse_str(r#"<Root Id="doc-1"><Item>value</Item></Root>"#)?;
1054        let config = XadesConfig::new().with_signing_time_unix_timestamp(signing_time);
1055
1056        let error = sign_xades_bes_enveloped(&document, &credentials, &config)
1057            .expect_err("signing before NotBefore must fail");
1058
1059        assert_eq!(error.kind(), &ErrorKind::Signature);
1060        assert!(error.message().contains("before certificate NotBefore"));
1061        Ok(())
1062    }
1063
1064    #[test]
1065    fn xades_signing_time_after_certificate_validity_is_rejected() -> XmlResult<()> {
1066        let signing_time = 1_800_000_000;
1067        let pfx = pkcs12_fixture_with_validity("secret", signing_time - 120, signing_time - 60);
1068        let credentials = Pkcs12SigningCredentials::from_der(&pfx, "secret")?;
1069        let document = parse_str(r#"<Root Id="doc-1"><Item>value</Item></Root>"#)?;
1070        let config = XadesConfig::new().with_signing_time_unix_timestamp(signing_time);
1071
1072        let error = sign_xades_bes_enveloped(&document, &credentials, &config)
1073            .expect_err("signing after NotAfter must fail");
1074
1075        assert_eq!(error.kind(), &ErrorKind::Signature);
1076        assert!(error.message().contains("after certificate NotAfter"));
1077        Ok(())
1078    }
1079
1080    #[test]
1081    fn real_crypto_provider_signs_and_verifies_enveloped_document() -> XmlResult<()> {
1082        let document = parse_str(r#"<Root Id="doc-1"><Item>value</Item></Root>"#)?;
1083        let provider = real_crypto_provider()?;
1084
1085        let signed = sign_enveloped(&document, &provider, &XmlDsigConfig::new())?;
1086        let report = verify_enveloped(&signed, &provider, &XmlDsigConfig::new())?;
1087
1088        assert!(report.valid);
1089        assert!(report.signature_value_valid);
1090
1091        Ok(())
1092    }
1093}