|
11 | 11 |
|
12 | 12 | use crate::c14n::c14n; |
13 | 13 | use crate::opc::{ |
14 | | - entry_to_uri, rels_path_for_part, serialize_rels, OpcPackage, OpcRelationship, |
15 | | - MIME_DS_CERTIFICATE, MIME_DS_ORIGIN, MIME_DS_SIGNATURE, MIME_RELS, REL_DS_CERTIFICATE, |
16 | | - REL_DS_ORIGIN, REL_DS_SIGNATURE, |
| 14 | + entry_to_uri, extension_for_opc_content_type, rels_path_for_part, serialize_rels, OpcPackage, |
| 15 | + OpcRelationship, MIME_DS_CERTIFICATE, MIME_DS_ORIGIN, MIME_DS_SIGNATURE, MIME_RELS, |
| 16 | + REL_DS_CERTIFICATE, REL_DS_ORIGIN, REL_DS_SIGNATURE, |
17 | 17 | }; |
18 | 18 | use crate::timestamp; |
19 | 19 | use crate::xml_sig::{build_signature_xml, DigestAlgorithm, PartDigest, TransformInfo}; |
@@ -152,12 +152,8 @@ pub fn sign( |
152 | 152 |
|
153 | 153 | for part_path in &parts_to_sign { |
154 | 154 | let data = pkg.entries.get(part_path.as_str()).cloned().unwrap_or_default(); |
155 | | - let mime = pkg.content_type_for_extension( |
156 | | - std::path::Path::new(part_path) |
157 | | - .extension() |
158 | | - .and_then(|e| e.to_str()) |
159 | | - .unwrap_or(""), |
160 | | - ).to_string(); |
| 155 | + let ext_token = extension_for_opc_content_type(part_path); |
| 156 | + let mime = pkg.content_type_for_extension(ext_token).to_string(); |
161 | 157 |
|
162 | 158 | if part_path == crate::opc::GLOBAL_RELS { |
163 | 159 | // Two digest entries for _rels/.rels (mirrors OpcSignatureManifest.Build). |
@@ -277,13 +273,9 @@ fn digest_rels_part( |
277 | 273 | let c14n_filtered = c14n(filtered_xml.as_bytes())?; |
278 | 274 | let hash2 = digest_alg.hash(&c14n_filtered); |
279 | 275 |
|
280 | | - // Collect the relationship types for the RelationshipsGroupReference elements. |
281 | | - let source_types: Vec<String> = { |
282 | | - let mut types: Vec<String> = |
283 | | - filtered.iter().map(|r| r.rel_type.clone()).collect(); |
284 | | - types.dedup(); |
285 | | - types |
286 | | - }; |
| 276 | + // One RelationshipsGroupReference per filtered relationship (same order as C# |
| 277 | + // `XmlSignatureBuilder` iterating `nodes` from the filtered relationships doc). |
| 278 | + let source_types: Vec<String> = filtered.iter().map(|r| r.rel_type.clone()).collect(); |
287 | 279 |
|
288 | 280 | Ok(vec![ |
289 | 281 | // Entry 1 – C14N transform only. |
@@ -324,15 +316,14 @@ fn build_filtered_rels_xml(rels: &[&OpcRelationship]) -> String { |
324 | 316 | s |
325 | 317 | } |
326 | 318 |
|
327 | | -/// Compute the certificate file name: serial number bytes reversed, hex-encoded. |
328 | | -/// Matches the C# `ByteArrayToReverseString(certificate.GetSerialNumber())`. |
| 319 | +/// Compute the certificate file name: serial number bytes, hex-encoded. |
329 | 320 | fn cert_der_filename(cert_der: &[u8]) -> Result<String> { |
330 | 321 | // Parse the DER certificate to extract the serial number. |
331 | 322 | let cert = Certificate::from_der(cert_der) |
332 | 323 | .context("Failed to parse certificate DER")?; |
333 | | - // serial_number().as_bytes() returns the big-endian integer content bytes. |
334 | 324 | let bytes = cert.tbs_certificate.serial_number.as_bytes(); |
335 | | - let reversed: Vec<u8> = bytes.iter().rev().copied().collect(); |
336 | | - let hex_str = hex::encode_upper(&reversed); |
| 325 | + // Don't reverse the hex. C# had some broken code that appeared to reverse |
| 326 | + // it, but actually doesn't. |
| 327 | + let hex_str = hex::encode_upper(bytes); |
337 | 328 | Ok(format!("{}.cer", hex_str)) |
338 | 329 | } |
0 commit comments