Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
custom:
- "https://github.com/structured-world/xml-sec/blob/main/README.md#support-the-project"
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Pure Rust XML Security library. Drop-in replacement for libxmlsec1.

**No C dependencies. No cmake. No system libraries. Just `cargo add xml-sec`.**

> [!WARNING]
> Early-stage pre-release. The API is unstable, XMLDSig/XMLEnc coverage is still incomplete,
> and this crate should not yet be used in production.

## Features

- **C14N** — XML Canonicalization (inclusive + exclusive, W3C compliant)
Expand All @@ -25,18 +29,35 @@ Every SAML, SOAP, and WS-Security implementation depends on libxmlsec1 — a C l

**Pre-release.** API is unstable. Not ready for production use.

Currently implemented (core paths):
- C14N 1.0, C14N 1.1, and Exclusive C14N
- XMLDSig parsing, same-document URI dereference, transform chains, and digest verification
- RSA PKCS#1 v1.5 verification helpers for SHA-1 / SHA-256 / SHA-384 / SHA-512
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Still in progress:
- End-to-end XMLDSig `VerifyContext`
- XMLDSig signing pipeline
- XMLEnc encryption/decryption pipeline

Current toolchain target: latest stable Rust.
Current MSRV: Rust 1.92.

## Specifications

| Spec | Status |
|------|--------|
| [Canonical XML 1.0](https://www.w3.org/TR/xml-c14n/) | Planned |
| [Exclusive C14N](https://www.w3.org/TR/xml-exc-c14n/) | Planned |
| [XMLDSig](https://www.w3.org/TR/xmldsig-core1/) | Planned |
| [Canonical XML 1.0](https://www.w3.org/TR/xml-c14n/) | Partially implemented |
| [Canonical XML 1.1](https://www.w3.org/TR/xml-c14n11/) | Partially implemented |
| [Exclusive C14N](https://www.w3.org/TR/xml-exc-c14n/) | Partially implemented |
| [XMLDSig](https://www.w3.org/TR/xmldsig-core1/) | Partially implemented |
| [XMLEnc](https://www.w3.org/TR/xmlenc-core1/) | Planned |

## License

Apache-2.0

## Support the Project

If `xml-sec` is useful in your stack, you can help fund continued implementation and maintenance.

**TRX / TRC-20:** `TFDsezHa1cBkoeZT5q2T49Wp66K8t2DmdA`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
4 changes: 4 additions & 0 deletions src/xmldsig/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

pub mod digest;
pub mod parse;
pub mod signature;
pub mod transforms;
pub mod types;
pub mod uri;
Expand All @@ -19,6 +20,9 @@ pub use digest::{DigestAlgorithm, compute_digest, constant_time_eq};
pub use parse::{
ParseError, Reference, SignatureAlgorithm, SignedInfo, find_signature_node, parse_signed_info,
};
pub use signature::{
SignatureVerificationError, verify_rsa_signature_pem, verify_rsa_signature_spki,
};
pub use transforms::{Transform, execute_transforms, parse_transforms};
pub use types::{NodeSet, TransformData, TransformError};
pub use verify::{
Expand Down
183 changes: 183 additions & 0 deletions src/xmldsig/signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
//! RSA signature verification helpers for XMLDSig.
//!
//! This module covers roadmap task P1-019: RSA PKCS#1 v1.5 verification for
//! `rsa-sha1`, `rsa-sha256`, `rsa-sha384`, and `rsa-sha512`.
//!
//! Input public keys are accepted in SubjectPublicKeyInfo (SPKI) form because
//! that is how the vendored PEM fixtures are stored. `ring` expects the inner
//! ASN.1 `RSAPublicKey` bytes, so we validate and unwrap SPKI first.

use ring::signature;
use x509_parser::prelude::FromDer;
use x509_parser::public_key::PublicKey;
use x509_parser::x509::SubjectPublicKeyInfo;

use super::parse::SignatureAlgorithm;

/// Errors while preparing or running RSA signature verification.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SignatureVerificationError {
/// The provided PEM block could not be parsed as PEM input.
#[error("invalid PEM public key")]
InvalidKeyPem,

/// The signature method is not an RSA PKCS#1 v1.5 algorithm.
#[error("unsupported signature algorithm: {uri}")]
UnsupportedAlgorithm {
/// XMLDSig algorithm URI used for diagnostics.
uri: String,
},

/// The provided PEM block was not a public key.
#[error("invalid key format: expected PUBLIC KEY PEM, got {label}")]
InvalidKeyFormat {
/// The PEM label that was actually supplied.
label: String,
},

/// The provided DER bytes were not a valid SPKI-encoded RSA public key.
#[error("invalid RSA SubjectPublicKeyInfo DER")]
InvalidKeyDer,
}

/// Verify an RSA XMLDSig signature using a PEM-encoded SPKI public key.
///
/// The PEM must contain a `PUBLIC KEY` block. Returns `Ok(false)` for signature
/// mismatch and `Err` for algorithm/key preparation errors.
#[must_use = "discarding the verification result skips signature validation"]
pub fn verify_rsa_signature_pem(
Comment thread
coderabbitai[bot] marked this conversation as resolved.
algorithm: SignatureAlgorithm,
public_key_pem: &str,
signed_data: &[u8],
Comment thread
polaz marked this conversation as resolved.
signature_value: &[u8],
) -> Result<bool, SignatureVerificationError> {
let (rest, pem) = x509_parser::pem::parse_x509_pem(public_key_pem.as_bytes())
.map_err(|_| SignatureVerificationError::InvalidKeyPem)?;
if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
return Err(SignatureVerificationError::InvalidKeyPem);
}
if pem.label != "PUBLIC KEY" {
return Err(SignatureVerificationError::InvalidKeyFormat { label: pem.label });
}

verify_rsa_signature_spki(algorithm, &pem.contents, signed_data, signature_value)
}

/// Verify an RSA XMLDSig signature using DER-encoded SPKI public key bytes.
///
/// The input must be an X.509 `SubjectPublicKeyInfo` wrapping an RSA key.
/// Returns `Ok(false)` for signature mismatch and `Err` for algorithm/key
/// preparation errors.
#[must_use = "discarding the verification result skips signature validation"]
pub fn verify_rsa_signature_spki(
algorithm: SignatureAlgorithm,
Comment thread
polaz marked this conversation as resolved.
public_key_spki_der: &[u8],
signed_data: &[u8],
signature_value: &[u8],
) -> Result<bool, SignatureVerificationError> {
let verification_algorithm = verification_algorithm(algorithm)?;
let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
.map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
if !rest.is_empty() {
return Err(SignatureVerificationError::InvalidKeyDer);
}
let public_key = spki
.parsed()
.map_err(|_| SignatureVerificationError::InvalidKeyDer)?;

match public_key {
PublicKey::RSA(rsa) => {
validate_rsa_public_key(&rsa, algorithm)?;
let key = signature::UnparsedPublicKey::new(
verification_algorithm,
spki.subject_public_key.data,
);
Ok(key.verify(signed_data, signature_value).is_ok())
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
_ => Err(SignatureVerificationError::InvalidKeyDer),
}
}

fn validate_rsa_public_key(
rsa: &x509_parser::public_key::RSAPublicKey<'_>,
algorithm: SignatureAlgorithm,
) -> Result<(), SignatureVerificationError> {
let min_modulus_bits = minimum_rsa_modulus_bits(algorithm)?;
let modulus_start = rsa
.modulus
.iter()
.position(|byte| *byte != 0)
.ok_or(SignatureVerificationError::InvalidKeyDer)?;
let modulus = &rsa.modulus[modulus_start..];
if modulus.is_empty() {
return Err(SignatureVerificationError::InvalidKeyDer);
}
// Match ring's RSA parameter checks: modulus length is evaluated after
// rounding up to the nearest whole byte, not by exact significant-bit
// length of the highest non-zero byte.
let modulus_bits = modulus
.len()
.checked_mul(8)
.ok_or(SignatureVerificationError::InvalidKeyDer)?;
if !(min_modulus_bits..=8192).contains(&modulus_bits) {
return Err(SignatureVerificationError::InvalidKeyDer);
}
Comment thread
polaz marked this conversation as resolved.

let exponent = rsa
.try_exponent()
.map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
if !(3..=((1_u64 << 33) - 1)).contains(&exponent) || exponent % 2 == 0 {
return Err(SignatureVerificationError::InvalidKeyDer);
}

Ok(())
}

fn minimum_rsa_modulus_bits(
algorithm: SignatureAlgorithm,
) -> Result<usize, SignatureVerificationError> {
match algorithm {
SignatureAlgorithm::RsaSha1
| SignatureAlgorithm::RsaSha256
| SignatureAlgorithm::RsaSha384
| SignatureAlgorithm::RsaSha512 => Ok(2048),
_ => Err(SignatureVerificationError::UnsupportedAlgorithm {
uri: algorithm.uri().to_string(),
}),
}
}

fn verification_algorithm(
algorithm: SignatureAlgorithm,
) -> Result<&'static dyn signature::VerificationAlgorithm, SignatureVerificationError> {
match algorithm {
SignatureAlgorithm::RsaSha1 => Ok(&signature::RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY),
SignatureAlgorithm::RsaSha256 => Ok(&signature::RSA_PKCS1_2048_8192_SHA256),
SignatureAlgorithm::RsaSha384 => Ok(&signature::RSA_PKCS1_2048_8192_SHA384),
SignatureAlgorithm::RsaSha512 => Ok(&signature::RSA_PKCS1_2048_8192_SHA512),
_ => Err(SignatureVerificationError::UnsupportedAlgorithm {
uri: algorithm.uri().to_string(),
}),
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

#[cfg(test)]
#[expect(clippy::unwrap_used, reason = "unit tests use fixed fixture data")]
mod tests {
use super::*;

#[test]
fn ecdsa_algorithms_are_rejected_for_rsa_verification() {
for algorithm in [
SignatureAlgorithm::EcdsaP256Sha256,
SignatureAlgorithm::EcdsaP384Sha384,
] {
let err = verification_algorithm(algorithm).unwrap_err();
assert!(matches!(
err,
SignatureVerificationError::UnsupportedAlgorithm { .. }
));
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<Reference URI="#object">
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<DigestValue>iDhYt78o294fA6pzQ7k44+eejrQMi+WX3l3UrUdtL1Q=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>1UQLUaUhBSZLindLf9zDhunnNA/0yTP0GkjSVqxWw1NuhgjvSo17VygyjatZa+fm
jU52ZSRGGjmpSygFwj5HwTFNAmzMLo2r8RdB+hSU9xLL+51pPCnK9oSJXs/dFmDh
b0bwPDvlLVvP5XA4fCKy9ZObljCAh6yRzTjavqOt5XFEf+bf8NzC2afilfwOFUVn
fKKIHmPyy3oYZGxbNP8RXRlqFz35779oftfAKGFP6jzks+gLQ3Lj9xsfZvqcCEMj
yWufppGXTOM2ttiEcFEaRN8hUjeNsKsExFeLfikAijKp3UYRtiqphBibojsmahU3
koRumVQ6L/tDhTJv9lf7dQ==</SignatureValue>
<KeyInfo>
<KeyName>TestKeyName-rsa-2048</KeyName>
<X509Data>
<X509Certificate>MIIFFjCCA/6gAwIBAgIUdzXuSH9oYtrxs5VtlhzLD6bzT1MwDQYJKoZIhvcNAQEL
BQAwgbYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQK
EzRYTUwgU2VjdXJpdHkgTGlicmFyeSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94
bWxzZWMpMRgwFgYDVQQLEw9TZWNvbmQgbGV2ZWwgQ0ExFjAUBgNVBAMTDUFsZWtz
ZXkgU2FuaW4xITAfBgkqhkiG9w0BCQEWEnhtbHNlY0BhbGVrc2V5LmNvbTAgFw0y
NjAzMDgyMjE0MTZaGA8yMTI2MDIxMjIyMTQxNlowfTELMAkGA1UEBhMCVVMxEzAR
BgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5
IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxGjAYBgNVBAMTEVRlc3Qg
S2V5IHJzYS0yMDQ4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3iVn
hDXlgGiWvV2f21bCP4NAeTkwouWvN9K94SNeV01xzuvPg2GRb+ozF0/YbQ8jj7UD
euIgcLoBrC/jSMtp7gJp6zj3oHhX97NZVv5SBUmBOJRbB8efy3apTvTvWlzJQhO4
WVXBRDqmA0dGHPRRuMB6l125wy+WBMWxO6BzUooe9m0OpQXjKokJKFcl9Zd4ht3E
qXW8cuHgiyrtYzXTcO63W9+J6dFOi1DTYhKMkK53jMsModlleEUdqUHgTbxK0TBS
YMa2sB1rGfcVq+QanOlsRHXAXiZM/BE6uPqlFq+g5osSZbHfH2qC/0G/wKKlWzIx
0YPjQSBq/MbroodKLQIDAQABo4IBUDCCAUwwDAYDVR0TBAUwAwEB/zAsBglghkgB
hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE
FG3Dlzf57FZfBmrUW3Cqz28yG8NGMIHuBgNVHSMEgeYwgeOAFNF9F6xFQoqO+bAX
JdU8cpidiDoloYG0pIGxMIGuMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
cm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cu
YWxla3NleS5jb20veG1sc2VjKTEQMA4GA1UECxMHUm9vdCBDQTEWMBQGA1UEAxMN
QWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29t
ghR3Ne5If2hi2vGzlW2WHMsPpvNPTzANBgkqhkiG9w0BAQsFAAOCAQEATAu+Gt18
Kg0CW8kT+l92sfsNysxS/eYJD3iNyku0oE72jmWVsOvS9phHDF0q01tv8SsIjio6
sUQXoQ+C+YDAI3g9M5imN5l41TGZF1yRS0i5VucZpnmMcWtNWEpkJd5mB6l4VRDK
IarRS3UuA2cmZdtfRNsXAnG7sCLiiQB5wWF0Gbe6oAb0Y+hURG7D2vnIAimi2lcH
LgCD9eXbGfMNhNYnN+hbTNZuwvbJIDWMLu5VWpOdeX+Axm5MXI7lNPMRda75uPRQ
O52QICz4Cz3lbq4SEhiF4CQ5h/FRj6Yc8m+ZY3/5khICap0dUjAmmezcVwMJlxXr
hwvZjgian+dyQw==
</X509Certificate>
<X509Certificate>MIIFEjCCA/qgAwIBAgIUdzXuSH9oYtrxs5VtlhzLD6bzT04wDQYJKoZIhvcNAQEL
BQAwga4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQK
EzRYTUwgU2VjdXJpdHkgTGlicmFyeSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94
bWxzZWMpMRAwDgYDVQQLEwdSb290IENBMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmlu
MSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb20wIBcNMjYwMzA4MjIw
NDQ3WhgPMjEyNjAyMTIyMjA0NDdaMIGuMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
Q2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6
Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEQMA4GA1UECxMHUm9vdCBDQTEWMBQG
A1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtz
ZXkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbKBr4EAoOm8
zAW/RL8Wrd1+24EPUbz1RYQuSlcuHcyBwt3uGXVvXNQ6fCGLq5ikIi3NkMymecIB
9u1Jc96T0FkpSxQrqIIvlCP6gpaLBa+lz8ix8Xeb0uJ4Dg8RDmwTBfQU2ENagYLc
v0mpW7myAmqzGq1+xdgd2Cbt1FRB5t4YKqNl6+pFbeXL9EGbRoNPyuu+CfWrWVEe
JfWD1YzM6fhB0c/zqCxC32Y1h/sAzNFyYRmYUULh2MwVBVyt839h0jAUBzzBh0/q
EoVL5a9daDx3m6+PS1ACb+nXUSYaOu8lVM2rPxRjVITHBo+NHn012T8JNrwcExMn
FgeaVXo+NwIDAQABo4IBIjCCAR4wHQYDVR0OBBYEFDN5WuQBQ05geQStksygwwDM
bhBEMIHuBgNVHSMEgeYwgeOAFDN5WuQBQ05geQStksygwwDMbhBEoYG0pIGxMIGu
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1M
IFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2Vj
KTEQMA4GA1UECxMHUm9vdCBDQTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8G
CSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tghR3Ne5If2hi2vGzlW2WHMsP
pvNPTjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBVUqxGkYxvFZ7s
/Zkmjj1u88PvOjdj36LnGQCyVDwJPXXAXoqW9I3W3BPra/Xy1vjFo5erkdjvNh0f
+iyZVS/9EVdPKssPdZd39p0YIiUyG1RUYmN/IBDzSX/LwBTiLGlMBHFTRj6Lfs+e
Nfu6PISqABh+It3/3jlB882eixesdsjvtZc0J6sDka1byMoqe40twfI0tTMMv4Jr
QwGn7YeiVuWZ/GKUQxYrA+FfIWR5maBWtnhdmjaOBgmUhNtJB8yBcH82cRrxRIMX
f2quQrB3P4/WQC2bw3YPGxSv5wQoZwcwCn6f7g7oQFy6cANONMBG7GJpbw5B/8uB
S1a6d6Rg
</X509Certificate>
<X509Certificate>MIIFSDCCBDCgAwIBAgIUdzXuSH9oYtrxs5VtlhzLD6bzT08wDQYJKoZIhvcNAQEL
BQAwga4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMT0wOwYDVQQK
EzRYTUwgU2VjdXJpdHkgTGlicmFyeSAoaHR0cDovL3d3dy5hbGVrc2V5LmNvbS94
bWxzZWMpMRAwDgYDVQQLEwdSb290IENBMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmlu
MSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb20wIBcNMjYwMzA4MjIw
NzQyWhgPMjEyNjAyMTIyMjA3NDJaMIG2MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
Q2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6
Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEYMBYGA1UECxMPU2Vjb25kIGxldmVs
IENBMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxz
ZWNAYWxla3NleS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDw
fEPVxQD3TLiNRpoh7g1KRYODmnJcdJzi7FMXfKuAgkhNmQaoAHQd7/pcwtg3oNUH
QukupST89AC7/qakF7ykdEQnVzxggYgdXbhfDZhLcaVUuMtFGgM6lHL0hnSZo8U9
LHKWOlPIhJemE/XziHqgAsQposis7IRhuUlSsDa2xFW7MfS2xF/+UhiclaHgyBZ/
RDzn2b5K14VAJdt1xRaoMC5zVIzu1uk33+j97L78+z65VRG7fxGTau2c94Mcl2V+
KjDulHAnxLVJkjczo0mVi+u0Vczq9VhUqbNlig9TERQAPBC3D4ZJHhJsBmJz+47i
7pNL/Pms8qr9U0PgbZaPAgMBAAGjggFQMIIBTDAMBgNVHRMEBTADAQH/MCwGCWCG
SAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4E
FgQU0X0XrEVCio75sBcl1TxymJ2IOiUwge4GA1UdIwSB5jCB44AUM3la5AFDTmB5
BK2SzKDDAMxuEEShgbSkgbEwga4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxp
Zm9ybmlhMT0wOwYDVQQKEzRYTUwgU2VjdXJpdHkgTGlicmFyeSAoaHR0cDovL3d3
dy5hbGVrc2V5LmNvbS94bWxzZWMpMRAwDgYDVQQLEwdSb290IENBMRYwFAYDVQQD
Ew1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5j
b22CFHc17kh/aGLa8bOVbZYcyw+m809OMA0GCSqGSIb3DQEBCwUAA4IBAQAhrm/J
FdnYclb8HwQJdgGSYtUw2Wdrl1950H/ZUGwSKs6lGX8YT5xnj55AELLhbetTo+Be
Wwmg9kZbqnRC9tt0vIhFMko/uQZkn7vzrFEIfXgnEm2UGkkULfXH9pgtO4A9EQ2s
bbR4Oyi3n9q1w39aBdkUZnw3uthWKVHjcMW+n4m0RZBh4/snhHHlnxaIJzm4lB/s
DKNcXJTJHUbd1Kch5aOuSXCCmltwpEdEM9yaY1mr+jH9aD7lfo3FEJQxpO6M+AH6
JDdmS2LzQUSXDO4fibegrI/IeTQeST92mZI4foLxqp6SG19WGs9sNFFDYCIl6lNq
LDAhZycGntLtGaZF
</X509Certificate>
</X509Data>
</KeyInfo>
<Object Id="object">some text</Object>
</Signature>
Loading
Loading