nokogiri-xmlsec-instructure 0.10.3 → 0.12.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/ext/nokogiri_ext_xmlsec/extconf.rb +8 -16
- data/ext/nokogiri_ext_xmlsec/nokogiri_decrypt_with_key.c +5 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_encrypt_with_key.c +17 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_sign.c +19 -2
- data/ext/nokogiri_ext_xmlsec/options.c +44 -1
- data/ext/nokogiri_ext_xmlsec/options.h +6 -4
- data/lib/nokogiri-xmlsec.rb +3 -1
- data/lib/xmlsec/version.rb +3 -1
- data/lib/xmlsec.rb +99 -88
- metadata +10 -167
- data/.github/workflows/push.yml +0 -40
- data/.gitignore +0 -23
- data/.rspec +0 -2
- data/.tool-versions +0 -1
- data/Appraisals +0 -9
- data/Gemfile +0 -4
- data/Guardfile +0 -13
- data/LICENSE.txt +0 -22
- data/README.md +0 -132
- data/Rakefile +0 -30
- data/gemfiles/nokogiri_12.5.gemfile +0 -7
- data/gemfiles/nokogiri_13.10.gemfile +0 -7
- data/nokogiri-xmlsec-instructure.gemspec +0 -41
- data/spec/fixtures/cert/server.crt +0 -14
- data/spec/fixtures/cert/server.csr +0 -11
- data/spec/fixtures/cert/server.key.decrypted +0 -15
- data/spec/fixtures/cert/server.key.encrypted +0 -18
- data/spec/fixtures/hate.xml +0 -7
- data/spec/fixtures/pwned.xml +0 -1
- data/spec/fixtures/rsa.pem +0 -15
- data/spec/fixtures/rsa.pub +0 -6
- data/spec/fixtures/sign2-doc.xml +0 -6
- data/spec/fixtures/sign2-result.xml +0 -25
- data/spec/fixtures/sign3-result.xml +0 -39
- data/spec/lib/nokogiri/xml/document/encryption_and_decryption_spec.rb +0 -55
- data/spec/lib/nokogiri/xml/document/signing_and_verifying_spec.rb +0 -122
- data/spec/lib/nokogiri/xml/document/unsafe_xml_spec.rb +0 -61
- data/spec/spec_helper.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b9e1360203a6f2ce84bc88abe8d731d657a88a02642d5cc26827f3dac53c6da
|
4
|
+
data.tar.gz: 1d5fa5d3e4570b50716268d2cd65a7159a0170466babb829042169c71c1e20b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97d2d274344a10a9be1fe7c7c354882ff1d1d1a56ba38cd28c4ba8f2e1f6e64c4aa4f8ac143361fda0187b1ef69ee2ac8cdec0688164c67a83567aab7a1286be
|
7
|
+
data.tar.gz: 477f5c98388d76e02cbdaf913ff21328c3ed397fd5797c363d7dfc5d9641f25657eea0bf0b027ac599d0620976c8e72f12de5c19aa3a0fcb77bc01bf69ff3de9
|
@@ -1,22 +1,14 @@
|
|
1
|
-
|
2
|
-
require 'nokogiri'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
end
|
3
|
+
require "mkmf"
|
4
|
+
require "nokogiri"
|
7
5
|
|
8
|
-
|
6
|
+
abort unless pkg_config("xmlsec1")
|
7
|
+
append_cflags("-fvisibility=hidden")
|
9
8
|
|
10
|
-
|
11
|
-
$CFLAGS << " " + `xmlsec1-config --cflags`.strip
|
12
|
-
$CFLAGS << " -fvisibility=hidden"
|
13
|
-
|
14
|
-
$CFLAGS << Dir[Gem.loaded_specs['nokogiri'].full_gem_path + "/ext/*"].map { |dir| " -I#{dir}"}.join("")
|
15
|
-
|
16
|
-
puts "Cflags: #{$CFLAGS}"
|
17
|
-
$libs = `xmlsec1-config --libs`.strip
|
9
|
+
abort unless find_header("nokogiri.h", *Dir["#{Gem.loaded_specs["nokogiri"].full_gem_path}/ext/*"])
|
18
10
|
|
19
11
|
# We reference symbols out of nokogiri but don't link directly against it
|
20
|
-
|
12
|
+
append_ldflags(["-Wl", "-undefined,dynamic_lookup"])
|
21
13
|
|
22
|
-
create_makefile(
|
14
|
+
create_makefile("nokogiri_ext_xmlsec")
|
@@ -39,6 +39,11 @@ VALUE decrypt_with_key(VALUE self, VALUE rb_key_name, VALUE rb_key) {
|
|
39
39
|
// don't let xmlsec free the node we're looking at out from under us
|
40
40
|
encCtx->flags |= XMLSEC_ENC_RETURN_REPLACED_NODE;
|
41
41
|
|
42
|
+
#ifdef XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH
|
43
|
+
// Enable lax key search, since xmlsec 1.3.0
|
44
|
+
encCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH;
|
45
|
+
#endif
|
46
|
+
|
42
47
|
// decrypt the data
|
43
48
|
if((xmlSecEncCtxDecrypt(encCtx, node) < 0) || (encCtx->result == NULL)) {
|
44
49
|
rb_exception_result = rb_eDecryptionError;
|
@@ -115,6 +115,11 @@ VALUE encrypt_with_key(VALUE self, VALUE rb_rsa_key_name, VALUE rb_rsa_key,
|
|
115
115
|
goto done;
|
116
116
|
}
|
117
117
|
|
118
|
+
#ifdef XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH
|
119
|
+
// Enable lax key search, since xmlsec 1.3.0
|
120
|
+
encCtx->keyInfoWriteCtx.flags |= XMLSEC_KEYINFO_FLAGS_LAX_KEY_SEARCH;
|
121
|
+
#endif
|
122
|
+
|
118
123
|
// Generate the symmetric key.
|
119
124
|
encCtx->encKey = xmlSecKeyGenerateByName(BAD_CAST options.key_type, options.key_bits,
|
120
125
|
xmlSecKeyDataTypeSession);
|
@@ -137,6 +142,10 @@ VALUE encrypt_with_key(VALUE self, VALUE rb_rsa_key_name, VALUE rb_rsa_key,
|
|
137
142
|
}
|
138
143
|
}
|
139
144
|
|
145
|
+
// We don't want xmlsec to free the node we're looking at out from under us,
|
146
|
+
// since it's still referenced from a Ruby object.
|
147
|
+
encCtx->flags |= XMLSEC_ENC_RETURN_REPLACED_NODE;
|
148
|
+
|
140
149
|
// Set key name.
|
141
150
|
if(keyName) {
|
142
151
|
if(xmlSecKeySetName(encCtx->encKey, (xmlSecByte *)keyName) < 0) {
|
@@ -180,6 +189,14 @@ done:
|
|
180
189
|
|
181
190
|
/* cleanup */
|
182
191
|
if(encCtx != NULL) {
|
192
|
+
// the replaced node is orphaned, but not freed; let Nokogiri
|
193
|
+
// own it now
|
194
|
+
if (encCtx->replacedNodeList != NULL) {
|
195
|
+
noko_xml_document_pin_node(encCtx->replacedNodeList);
|
196
|
+
// no really, please don't free it
|
197
|
+
encCtx->replacedNodeList = NULL;
|
198
|
+
}
|
199
|
+
|
183
200
|
xmlSecEncCtxDestroy(encCtx);
|
184
201
|
}
|
185
202
|
|
@@ -14,6 +14,9 @@
|
|
14
14
|
// taken from http://www.w3.org/TR/xmldsig-core
|
15
15
|
// :digest_alg - Algorithm identified by the URL fragment. Supported algorithms
|
16
16
|
// taken from http://www.w3.org/TR/xmldsig-core
|
17
|
+
// :canon_alg - Algorithm identified by the URL fragment. Supported algorithms
|
18
|
+
// taken from http://www.w3.org/TR/xmldsig-core/#sec-c14nAlg
|
19
|
+
// Default is ExclC14N (exclusive canonicalization).
|
17
20
|
// :name - [optional] String with name of the rsa key.
|
18
21
|
// :uri - [optional] The URI attribute for the <Reference> node in the
|
19
22
|
// signature.
|
@@ -45,6 +48,7 @@ VALUE sign(VALUE self, VALUE rb_opts) {
|
|
45
48
|
VALUE rb_cert = rb_hash_aref(rb_opts, ID2SYM(rb_intern("cert")));
|
46
49
|
VALUE rb_signature_alg = rb_hash_aref(rb_opts, ID2SYM(rb_intern("signature_alg")));
|
47
50
|
VALUE rb_digest_alg = rb_hash_aref(rb_opts, ID2SYM(rb_intern("digest_alg")));
|
51
|
+
VALUE rb_canon_alg = rb_hash_aref(rb_opts, ID2SYM(rb_intern("canon_alg")));
|
48
52
|
VALUE rb_uri = rb_hash_aref(rb_opts, ID2SYM(rb_intern("uri")));
|
49
53
|
VALUE rb_key_name = rb_hash_aref(rb_opts, ID2SYM(rb_intern("name")));
|
50
54
|
VALUE rb_store_references = rb_hash_aref(rb_opts, ID2SYM(rb_intern("store_references")));
|
@@ -71,6 +75,9 @@ VALUE sign(VALUE self, VALUE rb_opts) {
|
|
71
75
|
Check_Type(rb_uri, T_STRING);
|
72
76
|
refUri = StringValueCStr(rb_uri);
|
73
77
|
}
|
78
|
+
if (!NIL_P(rb_canon_alg)){
|
79
|
+
Check_Type(rb_canon_alg, T_STRING);
|
80
|
+
}
|
74
81
|
switch (TYPE(rb_store_references)) {
|
75
82
|
case T_TRUE:
|
76
83
|
store_references = 1;
|
@@ -83,6 +90,15 @@ VALUE sign(VALUE self, VALUE rb_opts) {
|
|
83
90
|
break;
|
84
91
|
}
|
85
92
|
|
93
|
+
xmlSecTransformId canon_algorithm = xmlSecTransformExclC14NId; // default
|
94
|
+
if (!NIL_P(rb_canon_alg)){
|
95
|
+
canon_algorithm = GetCanonicalizationMethod(rb_canon_alg,
|
96
|
+
&rb_exception_result, &exception_message);
|
97
|
+
if (canon_algorithm == xmlSecTransformIdUnknown){
|
98
|
+
goto done;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
86
102
|
xmlSecTransformId signature_algorithm = GetSignatureMethod(rb_signature_alg,
|
87
103
|
&rb_exception_result, &exception_message);
|
88
104
|
if (signature_algorithm == xmlSecTransformIdUnknown) {
|
@@ -93,7 +109,7 @@ VALUE sign(VALUE self, VALUE rb_opts) {
|
|
93
109
|
Noko_Node_Get_Struct(self, xmlNode, envelopeNode);
|
94
110
|
doc = envelopeNode->doc;
|
95
111
|
// create signature template for enveloped signature.
|
96
|
-
signNode = xmlSecTmplSignatureCreate(doc,
|
112
|
+
signNode = xmlSecTmplSignatureCreate(doc, canon_algorithm,
|
97
113
|
signature_algorithm, NULL);
|
98
114
|
if (signNode == NULL) {
|
99
115
|
rb_exception_result = rb_eSigningError;
|
@@ -111,6 +127,7 @@ VALUE sign(VALUE self, VALUE rb_opts) {
|
|
111
127
|
// Propagate exception.
|
112
128
|
goto done;
|
113
129
|
}
|
130
|
+
|
114
131
|
refNode = xmlSecTmplSignatureAddReference(signNode, digest_algorithm,
|
115
132
|
NULL, (const xmlChar *)refUri, NULL);
|
116
133
|
if(refNode == NULL) {
|
@@ -126,7 +143,7 @@ VALUE sign(VALUE self, VALUE rb_opts) {
|
|
126
143
|
goto done;
|
127
144
|
}
|
128
145
|
|
129
|
-
if(xmlSecTmplReferenceAddTransform(refNode,
|
146
|
+
if(xmlSecTmplReferenceAddTransform(refNode, canon_algorithm) == NULL){
|
130
147
|
rb_exception_result = rb_eSigningError;
|
131
148
|
exception_message = "failed to add canonicalization transform to reference";
|
132
149
|
goto done;
|
@@ -15,8 +15,11 @@ static const char RSA_OAEP_MGF1P[] = "rsa-oaep-mgf1p";
|
|
15
15
|
// Block Encryption Strings.
|
16
16
|
static const char TRIPLEDES_CBC[] = "tripledes-cbc";
|
17
17
|
static const char AES128_CBC[] = "aes128-cbc";
|
18
|
-
static const char AES256_CBC[] = "aes256-cbc";
|
19
18
|
static const char AES192_CBC[] = "aes192-cbc";
|
19
|
+
static const char AES256_CBC[] = "aes256-cbc";
|
20
|
+
static const char GCM128_CBC[] = "aes128-gcm";
|
21
|
+
static const char GCM192_CBC[] = "aes192-gcm";
|
22
|
+
static const char GCM256_CBC[] = "aes256-gcm";
|
20
23
|
|
21
24
|
// Supported signature algorithms taken from #6 of
|
22
25
|
// http://www.w3.org/TR/xmldsig-core1/
|
@@ -44,6 +47,13 @@ static const char DIGEST_SHA256[] = "sha256";
|
|
44
47
|
static const char DIGEST_SHA384[] = "sha384";
|
45
48
|
static const char DIGEST_SHA512[] = "sha512";
|
46
49
|
|
50
|
+
// Canonicalization algorithms
|
51
|
+
// http://www.w3.org/TR/xmldsig-core1/#sec-Canonicalization
|
52
|
+
static const char C14N[] = "c14n";
|
53
|
+
static const char C14N_WITH_COMMENTS[] = "c14n-with-comments";
|
54
|
+
static const char EXCL_C14N[] = "exc-c14n";
|
55
|
+
static const char EXCL_C14N_WITH_COMMENTS[] = "exc-c14n-with-comments";
|
56
|
+
|
47
57
|
BOOL GetXmlEncOptions(VALUE rb_opts,
|
48
58
|
XmlEncOptions* options,
|
49
59
|
VALUE* rb_exception_result,
|
@@ -82,6 +92,18 @@ BOOL GetXmlEncOptions(VALUE rb_opts,
|
|
82
92
|
options->block_encryption = xmlSecTransformDes3CbcId;
|
83
93
|
options->key_type = "des";
|
84
94
|
options->key_bits = 192;
|
95
|
+
} else if (strncmp(GCM256_CBC, blockEncryptionValue, blockEncryptionLen) == 0) {
|
96
|
+
options->block_encryption = xmlSecTransformAes256GcmId;
|
97
|
+
options->key_type = "aes";
|
98
|
+
options->key_bits = 256;
|
99
|
+
} else if (strncmp(GCM128_CBC, blockEncryptionValue, blockEncryptionLen) == 0) {
|
100
|
+
options->block_encryption = xmlSecTransformAes128GcmId;
|
101
|
+
options->key_type = "aes";
|
102
|
+
options->key_bits = 128;
|
103
|
+
} else if (strncmp(GCM192_CBC, blockEncryptionValue, blockEncryptionLen) == 0) {
|
104
|
+
options->block_encryption = xmlSecTransformAes192GcmId;
|
105
|
+
options->key_type = "aes";
|
106
|
+
options->key_bits = 192;
|
85
107
|
} else {
|
86
108
|
*rb_exception_result = rb_eArgError;
|
87
109
|
*exception_message = "Unknown :block_encryption value";
|
@@ -164,3 +186,24 @@ xmlSecTransformId GetDigestMethod(VALUE rb_digest_alg,
|
|
164
186
|
*exception_message = "Unknown :digest_algorithm";
|
165
187
|
return xmlSecTransformIdUnknown;
|
166
188
|
}
|
189
|
+
|
190
|
+
xmlSecTransformId GetCanonicalizationMethod(VALUE rb_canon_alg,
|
191
|
+
VALUE *rb_exception_result,
|
192
|
+
const char **exception_message){
|
193
|
+
const char *canonAlgorithm = RSTRING_PTR(rb_canon_alg);
|
194
|
+
unsigned int canonAlgorithmLength = RSTRING_LEN(rb_canon_alg);
|
195
|
+
|
196
|
+
if (strncmp(C14N, canonAlgorithm, canonAlgorithmLength) == 0){
|
197
|
+
return xmlSecTransformInclC14NId;
|
198
|
+
}else if (strncmp(C14N_WITH_COMMENTS, canonAlgorithm, canonAlgorithmLength) == 0){
|
199
|
+
return xmlSecTransformInclC14NWithCommentsId;
|
200
|
+
}else if (strncmp(EXCL_C14N, canonAlgorithm, canonAlgorithmLength) == 0){
|
201
|
+
return xmlSecTransformExclC14NId;
|
202
|
+
} else if (strncmp(EXCL_C14N_WITH_COMMENTS, canonAlgorithm, canonAlgorithmLength) == 0){
|
203
|
+
return xmlSecTransformExclC14NWithCommentsId;
|
204
|
+
}
|
205
|
+
|
206
|
+
*rb_exception_result = rb_eArgError;
|
207
|
+
*exception_message = "Unknown :canon_alg";
|
208
|
+
return xmlSecTransformIdUnknown;
|
209
|
+
}
|
@@ -30,7 +30,9 @@ xmlSecTransformId GetSignatureMethod(VALUE rb_method,
|
|
30
30
|
VALUE* rb_exception_result,
|
31
31
|
const char** exception_message);
|
32
32
|
xmlSecTransformId GetDigestMethod(VALUE rb_digest_method,
|
33
|
-
VALUE*
|
34
|
-
const char**
|
35
|
-
|
36
|
-
|
33
|
+
VALUE *rb_exception_result,
|
34
|
+
const char **exception_message);
|
35
|
+
xmlSecTransformId GetCanonicalizationMethod(VALUE rb_canonicalization_method,
|
36
|
+
VALUE *rb_exception_result,
|
37
|
+
const char **exception_message);
|
38
|
+
#endif // NOKOGIRI_EXT_XMLSEC_OPTIONS_H
|
data/lib/nokogiri-xmlsec.rb
CHANGED
data/lib/xmlsec/version.rb
CHANGED
data/lib/xmlsec.rb
CHANGED
@@ -1,103 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "xmlsec/version"
|
2
|
-
require
|
3
|
-
require
|
4
|
+
require "nokogiri"
|
5
|
+
require "nokogiri_ext_xmlsec"
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
module Nokogiri
|
8
|
+
module XML
|
9
|
+
class Document
|
10
|
+
def sign!(opts)
|
11
|
+
root.sign! opts
|
12
|
+
self
|
13
|
+
end
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
15
|
+
# Verifies the signature on the current document.
|
16
|
+
#
|
17
|
+
# Returns `true` if the signature is valid, `false` otherwise.
|
18
|
+
#
|
19
|
+
# Examples:
|
20
|
+
#
|
21
|
+
# # Try to validate with the given public or private key
|
22
|
+
# doc.verify_with key: 'rsa-key'
|
23
|
+
#
|
24
|
+
# # Try to validate with a set of keys. It will try to match
|
25
|
+
# # based on the contents of the `KeyName` element.
|
26
|
+
# doc.verify_with({
|
27
|
+
# 'key-name' => 'x509 certificate',
|
28
|
+
# 'another-key-name' => 'rsa-public-key'
|
29
|
+
# })
|
30
|
+
#
|
31
|
+
# # Try to validate with a trusted certificate
|
32
|
+
# doc.verify_with(cert: 'certificate')
|
33
|
+
#
|
34
|
+
# # Try to validate with a set of certificates, any one of which
|
35
|
+
# # can match
|
36
|
+
# doc.verify_with(certs: ['cert1', 'cert2'])
|
37
|
+
#
|
38
|
+
# # Validate the signature, checking the certificate validity as of
|
39
|
+
# # a certain time (anything that's convertible to an integer, such as a Time)
|
40
|
+
# doc.verify_with(cert: 'certificate', verification_time: message_creation_timestamp)
|
41
|
+
#
|
42
|
+
# # Validate the signature, but don't validate that the certificate is valid,
|
43
|
+
# # or has a full trust chain
|
44
|
+
# doc.verify_with(cert: 'certificate', verify_certificates: false)
|
45
|
+
#
|
46
|
+
def verify_with(opts_or_keys)
|
47
|
+
first_signature = root.at_xpath("//ds:Signature", "ds" => "http://www.w3.org/2000/09/xmldsig#")
|
48
|
+
raise XMLSec::VerificationError("start node not found") unless first_signature
|
45
49
|
|
46
|
-
|
47
|
-
|
50
|
+
first_signature.verify_with(opts_or_keys)
|
51
|
+
end
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
# Attempts to verify the signature of this document using only certificates
|
54
|
+
# installed on the system. This is equivalent to calling
|
55
|
+
# `verify_with certificates: []` (that is, an empty array).
|
56
|
+
#
|
57
|
+
def verify_signature
|
58
|
+
verify_with(certs: [])
|
59
|
+
end
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
61
|
+
# Encrypts the current document, then returns it.
|
62
|
+
#
|
63
|
+
# Examples:
|
64
|
+
#
|
65
|
+
# # encrypt with a public key and optional key name
|
66
|
+
# doc.encrypt! key: 'public-key', name: 'name'
|
67
|
+
#
|
68
|
+
def encrypt!(key:, name: nil, **)
|
69
|
+
root.encrypt_with(key:, name:, **)
|
70
|
+
self
|
71
|
+
end
|
68
72
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
73
|
+
# Decrypts the current document, then returns it.
|
74
|
+
#
|
75
|
+
# Examples:
|
76
|
+
#
|
77
|
+
# # decrypt with a specific private key
|
78
|
+
# doc.decrypt! key: 'private-key'
|
79
|
+
# # pass the key as an OpenSSL PKey object
|
80
|
+
# doc.decrypt! key: OpenSSL::PKey.read('private-key')
|
81
|
+
#
|
82
|
+
def decrypt!(opts)
|
83
|
+
first_encrypted_node = root.at_xpath("//xenc:EncryptedData", "xenc" => "http://www.w3.org/2001/04/xmlenc#")
|
84
|
+
raise XMLSec::DecryptionError("start node not found") unless first_encrypted_node
|
81
85
|
|
82
|
-
|
83
|
-
|
86
|
+
first_encrypted_node.decrypt_with opts
|
87
|
+
self
|
88
|
+
end
|
89
|
+
end
|
84
90
|
end
|
85
91
|
end
|
86
92
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
93
|
+
module Nokogiri
|
94
|
+
module XML
|
95
|
+
class Node
|
96
|
+
def encrypt_with(key:, name: nil, **opts)
|
97
|
+
raise ArgumentError("public :key is required for encryption") unless key
|
98
|
+
|
99
|
+
encrypt_with_key(name, key, opts)
|
100
|
+
end
|
92
101
|
|
93
|
-
|
94
|
-
|
102
|
+
def decrypt_with(opts)
|
103
|
+
raise "inadequate options specified for decryption" unless opts[:key]
|
95
104
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
105
|
+
parent = self.parent
|
106
|
+
previous = self.previous
|
107
|
+
key = opts[:key]
|
108
|
+
key = key.to_pem if key.respond_to?(:to_pem)
|
109
|
+
decrypt_with_key(opts[:name].to_s, key)
|
110
|
+
previous ? previous.next : parent.children.first
|
111
|
+
end
|
112
|
+
end
|
102
113
|
end
|
103
114
|
end
|