moxml 0.1.25 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +9 -0
- data/.github/workflows/windows.yml +62 -0
- data/Gemfile +3 -1
- data/README.adoc +133 -6
- data/docs/signature/algorithms.md +108 -0
- data/docs/signature/architecture.md +148 -0
- data/docs/signature/c14n.md +110 -0
- data/docs/signature/examples.md +109 -0
- data/docs/signature/flows.md +132 -0
- data/docs/signature/quick-reference.md +115 -0
- data/docs/signature/security.md +143 -0
- data/examples/signature/auto_key_extraction.rb +24 -0
- data/examples/signature/enveloped_rsa.rb +45 -0
- data/examples/signature/hmac.rb +33 -0
- data/lib/compat/opal/moxml_boot.rb +5 -0
- data/lib/moxml/adapter/base.rb +53 -49
- data/lib/moxml/adapter/customized_leptris/declaration.rb +32 -0
- data/lib/moxml/adapter/customized_leptris/doctype.rb +31 -0
- data/lib/moxml/adapter/customized_leptris/entity_reference.rb +12 -0
- data/lib/moxml/adapter/customized_leptris/text_segment.rb +26 -0
- data/lib/moxml/adapter/customized_leptris.rb +16 -0
- data/lib/moxml/adapter/customized_libxml/cdata.rb +3 -12
- data/lib/moxml/adapter/customized_libxml/comment.rb +2 -9
- data/lib/moxml/adapter/customized_libxml/declaration.rb +1 -10
- data/lib/moxml/adapter/customized_libxml/node.rb +8 -0
- data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +2 -10
- data/lib/moxml/adapter/customized_libxml/text.rb +2 -9
- data/lib/moxml/adapter/customized_oga/entity_decoder.rb +37 -0
- data/lib/moxml/adapter/customized_oga/raw_value_override.rb +52 -0
- data/lib/moxml/adapter/customized_oga/xml_generator.rb +4 -34
- data/lib/moxml/adapter/customized_oga.rb +2 -0
- data/lib/moxml/adapter/customized_ox/entity_reference.rb +1 -17
- data/lib/moxml/adapter/customized_rexml/entity_reference.rb +1 -11
- data/lib/moxml/adapter/headed_ox.rb +9 -128
- data/lib/moxml/adapter/leptris.rb +965 -0
- data/lib/moxml/adapter/libxml.rb +131 -99
- data/lib/moxml/adapter/nokogiri.rb +29 -8
- data/lib/moxml/adapter/oga.rb +91 -54
- data/lib/moxml/adapter/ox.rb +93 -137
- data/lib/moxml/adapter/rexml.rb +61 -50
- data/lib/moxml/adapter.rb +2 -1
- data/lib/moxml/attribute.rb +8 -4
- data/lib/moxml/attribute_resolver.rb +189 -0
- data/lib/moxml/c14n/attribute_handler.rb +71 -0
- data/lib/moxml/c14n/character_encoder.rb +29 -0
- data/lib/moxml/c14n/data_model.rb +145 -0
- data/lib/moxml/c14n/exclusive.rb +188 -0
- data/lib/moxml/c14n/inclusive_10.rb +42 -0
- data/lib/moxml/c14n/inclusive_11.rb +21 -0
- data/lib/moxml/c14n/namespace_context.rb +68 -0
- data/lib/moxml/c14n/namespace_handler.rb +71 -0
- data/lib/moxml/c14n/node.rb +42 -0
- data/lib/moxml/c14n/nodes/attribute_node.rb +50 -0
- data/lib/moxml/c14n/nodes/comment_node.rb +29 -0
- data/lib/moxml/c14n/nodes/element_node.rb +62 -0
- data/lib/moxml/c14n/nodes/namespace_node.rb +41 -0
- data/lib/moxml/c14n/nodes/processing_instruction_node.rb +30 -0
- data/lib/moxml/c14n/nodes/root_node.rb +22 -0
- data/lib/moxml/c14n/nodes/text_node.rb +29 -0
- data/lib/moxml/c14n/nodes.rb +17 -0
- data/lib/moxml/c14n/processor.rb +118 -0
- data/lib/moxml/c14n/writer.rb +121 -0
- data/lib/moxml/c14n/xml_base_handler.rb +140 -0
- data/lib/moxml/c14n.rb +96 -0
- data/lib/moxml/config.rb +29 -3
- data/lib/moxml/context.rb +12 -0
- data/lib/moxml/element.rb +29 -12
- data/lib/moxml/entity/reference.rb +28 -0
- data/lib/moxml/entity.rb +125 -0
- data/lib/moxml/node.rb +90 -1
- data/lib/moxml/sax/namespace_splitter.rb +5 -2
- data/lib/moxml/signature/algorithms/base64_transform.rb +40 -0
- data/lib/moxml/signature/algorithms/canonicalization_base.rb +87 -0
- data/lib/moxml/signature/algorithms/digest_base.rb +51 -0
- data/lib/moxml/signature/algorithms/dsa_sha.rb +107 -0
- data/lib/moxml/signature/algorithms/ecdsa_sha.rb +125 -0
- data/lib/moxml/signature/algorithms/enveloped_signature_transform.rb +79 -0
- data/lib/moxml/signature/algorithms/exc_c14n_10.rb +23 -0
- data/lib/moxml/signature/algorithms/hmac_sha.rb +117 -0
- data/lib/moxml/signature/algorithms/inclusive_c14n_10.rb +21 -0
- data/lib/moxml/signature/algorithms/inclusive_c14n_11.rb +23 -0
- data/lib/moxml/signature/algorithms/rsa_pkcs1_sha.rb +56 -0
- data/lib/moxml/signature/algorithms/sha1.rb +13 -0
- data/lib/moxml/signature/algorithms/sha224.rb +13 -0
- data/lib/moxml/signature/algorithms/sha256.rb +13 -0
- data/lib/moxml/signature/algorithms/sha384.rb +13 -0
- data/lib/moxml/signature/algorithms/sha512.rb +13 -0
- data/lib/moxml/signature/algorithms/signature_method_base.rb +58 -0
- data/lib/moxml/signature/algorithms/transform_base.rb +40 -0
- data/lib/moxml/signature/algorithms.rb +113 -0
- data/lib/moxml/signature/errors.rb +99 -0
- data/lib/moxml/signature/key_extractor.rb +172 -0
- data/lib/moxml/signature/model/algorithm_method.rb +18 -0
- data/lib/moxml/signature/model/digest_method.rb +15 -0
- data/lib/moxml/signature/model/key/dsa_key_value.rb +28 -0
- data/lib/moxml/signature/model/key/ec_key_value.rb +22 -0
- data/lib/moxml/signature/model/key/rsa_key_value.rb +19 -0
- data/lib/moxml/signature/model/key/x509_data.rb +32 -0
- data/lib/moxml/signature/model/key/x509_digest.rb +19 -0
- data/lib/moxml/signature/model/key/x509_issuer_serial.rb +19 -0
- data/lib/moxml/signature/model/key_info.rb +21 -0
- data/lib/moxml/signature/model/key_value.rb +20 -0
- data/lib/moxml/signature/model/object_element.rb +18 -0
- data/lib/moxml/signature/model/reference.rb +22 -0
- data/lib/moxml/signature/model/signature.rb +21 -0
- data/lib/moxml/signature/model/signature_value.rb +16 -0
- data/lib/moxml/signature/model/signed_info.rb +20 -0
- data/lib/moxml/signature/model/transform.rb +16 -0
- data/lib/moxml/signature/model/transforms.rb +36 -0
- data/lib/moxml/signature/model.rb +35 -0
- data/lib/moxml/signature/parser.rb +270 -0
- data/lib/moxml/signature/reference_resolver.rb +79 -0
- data/lib/moxml/signature/reference_result.rb +22 -0
- data/lib/moxml/signature/serializer.rb +163 -0
- data/lib/moxml/signature/signer.rb +73 -0
- data/lib/moxml/signature/single_verification_result.rb +31 -0
- data/lib/moxml/signature/transform_pipeline.rb +106 -0
- data/lib/moxml/signature/verification_result.rb +27 -0
- data/lib/moxml/signature/verifier.rb +129 -0
- data/lib/moxml/signature.rb +94 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xml_emitter.rb +71 -0
- data/lib/moxml/xpath/compiler.rb +60 -7
- data/lib/moxml/xpath/parser.rb +22 -15
- data/lib/moxml.rb +5 -0
- data/reference-docs/w3c-xmldsig-bestpractices.md +216 -0
- data/reference-docs/w3c-xmldsig-core.md +400 -0
- data/spec/examples/readme_examples_spec.rb +0 -4
- data/spec/examples/xpath_examples_spec.rb +0 -11
- data/spec/fixtures/xmldsig/keys/rsa_private.pem +28 -0
- data/spec/fixtures/xmldsig/keys/rsa_public.pem +9 -0
- data/spec/fixtures/xmldsig/keys/rsa_ref.pem +15 -0
- data/spec/fixtures/xmldsig/keys/rsa_ref.pub +6 -0
- data/spec/fixtures/xmldsig/sign2-doc.xml +6 -0
- data/spec/fixtures/xmldsig/sign2-result.xml +25 -0
- data/spec/fixtures/xmldsig/sign3-result.xml +39 -0
- data/spec/integration/all_adapters_spec.rb +17 -0
- data/spec/integration/sax_parity_spec.rb +58 -0
- data/spec/integration/shared_examples/edge_cases.rb +0 -10
- data/spec/integration/shared_examples/integration_workflows.rb +0 -10
- data/spec/integration/shared_examples/node_wrappers/attribute_behavior.rb +98 -0
- data/spec/integration/shared_examples/node_wrappers/namespace_behavior.rb +26 -0
- data/spec/integration/shared_examples/node_wrappers/node_behavior.rb +0 -8
- data/spec/moxml/adapter/leptris_spec.rb +75 -0
- data/spec/moxml/adapter/ox_spec.rb +20 -6
- data/spec/moxml/adapter/platform_spec.rb +15 -2
- data/spec/moxml/adapter/shared_examples/adapter_contract.rb +180 -0
- data/spec/moxml/attribute_resolver_spec.rb +108 -0
- data/spec/moxml/c14n/api_spec.rb +117 -0
- data/spec/moxml/c14n/c14n_spec.rb +88 -0
- data/spec/moxml/c14n/comments_pis_spec.rb +65 -0
- data/spec/moxml/c14n/inclusive10_spec.rb +60 -0
- data/spec/moxml/c14n/namespace_edge_cases_spec.rb +88 -0
- data/spec/moxml/c14n/xml_attributes_spec.rb +54 -0
- data/spec/moxml/doctype_spec.rb +1 -1
- data/spec/moxml/entity_spec.rb +76 -0
- data/spec/moxml/node_spec.rb +104 -0
- data/spec/moxml/sax_entity_parity_spec.rb +358 -0
- data/spec/moxml/signature/algorithms/base64_transform_spec.rb +26 -0
- data/spec/moxml/signature/algorithms/digest_base_spec.rb +72 -0
- data/spec/moxml/signature/algorithms/dsa_sha_spec.rb +46 -0
- data/spec/moxml/signature/algorithms/ecdsa_sha_spec.rb +72 -0
- data/spec/moxml/signature/algorithms/enveloped_signature_transform_spec.rb +40 -0
- data/spec/moxml/signature/algorithms/hmac_sha_spec.rb +71 -0
- data/spec/moxml/signature/algorithms/rsa_pkcs1_sha_spec.rb +55 -0
- data/spec/moxml/signature/algorithms_spec.rb +76 -0
- data/spec/moxml/signature/cross_verify_spec.rb +64 -0
- data/spec/moxml/signature/edge_cases_spec.rb +284 -0
- data/spec/moxml/signature/fixtures_spec.rb +80 -0
- data/spec/moxml/signature/key_extractor_spec.rb +96 -0
- data/spec/moxml/signature/model_spec.rb +54 -0
- data/spec/moxml/signature/round_trip_spec.rb +113 -0
- data/spec/moxml/xml_emitter_spec.rb +64 -0
- data/spec/moxml/xpath/axes_spec.rb +72 -0
- data/spec/moxml/xpath/parser_spec.rb +6 -0
- data/spec/moxml/xpath_capabilities_spec.rb +5 -3
- data/spec/performance/benchmark_spec.rb +13 -6
- data/spec/performance/thread_safety_spec.rb +0 -4
- data/spec/spec_helper.rb +5 -1
- metadata +130 -2
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Moxml::Signature — Architecture
|
|
2
|
+
|
|
3
|
+
## Where it lives
|
|
4
|
+
|
|
5
|
+
`Moxml::Signature` is a sub-module of moxml that implements W3C XML
|
|
6
|
+
Signature (xmldsig-core-1.1). It is **XML implementation agnostic** —
|
|
7
|
+
every XML operation flows through `Moxml::Document` / `Moxml::Element`,
|
|
8
|
+
so the same signature code works whether you parse with Nokogiri, Oga,
|
|
9
|
+
REXML, Ox, or LibXML.
|
|
10
|
+
|
|
11
|
+
C14N itself is a top-level `Moxml::C14n` feature, sibling to
|
|
12
|
+
`Moxml::XPath`, `Moxml::Builder`, and `Moxml::SAX`. Signature uses it;
|
|
13
|
+
so can any other consumer (e.g., the sibling `canon` gem).
|
|
14
|
+
|
|
15
|
+
## Module layout
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
lib/moxml.rb # top-level, autoloads Signature and C14n
|
|
19
|
+
lib/moxml/signature.rb # Signature namespace + .sign/.verify entry points
|
|
20
|
+
|
|
21
|
+
lib/moxml/signature/errors.rb # error hierarchy
|
|
22
|
+
lib/moxml/signature/algorithms.rb # OCP registry hub
|
|
23
|
+
lib/moxml/signature/algorithms/ # concrete algorithms (digests, sig methods, transforms)
|
|
24
|
+
lib/moxml/signature/model/ # PORO models
|
|
25
|
+
lib/moxml/signature/serializer.rb # model → XML (uses moxml primitives)
|
|
26
|
+
lib/moxml/signature/parser.rb # XML → model
|
|
27
|
+
lib/moxml/signature/reference_resolver.rb # Reference URI → node-set / octets
|
|
28
|
+
lib/moxml/signature/transform_pipeline.rb # shared transform chain (DRY)
|
|
29
|
+
lib/moxml/signature/signer.rb # spec §3.1 signing flow
|
|
30
|
+
lib/moxml/signature/verifier.rb # spec §3.2 verification flow
|
|
31
|
+
lib/moxml/signature/key_extractor.rb # X509 / RSA / DSA / EC / KeyName → OpenSSL key
|
|
32
|
+
lib/moxml/signature/verification_result.rb
|
|
33
|
+
lib/moxml/signature/single_verification_result.rb
|
|
34
|
+
lib/moxml/signature/reference_result.rb
|
|
35
|
+
|
|
36
|
+
lib/moxml/c14n.rb # top-level C14n namespace
|
|
37
|
+
lib/moxml/c14n/ # canon-ported engine + moxml-native Exclusive
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Layering
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
44
|
+
│ Application code │
|
|
45
|
+
│ Moxml::Signature.sign / .verify │
|
|
46
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
47
|
+
↕
|
|
48
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
49
|
+
│ Orchestration: Signer, Verifier, TransformPipeline, KeyExtractor│
|
|
50
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
51
|
+
↕
|
|
52
|
+
┌──────────────────────┐ ┌──────────────────────────────────────┐
|
|
53
|
+
│ Algorithms (OCP hub) │ │ Models: Signature, SignedInfo, etc. │
|
|
54
|
+
└──────────────────────┘ └──────────────────────────────────────┘
|
|
55
|
+
↕
|
|
56
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
57
|
+
│ Moxml::C14n (canon-ported Inclusive + moxml-native Exclusive) │
|
|
58
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
59
|
+
↕
|
|
60
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
61
|
+
│ Moxml::Document / Element / Text / Namespace / Attribute │
|
|
62
|
+
│ (adapter-agnostic — Nokogiri, Oga, REXML, Ox, LibXML) │
|
|
63
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
64
|
+
↕
|
|
65
|
+
OpenSSL (crypto)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Core design decisions
|
|
69
|
+
|
|
70
|
+
### 1. Algorithm registry as the OCP hub
|
|
71
|
+
|
|
72
|
+
Every W3C algorithm (digest, signature method, canonicalization,
|
|
73
|
+
transform) is identified by URI. The `Moxml::Signature::Algorithms`
|
|
74
|
+
module is the registry; adding a new algorithm means:
|
|
75
|
+
|
|
76
|
+
1. Subclass the relevant base (`DigestBase`, `SignatureMethodBase`,
|
|
77
|
+
`CanonicalizationBase`, `TransformBase`).
|
|
78
|
+
2. Declare `identifier "http://..."` on the subclass.
|
|
79
|
+
3. Add an autoload entry in `algorithms.rb` and a reference in
|
|
80
|
+
`load_builtins!`.
|
|
81
|
+
|
|
82
|
+
No edits to existing code. The registry has four categories:
|
|
83
|
+
`:digest`, `:signature_method`, `:canonicalization`, `:transform`.
|
|
84
|
+
|
|
85
|
+
### 2. Models are POROs; serialization is a service
|
|
86
|
+
|
|
87
|
+
Models (`Model::Signature`, `Model::SignedInfo`, `Model::Reference`,
|
|
88
|
+
etc.) are plain Ruby objects with `attr_accessor`. They do **not** own
|
|
89
|
+
their wire shape. A dedicated `Serializer` translates model → XML using
|
|
90
|
+
moxml primitives; `Parser` translates XML → model. This keeps the data
|
|
91
|
+
shape and the wire shape independent, and matches the user's global
|
|
92
|
+
rule ("no hand-rolled serialization on model classes").
|
|
93
|
+
|
|
94
|
+
### 3. Signer / Verifier orchestrate, don't compute
|
|
95
|
+
|
|
96
|
+
`Signer` walks the references, delegates to the transform pipeline,
|
|
97
|
+
computes digests via `DigestMethod` instances, and signs the
|
|
98
|
+
canonicalized SignedInfo. It contains no algorithm-specific logic.
|
|
99
|
+
|
|
100
|
+
`Verifier` follows Best Practice 1: authenticate SignatureValue first,
|
|
101
|
+
then run reference transforms. Errors are captured into the result
|
|
102
|
+
object (`SingleVerificationResult#error`), not raised, so a malicious
|
|
103
|
+
signature cannot panic the application.
|
|
104
|
+
|
|
105
|
+
### 4. TransformPipeline is shared by Signer and Verifier
|
|
106
|
+
|
|
107
|
+
The transform-chain logic (lookup algorithm, coerce input type, apply,
|
|
108
|
+
repeat) is the same for signing and verifying. It lives in
|
|
109
|
+
`TransformPipeline` — DRY.
|
|
110
|
+
|
|
111
|
+
### 5. C14N is shared infrastructure
|
|
112
|
+
|
|
113
|
+
Inclusive C14N is ported from `~/src/lutaml/canon` (mature, ~1,200
|
|
114
|
+
lines, full node-set subset support, xml:base fixup, xml:* inheritable
|
|
115
|
+
attribute resolution). Exclusive C14N is moxml-native (canon doesn't
|
|
116
|
+
implement it). Both expose the same `#canonicalize(node, with_comments:,
|
|
117
|
+
inclusive_namespaces:)` interface.
|
|
118
|
+
|
|
119
|
+
## Adapter-agnostic invariant
|
|
120
|
+
|
|
121
|
+
Every XML operation — parse, walk, serialize, canonicalize — goes
|
|
122
|
+
through `Moxml::Node`. The signature module never imports Nokogiri,
|
|
123
|
+
Oga, REXML, Ox, or LibXML directly. Switching adapters does not change
|
|
124
|
+
signature behavior.
|
|
125
|
+
|
|
126
|
+
The one exception is the `context:` parameter threaded through every
|
|
127
|
+
constructor. When a transform receives octet-stream input, it parses
|
|
128
|
+
with the same adapter the caller used (`context.parse(...)`), preserving
|
|
129
|
+
byte-exact canonicalization across adapters.
|
|
130
|
+
|
|
131
|
+
## Cross-verification
|
|
132
|
+
|
|
133
|
+
`spec/fixtures/xmldsig/sign2-result.xml` and `sign3-result.xml` are
|
|
134
|
+
real libxmlsec1-produced signatures (from the Ruby
|
|
135
|
+
`nokogiri-xmlsec-instructure` reference). Both verify byte-exact against
|
|
136
|
+
`Moxml::Signature.verify`, proving the C14N and signing logic matches a
|
|
137
|
+
battle-tested C implementation.
|
|
138
|
+
|
|
139
|
+
## What this module deliberately doesn't do
|
|
140
|
+
|
|
141
|
+
- **XPath Filter transform** — Best Practice 5 says avoid. The Enveloped
|
|
142
|
+
Signature transform walks ancestors directly, no XPath needed.
|
|
143
|
+
- **XSLT transform** — Best Practice 3 says avoid. Disabled.
|
|
144
|
+
- **External URI dereferencing** — Best Practice 8 says constrain.
|
|
145
|
+
Applications must provide their own resolver.
|
|
146
|
+
- **X.509 chain validation** — application responsibility (trust policy).
|
|
147
|
+
- **XML Encryption** — separate spec (xmlenc-core-1.1).
|
|
148
|
+
- **XAdES** — separate spec (ETSI TS 101 903).
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Canonicalization (C14N)
|
|
2
|
+
|
|
3
|
+
Canonicalization is the load-bearing primitive for XML signature.
|
|
4
|
+
Two documents that differ only in surface representation (whitespace,
|
|
5
|
+
attribute order, namespace prefix choice) must produce identical
|
|
6
|
+
canonical bytes — otherwise signatures won't verify.
|
|
7
|
+
|
|
8
|
+
`Moxml::C14n` is a top-level moxml feature, sibling to `Moxml::XPath`,
|
|
9
|
+
`Moxml::Builder`, and `Moxml::SAX`.
|
|
10
|
+
|
|
11
|
+
## Algorithms
|
|
12
|
+
|
|
13
|
+
### Inclusive C14N 1.0 / 1.1
|
|
14
|
+
|
|
15
|
+
- W3C: <https://www.w3.org/TR/xml-c14n/>, <https://www.w3.org/TR/xml-c14n11/>
|
|
16
|
+
- Implementation: **ported from `~/src/lutaml/canon`** (~1,200 lines)
|
|
17
|
+
- Files: `lib/moxml/c14n/inclusive_10.rb`, `inclusive_11.rb`,
|
|
18
|
+
`data_model.rb`, `processor.rb`, `namespace_handler.rb`,
|
|
19
|
+
`attribute_handler.rb`, `xml_base_handler.rb`, `character_encoder.rb`,
|
|
20
|
+
`node.rb`, `nodes/*.rb`
|
|
21
|
+
- "Attracts" ancestor context: at the apex, every in-scope namespace is
|
|
22
|
+
rendered, including those inherited from outside the canonicalization
|
|
23
|
+
subset.
|
|
24
|
+
|
|
25
|
+
### Exclusive C14N 1.0
|
|
26
|
+
|
|
27
|
+
- W3C: <https://www.w3.org/TR/xml-exc-c14n/>
|
|
28
|
+
- Implementation: **moxml-native** (canon does not implement exclusive)
|
|
29
|
+
- Files: `lib/moxml/c14n/exclusive.rb`, `writer.rb`, `namespace_context.rb`
|
|
30
|
+
- "Repels" ancestor context: only namespaces visibly used by the apex
|
|
31
|
+
element's qualified name or attributes are rendered. Keeps signatures
|
|
32
|
+
valid when subdocuments are moved between XML contexts (e.g., into a
|
|
33
|
+
SOAP envelope).
|
|
34
|
+
|
|
35
|
+
## Public API
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
# Convenience: inclusive C14N 1.0
|
|
39
|
+
Moxml::C14n.canonicalize(node_or_xml, with_comments: false)
|
|
40
|
+
# → canonical UTF-8 octet String
|
|
41
|
+
|
|
42
|
+
# Convenience: exclusive C14N 1.0
|
|
43
|
+
Moxml::C14n.canonicalize_exclusive(
|
|
44
|
+
node_or_xml,
|
|
45
|
+
with_comments: false,
|
|
46
|
+
inclusive_namespaces: [], # InclusiveNamespacesPrefixList parameter
|
|
47
|
+
)
|
|
48
|
+
# → canonical UTF-8 octet String
|
|
49
|
+
|
|
50
|
+
# Direct engine access (used by signature algorithms)
|
|
51
|
+
Moxml::C14n::Inclusive10.new.canonicalize(node, with_comments:, inclusive_namespaces:)
|
|
52
|
+
Moxml::C14n::Inclusive11.new.canonicalize(node, with_comments:, inclusive_namespaces:)
|
|
53
|
+
Moxml::C14n::Exclusive.new.canonicalize(node, with_comments:, inclusive_namespaces:)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`node_or_xml` accepts a `Moxml::Node`, `Moxml::Document`, or XML `String`.
|
|
57
|
+
|
|
58
|
+
## Data model
|
|
59
|
+
|
|
60
|
+
The canon-derived inclusive C14N walks an intermediate data model
|
|
61
|
+
(`Moxml::C14n::Nodes::*`) rather than the live `Moxml::Node` tree. This
|
|
62
|
+
is because canonicalization needs:
|
|
63
|
+
|
|
64
|
+
- **Node-set membership flags** for subset canonicalization (spec §3).
|
|
65
|
+
Same-document references select a node-set; only selected nodes are
|
|
66
|
+
rendered.
|
|
67
|
+
- **Sorted namespace and attribute axes** per spec §2.3 / §2.4.
|
|
68
|
+
- **xml:base fixup** per RFC 3986 with C14N 1.1 modifications.
|
|
69
|
+
- **xml:* inheritable attribute resolution** (xml:lang, xml:space) from
|
|
70
|
+
omitted ancestors.
|
|
71
|
+
|
|
72
|
+
The data model is built from `Moxml::Node` via `Moxml::C14n::DataModel`.
|
|
73
|
+
|
|
74
|
+
## Output invariants
|
|
75
|
+
|
|
76
|
+
All algorithms produce canonical octets with these properties:
|
|
77
|
+
|
|
78
|
+
- UTF-8 encoded, no BOM
|
|
79
|
+
- NFC characters preserved
|
|
80
|
+
- Document-order traversal
|
|
81
|
+
- Entity references: `&` → `&`, `<` → `<`, `>` → `>`
|
|
82
|
+
- Attribute values: also escape `"`, tab, LF, CR
|
|
83
|
+
- Empty elements expanded: `<foo/>` → `<foo></foo>`
|
|
84
|
+
- Line endings normalized to LF (XML parser already does this)
|
|
85
|
+
|
|
86
|
+
## Why not delegate to Nokogiri's native C14N?
|
|
87
|
+
|
|
88
|
+
Nokogiri (via libxml2) has both inclusive and exclusive C14N built-in,
|
|
89
|
+
and it's much faster than pure Ruby. But:
|
|
90
|
+
|
|
91
|
+
1. **XML-agnosticity.** Moxml's whole point is that switching adapters
|
|
92
|
+
doesn't change behavior. If C14N were Nokogiri-only, Oga/REXML/Ox
|
|
93
|
+
users would get no canonicalization.
|
|
94
|
+
2. **Adapter-coupled canonicalization breaks cross-adapter signature
|
|
95
|
+
verification.** A signature produced with Nokogiri must verify with
|
|
96
|
+
Oga — same canonical bytes.
|
|
97
|
+
3. **Canon already had the mature pure-Ruby implementation.** Porting it
|
|
98
|
+
was less work than building a hybrid adapter-delegated system.
|
|
99
|
+
|
|
100
|
+
**Future work:** add an optional adapter-level C14N delegation. The
|
|
101
|
+
Nokogiri adapter could expose `canonicalize(node, ...)` and the
|
|
102
|
+
`Moxml::C14n` top-level method would delegate when available. This is
|
|
103
|
+
documented in TODO.complete/19.
|
|
104
|
+
|
|
105
|
+
## Cross-verification
|
|
106
|
+
|
|
107
|
+
The libxmlsec1-produced fixtures in `spec/fixtures/xmldsig/` verify
|
|
108
|
+
byte-exact against our C14N output. This proves the implementation
|
|
109
|
+
matches libxml2's C-based canonicalization for the cases the Ruby
|
|
110
|
+
reference exercises.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
Runnable examples live in `examples/signature/`. Run with:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bundle exec ruby examples/signature/enveloped_rsa.rb
|
|
7
|
+
bundle exec ruby examples/signature/hmac.rb
|
|
8
|
+
bundle exec ruby examples/signature/auto_key_extraction.rb
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Minimal enveloped RSA-SHA256 signature
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require "moxml"
|
|
15
|
+
require "moxml/signature"
|
|
16
|
+
require "openssl"
|
|
17
|
+
|
|
18
|
+
ctx = Moxml.new(:nokogiri)
|
|
19
|
+
key = OpenSSL::PKey::RSA.generate(2048)
|
|
20
|
+
|
|
21
|
+
doc = ctx.parse("<doc><greeting>Hello, World!</greeting></doc>")
|
|
22
|
+
|
|
23
|
+
# Sign — produces a Model::Signature
|
|
24
|
+
signature = Moxml::Signature.sign(
|
|
25
|
+
context: ctx,
|
|
26
|
+
document: doc,
|
|
27
|
+
key: key,
|
|
28
|
+
signature_method: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
|
29
|
+
canonicalization_method: "http://www.w3.org/2001/10/xml-exc-c14n#",
|
|
30
|
+
digest_method: "http://www.w3.org/2001/04/xmlenc#sha256",
|
|
31
|
+
reference_uri: "",
|
|
32
|
+
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Serialize the signature into XML and attach to the document
|
|
36
|
+
serialized = Moxml::Signature::Serializer.new(context: ctx).serialize(signature)
|
|
37
|
+
doc.root.add_child(serialized.root)
|
|
38
|
+
|
|
39
|
+
puts doc.to_xml
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Verify with the public key
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
result = Moxml::Signature.verify(
|
|
46
|
+
context: ctx,
|
|
47
|
+
document: doc,
|
|
48
|
+
key: key, # private key works; public key alone is enough
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
puts "Valid: #{result.valid?}"
|
|
52
|
+
puts "Signature count: #{result.signature_count}"
|
|
53
|
+
result.results.each do |r|
|
|
54
|
+
puts " signature_valid=#{r.signature_valid?}"
|
|
55
|
+
r.references.each { |ref| puts " ref #{ref.uri.inspect}: #{ref.valid?}" }
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Auto-extract key from X509Certificate
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
# sign3-result.xml embeds the signing certificate in KeyInfo.
|
|
63
|
+
# No explicit key is needed — the Verifier extracts it automatically.
|
|
64
|
+
doc = ctx.parse(File.read("spec/fixtures/xmldsig/sign3-result.xml"))
|
|
65
|
+
result = Moxml::Signature.verify(context: ctx, document: doc)
|
|
66
|
+
puts "Auto-extracted: #{result.valid?}"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## KeyName-based key resolution
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
# Signer used <KeyName>my-key</KeyName>; verifier resolves via key_map:
|
|
73
|
+
result = Moxml::Signature.verify(
|
|
74
|
+
context: ctx,
|
|
75
|
+
document: doc,
|
|
76
|
+
key_map: { "my-key" => trusted_public_key },
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## HMAC with truncation
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
signature = Moxml::Signature.sign(
|
|
84
|
+
context: ctx,
|
|
85
|
+
document: doc,
|
|
86
|
+
key: "shared-secret",
|
|
87
|
+
signature_method: "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
|
|
88
|
+
canonicalization_method: "http://www.w3.org/2001/10/xml-exc-c14n#",
|
|
89
|
+
digest_method: "http://www.w3.org/2001/04/xmlenc#sha256",
|
|
90
|
+
reference_uri: "",
|
|
91
|
+
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"],
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
HMAC `OutputLength` truncation below `max(hash_bits/2, 80)` is rejected.
|
|
96
|
+
|
|
97
|
+
## Adding a custom algorithm
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
class MyDigest < Moxml::Signature::Algorithms::DigestBase
|
|
101
|
+
identifier "http://example.com/my-digest"
|
|
102
|
+
|
|
103
|
+
def compute_digest(data)
|
|
104
|
+
OpenSSL::Digest.digest("SHA3-256", data)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Now usable in any Reference#digest_method
|
|
109
|
+
```
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Signing and verification flows
|
|
2
|
+
|
|
3
|
+
## Signer (spec §3.1)
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Moxml::Signature.sign(context:, document:, key:, **options)
|
|
7
|
+
│
|
|
8
|
+
▼
|
|
9
|
+
build a Model::Signature with the requested algorithms
|
|
10
|
+
│
|
|
11
|
+
▼
|
|
12
|
+
Signer#sign
|
|
13
|
+
│
|
|
14
|
+
├── for each Reference in SignedInfo:
|
|
15
|
+
│ ├── ReferenceResolver.resolve(uri) → node or octets
|
|
16
|
+
│ ├── TransformPipeline.apply(input, transforms)
|
|
17
|
+
│ ├── TransformPipeline.to_octets(transformed, reference)
|
|
18
|
+
│ └── DigestMethod#digest_base64(canonical) → Reference#digest_value
|
|
19
|
+
│
|
|
20
|
+
├── Serializer.serialize_signed_info(signed_info) → Moxml::Document
|
|
21
|
+
├── CanonicalizationMethod#canonicalize(signed_info_root) → octets
|
|
22
|
+
└── SignatureMethod#sign(canonical_octets, key) → SignatureValue
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The Signer never touches algorithms directly — every step delegates to
|
|
26
|
+
the algorithm registry. This is OCP: new algorithms are added without
|
|
27
|
+
touching the Signer.
|
|
28
|
+
|
|
29
|
+
## Verifier (spec §3.2 + Best Practice 1)
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Moxml::Signature.verify(context:, document:, key: nil, key_map: {})
|
|
33
|
+
│
|
|
34
|
+
▼
|
|
35
|
+
find all ds:Signature elements in the document
|
|
36
|
+
│
|
|
37
|
+
▼ for each Signature
|
|
38
|
+
verify_one(signature_element)
|
|
39
|
+
│
|
|
40
|
+
├── Parser.parse(signature_element) → Model::Signature
|
|
41
|
+
│
|
|
42
|
+
├── verify_signature_value FIRST (Best Practice 1)
|
|
43
|
+
│ ├── find original SignedInfo element (do NOT re-serialize)
|
|
44
|
+
│ ├── CanonicalizationMethod#canonicalize(signed_info_elem)
|
|
45
|
+
│ ├── resolve_key(signature) (KeyExtractor if no key:)
|
|
46
|
+
│ └── SignatureMethod#verify(canonical, key, sig_value)
|
|
47
|
+
│
|
|
48
|
+
└── if signature value verifies:
|
|
49
|
+
for each Reference:
|
|
50
|
+
├── ReferenceResolver.resolve(uri)
|
|
51
|
+
├── TransformPipeline.apply(...)
|
|
52
|
+
├── TransformPipeline.to_octets(...)
|
|
53
|
+
└── DigestMethod#digest_base64 vs Reference#digest_value
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Best Practice 1: authenticate before transforms
|
|
57
|
+
|
|
58
|
+
The verifier deliberately runs `SignatureValue` validation BEFORE
|
|
59
|
+
applying any transforms. Reason: a malicious signature could include
|
|
60
|
+
XSLT or expensive XPath transforms. Best Practice 1 says only run
|
|
61
|
+
those after authenticating the signer.
|
|
62
|
+
|
|
63
|
+
If `SignatureValue` doesn't verify, we skip reference transforms
|
|
64
|
+
entirely. The result object reports `signature_valid?: false` and
|
|
65
|
+
`references: []`.
|
|
66
|
+
|
|
67
|
+
### Why we canonicalize the original element
|
|
68
|
+
|
|
69
|
+
The verifier canonicalizes the SignedInfo **as it appears in the
|
|
70
|
+
document**, not a re-serialized model. Reason: re-serialization could
|
|
71
|
+
change namespace prefixes (e.g., default-namespace SignedInfo might
|
|
72
|
+
become `ds:SignedInfo`), breaking the byte-exact match the signature
|
|
73
|
+
was computed against.
|
|
74
|
+
|
|
75
|
+
The Signer serializes (it has to — it's building new XML). The Verifier
|
|
76
|
+
has the original element from the document and uses it directly. This
|
|
77
|
+
asymmetry is intentional and necessary for cross-verification with
|
|
78
|
+
libxmlsec1-produced signatures.
|
|
79
|
+
|
|
80
|
+
### Errors are captured, not raised
|
|
81
|
+
|
|
82
|
+
`SingleVerificationResult#error` carries any `VerificationError` or
|
|
83
|
+
`UnknownAlgorithm` that caused failure. The verifier returns a result
|
|
84
|
+
object rather than raising, so a hostile signature cannot panic the
|
|
85
|
+
application. Use `result.results.first.error` for debugging.
|
|
86
|
+
|
|
87
|
+
## Key resolution
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
Moxml::Signature.verify(context:, document:) # no key:
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
When no `key:` is passed, the Verifier uses `KeyExtractor` to derive
|
|
94
|
+
one from the signature's `KeyInfo`:
|
|
95
|
+
|
|
96
|
+
1. **X509Certificate** (preferred) — decode base64 DER →
|
|
97
|
+
`OpenSSL::X509::Certificate` → `.public_key`
|
|
98
|
+
2. **RSAKeyValue** — reconstruct via ASN.1 (OpenSSL 3.x dropped
|
|
99
|
+
`RSA.new(n, e)`)
|
|
100
|
+
3. **DSAKeyValue** — reconstruct via ASN.1
|
|
101
|
+
4. **ECKeyValue** — build SubjectPublicKeyInfo DER for the named curve
|
|
102
|
+
(OpenSSL 3.x made PKey immutable)
|
|
103
|
+
6. **KeyName** — look up in the application-supplied `key_map:`
|
|
104
|
+
|
|
105
|
+
OpenSSL 3.x compatibility notes are inline in `key_extractor.rb`.
|
|
106
|
+
|
|
107
|
+
## Transform pipeline
|
|
108
|
+
|
|
109
|
+
`TransformPipeline` is shared by Signer and Verifier. Each Transform
|
|
110
|
+
in the chain declares `input_type` (`:octets` or `:nodeset`) and
|
|
111
|
+
`output_type`; the pipeline coerces types between transforms per
|
|
112
|
+
spec §4.4.3.2:
|
|
113
|
+
|
|
114
|
+
- `octets → nodeset`: parse as XML via the caller's `context:`
|
|
115
|
+
- `nodeset → octets`: apply inclusive C14N 1.0
|
|
116
|
+
|
|
117
|
+
Final conversion to octets (for digesting) uses the last canonicalization
|
|
118
|
+
transform in the chain if any, else inclusive C14N 1.0 as the spec default.
|
|
119
|
+
|
|
120
|
+
## Result objects
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
result = Moxml::Signature.verify(context:, document:, key:)
|
|
124
|
+
result.valid? # → bool (all signatures valid)
|
|
125
|
+
result.signature_count # → Integer
|
|
126
|
+
result.results # → [SingleVerificationResult, ...]
|
|
127
|
+
result.results.first.signature_valid?
|
|
128
|
+
result.results.first.references # → [ReferenceResult, ...]
|
|
129
|
+
result.results.first.references.first.valid?
|
|
130
|
+
result.results.first.error # → Exception or nil
|
|
131
|
+
result.failing # → [SingleVerificationResult, ...]
|
|
132
|
+
```
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Quick reference
|
|
2
|
+
|
|
3
|
+
## Public API
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
# Sign a document
|
|
7
|
+
signature = Moxml::Signature.sign(
|
|
8
|
+
context:, # Moxml::Context (from Moxml.new(:nokogiri))
|
|
9
|
+
document:, # Moxml::Document to sign
|
|
10
|
+
key:, # OpenSSL::PKey::* or HMAC secret String
|
|
11
|
+
signature_method:, # algorithm URI
|
|
12
|
+
canonicalization_method:, # algorithm URI
|
|
13
|
+
digest_method:, # algorithm URI
|
|
14
|
+
reference_uri:, # "" for whole document
|
|
15
|
+
transforms:, # array of algorithm URIs
|
|
16
|
+
key_info: nil, # optional Model::KeyInfo
|
|
17
|
+
signature_id: nil, # optional Id attribute
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Verify a document
|
|
21
|
+
result = Moxml::Signature.verify(
|
|
22
|
+
context:,
|
|
23
|
+
document:,
|
|
24
|
+
key: nil, # optional; auto-extracted from KeyInfo if absent
|
|
25
|
+
key_map: {}, # for KeyName-based resolution
|
|
26
|
+
)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Algorithm URIs
|
|
30
|
+
|
|
31
|
+
### Digests
|
|
32
|
+
- `http://www.w3.org/2000/09/xmldsig#sha1` (verification only)
|
|
33
|
+
- `http://www.w3.org/2001/04/xmlenc#sha256` (REQUIRED)
|
|
34
|
+
- `http://www.w3.org/2001/04/xmlenc#sha512`
|
|
35
|
+
- `http://www.w3.org/2001/04/xmldsig-more#sha224`
|
|
36
|
+
- `http://www.w3.org/2001/04/xmldsig-more#sha384`
|
|
37
|
+
|
|
38
|
+
### Signature methods
|
|
39
|
+
- RSA-PKCS1v1.5: `http://www.w3.org/2001/04/xmldsig-more#rsa-sha{1,224,256,384,512}`
|
|
40
|
+
- HMAC: `http://www.w3.org/2001/04/xmldsig-more#hmac-sha{1,224,256,384,512}`
|
|
41
|
+
- ECDSA: `http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha{1,224,256,384,512}`
|
|
42
|
+
- DSA: `http://www.w3.org/2000/09/xmldsig#dsa-sha1`, `http://www.w3.org/2009/xmldsig11#dsa-sha256`
|
|
43
|
+
|
|
44
|
+
### Canonicalization
|
|
45
|
+
- Exclusive: `http://www.w3.org/2001/10/xml-exc-c14n#` (add `#WithComments`)
|
|
46
|
+
- Inclusive 1.0: `http://www.w3.org/TR/2001/REC-xml-c14n-20010315`
|
|
47
|
+
- Inclusive 1.1: `http://www.w3.org/2006/12/xml-c14n11`
|
|
48
|
+
|
|
49
|
+
### Transforms
|
|
50
|
+
- `http://www.w3.org/2000/09/xmldsig#enveloped-signature`
|
|
51
|
+
- `http://www.w3.org/2000/09/xmldsig#base64`
|
|
52
|
+
|
|
53
|
+
## Common algorithm combinations
|
|
54
|
+
|
|
55
|
+
### Enveloped RSA-SHA256 (most common)
|
|
56
|
+
```ruby
|
|
57
|
+
signature_method: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
|
58
|
+
canonicalization_method: "http://www.w3.org/2001/10/xml-exc-c14n#",
|
|
59
|
+
digest_method: "http://www.w3.org/2001/04/xmlenc#sha256",
|
|
60
|
+
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"],
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### HMAC-SHA256
|
|
64
|
+
```ruby
|
|
65
|
+
signature_method: "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
|
|
66
|
+
canonicalization_method: "http://www.w3.org/2001/10/xml-exc-c14n#",
|
|
67
|
+
digest_method: "http://www.w3.org/2001/04/xmlenc#sha256",
|
|
68
|
+
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"],
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### ECDSA-SHA256 (P-256)
|
|
72
|
+
```ruby
|
|
73
|
+
signature_method: "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256",
|
|
74
|
+
canonicalization_method: "http://www.w3.org/2001/10/xml-exc-c14n#",
|
|
75
|
+
digest_method: "http://www.w3.org/2001/04/xmlenc#sha256",
|
|
76
|
+
transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"],
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Result inspection
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
result.valid? # → bool (all signatures)
|
|
83
|
+
result.signature_count # → Integer
|
|
84
|
+
result.failing # → [SingleVerificationResult]
|
|
85
|
+
|
|
86
|
+
result.results.first.signature_valid? # → bool (crypto verify)
|
|
87
|
+
result.results.first.references # → [ReferenceResult]
|
|
88
|
+
result.results.first.failing_references # → [ReferenceResult]
|
|
89
|
+
result.results.first.error # → Exception or nil (debugging)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## C14N top-level API
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
Moxml::C14n.canonicalize(node_or_xml, with_comments: false)
|
|
96
|
+
Moxml::C14n.canonicalize_exclusive(node_or_xml, with_comments: false,
|
|
97
|
+
inclusive_namespaces: [])
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Errors
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
Moxml::Signature::Error # base
|
|
104
|
+
Moxml::Signature::SignatureError # generic
|
|
105
|
+
Moxml::Signature::UnknownAlgorithm # URI not in registry
|
|
106
|
+
Moxml::Signature::DuplicateAlgorithm # URI registered twice
|
|
107
|
+
Moxml::Signature::SigningError # signing failed
|
|
108
|
+
Moxml::Signature::VerificationError # verification failed
|
|
109
|
+
Moxml::Signature::ReferenceDigestMismatch # digest mismatch
|
|
110
|
+
Moxml::Signature::SignatureValueMismatch # crypto verify failed
|
|
111
|
+
Moxml::Signature::TransformError # transform pipeline
|
|
112
|
+
Moxml::Signature::CanonicalizationError # C14N failure
|
|
113
|
+
Moxml::Signature::MalformedSignatureError # XML schema violation
|
|
114
|
+
Moxml::Signature::SignatureKeyError # wrong key type
|
|
115
|
+
```
|