nokogiri-xmlsec-instructure 0.9.4
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 +7 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/Guardfile +13 -0
- data/LICENSE.txt +22 -0
- data/README.md +124 -0
- data/Rakefile +30 -0
- data/ext/nokogiri_ext_xmlsec/common.h +13 -0
- data/ext/nokogiri_ext_xmlsec/extconf.rb +27 -0
- data/ext/nokogiri_ext_xmlsec/init.c +76 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_decrypt_with_key.c +82 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_encrypt_with_key.c +169 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_helpers_set_attribute_id.c +94 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_init.c +30 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_sign.c +252 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_verify_with.c +259 -0
- data/ext/nokogiri_ext_xmlsec/options.c +166 -0
- data/ext/nokogiri_ext_xmlsec/options.h +36 -0
- data/ext/nokogiri_ext_xmlsec/shutdown.c +12 -0
- data/ext/nokogiri_ext_xmlsec/util.c +139 -0
- data/ext/nokogiri_ext_xmlsec/util.h +42 -0
- data/ext/nokogiri_ext_xmlsec/xmlsecrb.h +42 -0
- data/lib/nokogiri-xmlsec.rb +1 -0
- data/lib/xmlsec.rb +102 -0
- data/lib/xmlsec/version.rb +3 -0
- data/nokogiri-xmlsec-instructure.gemspec +39 -0
- data/spec/fixtures/cert/server.crt +14 -0
- data/spec/fixtures/cert/server.csr +11 -0
- data/spec/fixtures/cert/server.key.decrypted +15 -0
- data/spec/fixtures/cert/server.key.encrypted +18 -0
- data/spec/fixtures/hate.xml +7 -0
- data/spec/fixtures/pwned.xml +1 -0
- data/spec/fixtures/rsa.pem +15 -0
- data/spec/fixtures/rsa.pub +6 -0
- data/spec/fixtures/sign2-doc.xml +6 -0
- data/spec/fixtures/sign2-result.xml +25 -0
- data/spec/fixtures/sign3-result.xml +38 -0
- data/spec/lib/nokogiri/xml/document/encryption_and_decryption_spec.rb +34 -0
- data/spec/lib/nokogiri/xml/document/signing_and_verifying_spec.rb +122 -0
- data/spec/lib/nokogiri/xml/document/unsafe_xml_spec.rb +61 -0
- data/spec/spec_helper.rb +10 -0
- metadata +211 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
#include "xmlsecrb.h"
|
2
|
+
#include "util.h"
|
3
|
+
|
4
|
+
// declaration from Nokogiri proper
|
5
|
+
VALUE Nokogiri_wrap_xml_node(VALUE klass, xmlNodePtr node) ;
|
6
|
+
|
7
|
+
VALUE set_id_attribute(VALUE self, VALUE rb_attr_name) {
|
8
|
+
VALUE rb_exception_result = Qnil;
|
9
|
+
const char* exception_message = NULL;
|
10
|
+
|
11
|
+
xmlNodePtr node = NULL;
|
12
|
+
xmlAttrPtr attr = NULL;
|
13
|
+
xmlAttrPtr tmp = NULL;
|
14
|
+
xmlChar *name = NULL;
|
15
|
+
char *idName = NULL;
|
16
|
+
char *exception_attribute_arg = NULL;
|
17
|
+
|
18
|
+
resetXmlSecError();
|
19
|
+
|
20
|
+
Data_Get_Struct(self, xmlNode, node);
|
21
|
+
Check_Type(rb_attr_name, T_STRING);
|
22
|
+
idName = StringValueCStr(rb_attr_name);
|
23
|
+
|
24
|
+
// find pointer to id attribute
|
25
|
+
attr = xmlHasProp(node, (const xmlChar* )idName);
|
26
|
+
if((attr == NULL) || (attr->children == NULL)) {
|
27
|
+
rb_exception_result = rb_eRuntimeError;
|
28
|
+
exception_message = "Can't find attribute to add register as id";
|
29
|
+
goto done;
|
30
|
+
}
|
31
|
+
|
32
|
+
// get the attribute (id) value
|
33
|
+
name = xmlNodeListGetString(node->doc, attr->children, 1);
|
34
|
+
if(name == NULL) {
|
35
|
+
rb_exception_result = rb_eRuntimeError;
|
36
|
+
exception_message = "has no value";
|
37
|
+
exception_attribute_arg = idName;
|
38
|
+
goto done;
|
39
|
+
}
|
40
|
+
|
41
|
+
// check that we don't have that id already registered
|
42
|
+
tmp = xmlGetID(node->doc, name);
|
43
|
+
if(tmp != NULL) {
|
44
|
+
rb_exception_result = rb_eRuntimeError;
|
45
|
+
exception_message = "is already an ID";
|
46
|
+
exception_attribute_arg = idName;
|
47
|
+
goto done;
|
48
|
+
}
|
49
|
+
|
50
|
+
// finally register id
|
51
|
+
xmlAddID(NULL, node->doc, name, attr);
|
52
|
+
|
53
|
+
done:
|
54
|
+
// and do not forget to cleanup
|
55
|
+
if (name) {
|
56
|
+
xmlFree(name);
|
57
|
+
}
|
58
|
+
|
59
|
+
if(rb_exception_result != Qnil) {
|
60
|
+
if (exception_attribute_arg) {
|
61
|
+
if (hasXmlSecLastError()) {
|
62
|
+
rb_raise(rb_exception_result, "Attribute %s %s, XmlSec error: %s",
|
63
|
+
exception_attribute_arg, exception_message, getXmlSecLastError());
|
64
|
+
} else {
|
65
|
+
rb_raise(rb_exception_result, "Attribute %s %s",
|
66
|
+
exception_attribute_arg, exception_message);
|
67
|
+
}
|
68
|
+
} else {
|
69
|
+
if (hasXmlSecLastError()) {
|
70
|
+
rb_raise(rb_exception_result, "%s, XmlSec error: %s", exception_message,
|
71
|
+
getXmlSecLastError());
|
72
|
+
} else {
|
73
|
+
rb_raise(rb_exception_result, "%s", exception_message);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
return Qtrue;
|
79
|
+
}
|
80
|
+
|
81
|
+
VALUE get_id(VALUE self, VALUE rb_id)
|
82
|
+
{
|
83
|
+
xmlAttrPtr prop;
|
84
|
+
xmlDocPtr doc;
|
85
|
+
|
86
|
+
Check_Type(rb_id, T_STRING);
|
87
|
+
Data_Get_Struct(self, xmlDoc, doc);
|
88
|
+
prop = xmlGetID(doc, (const xmlChar *)StringValueCStr(rb_id));
|
89
|
+
if (prop) {
|
90
|
+
return Nokogiri_wrap_xml_node(Qnil, (xmlNodePtr)prop);
|
91
|
+
} else {
|
92
|
+
return Qnil;
|
93
|
+
}
|
94
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#include "xmlsecrb.h"
|
2
|
+
|
3
|
+
VALUE rb_cNokogiri_XML_Document = Qnil;
|
4
|
+
VALUE rb_cNokogiri_XML_Node = Qnil;
|
5
|
+
VALUE rb_eSigningError = Qnil;
|
6
|
+
VALUE rb_eVerificationError = Qnil;
|
7
|
+
VALUE rb_eKeystoreError = Qnil;
|
8
|
+
VALUE rb_eEncryptionError = Qnil;
|
9
|
+
VALUE rb_eDecryptionError = Qnil;
|
10
|
+
|
11
|
+
void Init_Nokogiri_ext() {
|
12
|
+
VALUE XMLSec = rb_define_module("XMLSec");
|
13
|
+
VALUE Nokogiri = rb_define_module("Nokogiri");
|
14
|
+
VALUE Nokogiri_XML = rb_define_module_under(Nokogiri, "XML");
|
15
|
+
rb_cNokogiri_XML_Document = rb_const_get(Nokogiri_XML, rb_intern("Document"));
|
16
|
+
rb_cNokogiri_XML_Node = rb_const_get(Nokogiri_XML, rb_intern("Node"));
|
17
|
+
|
18
|
+
rb_define_method(rb_cNokogiri_XML_Node, "sign!", sign, 1);
|
19
|
+
rb_define_method(rb_cNokogiri_XML_Node, "verify_with", verify_with, 1);
|
20
|
+
rb_define_method(rb_cNokogiri_XML_Document, "encrypt_with_key", encrypt_with_key, 3);
|
21
|
+
rb_define_method(rb_cNokogiri_XML_Node, "decrypt_with_key", decrypt_with_key, 2);
|
22
|
+
rb_define_method(rb_cNokogiri_XML_Document, "get_id", get_id, 1);
|
23
|
+
rb_define_method(rb_cNokogiri_XML_Node, "set_id_attribute", set_id_attribute, 1);
|
24
|
+
|
25
|
+
rb_eSigningError = rb_define_class_under(XMLSec, "SigningError", rb_eRuntimeError);
|
26
|
+
rb_eVerificationError = rb_define_class_under(XMLSec, "VerificationError", rb_eRuntimeError);
|
27
|
+
rb_eKeystoreError = rb_define_class_under(XMLSec, "KeystoreError", rb_eRuntimeError);
|
28
|
+
rb_eEncryptionError = rb_define_class_under(XMLSec, "EncryptionError", rb_eRuntimeError);
|
29
|
+
rb_eDecryptionError = rb_define_class_under(XMLSec, "DecryptionError", rb_eRuntimeError);
|
30
|
+
}
|
@@ -0,0 +1,252 @@
|
|
1
|
+
#include "xmlsecrb.h"
|
2
|
+
|
3
|
+
#include "options.h"
|
4
|
+
#include "util.h"
|
5
|
+
|
6
|
+
// Appends an xmlsig <dsig:Signature> node to document stored in |self|
|
7
|
+
// with a signature based on the given key and cert.
|
8
|
+
//
|
9
|
+
// Expects a ruby hash for the signing arguments.
|
10
|
+
// Hash parameters:
|
11
|
+
// :key - A PEM encoded rsa key for signing.
|
12
|
+
// :cert - The public cert to include with the signature.
|
13
|
+
// :signature_alg - Algorithm identified by the URL fragment. Supported algorithms
|
14
|
+
// taken from http://www.w3.org/TR/xmldsig-core
|
15
|
+
// :digest_alg - Algorithm identified by the URL fragment. Supported algorithms
|
16
|
+
// taken from http://www.w3.org/TR/xmldsig-core
|
17
|
+
// :name - [optional] String with name of the rsa key.
|
18
|
+
// :uri - [optional] The URI attribute for the <Reference> node in the
|
19
|
+
// signature.
|
20
|
+
// :store_references - [optional] If true, the options hash will be modified,
|
21
|
+
// and this value will be replaced with pre-digest buffer for
|
22
|
+
// debugging purposes
|
23
|
+
VALUE sign(VALUE self, VALUE rb_opts) {
|
24
|
+
VALUE rb_exception_result = Qnil;
|
25
|
+
const char* exception_message = NULL;
|
26
|
+
|
27
|
+
xmlDocPtr doc = NULL;
|
28
|
+
xmlNodePtr envelopeNode = NULL;
|
29
|
+
xmlNodePtr signNode = NULL;
|
30
|
+
xmlNodePtr refNode = NULL;
|
31
|
+
xmlNodePtr keyInfoNode = NULL;
|
32
|
+
xmlSecDSigCtxPtr dsigCtx = NULL;
|
33
|
+
char *keyName = NULL;
|
34
|
+
char *certificate = NULL;
|
35
|
+
char *rsaKey = NULL;
|
36
|
+
char *refUri = NULL;
|
37
|
+
unsigned int rsaKeyLength = 0;
|
38
|
+
unsigned int certificateLength = 0;
|
39
|
+
VALUE rb_references = Qnil;
|
40
|
+
int store_references = 0;
|
41
|
+
VALUE rb_pre_digest_buffer_sym, rb_reference, rb_pre_digest_buffer;
|
42
|
+
xmlSecSize pos;
|
43
|
+
|
44
|
+
VALUE rb_rsa_key = rb_hash_aref(rb_opts, ID2SYM(rb_intern("key")));
|
45
|
+
VALUE rb_cert = rb_hash_aref(rb_opts, ID2SYM(rb_intern("cert")));
|
46
|
+
VALUE rb_signature_alg = rb_hash_aref(rb_opts, ID2SYM(rb_intern("signature_alg")));
|
47
|
+
VALUE rb_digest_alg = rb_hash_aref(rb_opts, ID2SYM(rb_intern("digest_alg")));
|
48
|
+
VALUE rb_uri = rb_hash_aref(rb_opts, ID2SYM(rb_intern("uri")));
|
49
|
+
VALUE rb_key_name = rb_hash_aref(rb_opts, ID2SYM(rb_intern("name")));
|
50
|
+
VALUE rb_store_references = rb_hash_aref(rb_opts, ID2SYM(rb_intern("store_references")));
|
51
|
+
|
52
|
+
resetXmlSecError();
|
53
|
+
|
54
|
+
Check_Type(rb_rsa_key, T_STRING);
|
55
|
+
Check_Type(rb_signature_alg, T_STRING);
|
56
|
+
Check_Type(rb_digest_alg, T_STRING);
|
57
|
+
|
58
|
+
rsaKey = RSTRING_PTR(rb_rsa_key);
|
59
|
+
rsaKeyLength = RSTRING_LEN(rb_rsa_key);
|
60
|
+
|
61
|
+
if (!NIL_P(rb_cert)) {
|
62
|
+
Check_Type(rb_cert, T_STRING);
|
63
|
+
certificate = RSTRING_PTR(rb_cert);
|
64
|
+
certificateLength = RSTRING_LEN(rb_cert);
|
65
|
+
}
|
66
|
+
if (!NIL_P(rb_key_name)) {
|
67
|
+
Check_Type(rb_key_name, T_STRING);
|
68
|
+
keyName = StringValueCStr(rb_key_name);
|
69
|
+
}
|
70
|
+
if (!NIL_P(rb_uri)) {
|
71
|
+
Check_Type(rb_uri, T_STRING);
|
72
|
+
refUri = StringValueCStr(rb_uri);
|
73
|
+
}
|
74
|
+
switch (TYPE(rb_store_references)) {
|
75
|
+
case T_TRUE:
|
76
|
+
store_references = 1;
|
77
|
+
break;
|
78
|
+
case T_FALSE:
|
79
|
+
case T_NIL:
|
80
|
+
break;
|
81
|
+
default:
|
82
|
+
Check_Type(rb_store_references, T_TRUE);
|
83
|
+
break;
|
84
|
+
}
|
85
|
+
|
86
|
+
xmlSecTransformId signature_algorithm = GetSignatureMethod(rb_signature_alg,
|
87
|
+
&rb_exception_result, &exception_message);
|
88
|
+
if (signature_algorithm == xmlSecTransformIdUnknown) {
|
89
|
+
// Propagate exception.
|
90
|
+
goto done;
|
91
|
+
}
|
92
|
+
|
93
|
+
Data_Get_Struct(self, xmlNode, envelopeNode);
|
94
|
+
doc = envelopeNode->doc;
|
95
|
+
// create signature template for enveloped signature.
|
96
|
+
signNode = xmlSecTmplSignatureCreate(doc, xmlSecTransformExclC14NId,
|
97
|
+
signature_algorithm, NULL);
|
98
|
+
if (signNode == NULL) {
|
99
|
+
rb_exception_result = rb_eSigningError;
|
100
|
+
exception_message = "failed to create signature template";
|
101
|
+
goto done;
|
102
|
+
}
|
103
|
+
|
104
|
+
// add <dsig:Signature/> node to the doc
|
105
|
+
xmlAddChild(envelopeNode, signNode);
|
106
|
+
|
107
|
+
// add reference
|
108
|
+
xmlSecTransformId digest_algorithm = GetDigestMethod(rb_digest_alg,
|
109
|
+
&rb_exception_result, &exception_message);
|
110
|
+
if (digest_algorithm == xmlSecTransformIdUnknown) {
|
111
|
+
// Propagate exception.
|
112
|
+
goto done;
|
113
|
+
}
|
114
|
+
refNode = xmlSecTmplSignatureAddReference(signNode, digest_algorithm,
|
115
|
+
NULL, (const xmlChar *)refUri, NULL);
|
116
|
+
if(refNode == NULL) {
|
117
|
+
rb_exception_result = rb_eSigningError;
|
118
|
+
exception_message = "failed to add reference to signature template";
|
119
|
+
goto done;
|
120
|
+
}
|
121
|
+
|
122
|
+
// add enveloped transform
|
123
|
+
if(xmlSecTmplReferenceAddTransform(refNode, xmlSecTransformEnvelopedId) == NULL) {
|
124
|
+
rb_exception_result = rb_eSigningError;
|
125
|
+
exception_message = "failed to add enveloped transform to reference";
|
126
|
+
goto done;
|
127
|
+
}
|
128
|
+
|
129
|
+
if(xmlSecTmplReferenceAddTransform(refNode, xmlSecTransformExclC14NId) == NULL) {
|
130
|
+
rb_exception_result = rb_eSigningError;
|
131
|
+
exception_message = "failed to add canonicalization transform to reference";
|
132
|
+
goto done;
|
133
|
+
}
|
134
|
+
|
135
|
+
// add <dsig:KeyInfo/>
|
136
|
+
keyInfoNode = xmlSecTmplSignatureEnsureKeyInfo(signNode, NULL);
|
137
|
+
if(keyInfoNode == NULL) {
|
138
|
+
rb_exception_result = rb_eSigningError;
|
139
|
+
exception_message = "failed to add key info";
|
140
|
+
goto done;
|
141
|
+
}
|
142
|
+
|
143
|
+
if(certificate) {
|
144
|
+
// add <dsig:X509Data/>
|
145
|
+
if(xmlSecTmplKeyInfoAddX509Data(keyInfoNode) == NULL) {
|
146
|
+
rb_exception_result = rb_eSigningError;
|
147
|
+
exception_message = "failed to add X509Data node";
|
148
|
+
goto done;
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
if(keyName) {
|
153
|
+
// add <dsig:KeyName/>
|
154
|
+
if(xmlSecTmplKeyInfoAddKeyName(keyInfoNode, NULL) == NULL) {
|
155
|
+
rb_exception_result = rb_eSigningError;
|
156
|
+
exception_message = "failed to add key name";
|
157
|
+
goto done;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
// create signature context, we don't need keys manager in this example
|
162
|
+
dsigCtx = createDSigContext(NULL);
|
163
|
+
if(dsigCtx == NULL) {
|
164
|
+
rb_exception_result = rb_eSigningError;
|
165
|
+
exception_message = "failed to create signature context";
|
166
|
+
goto done;
|
167
|
+
}
|
168
|
+
if (store_references) {
|
169
|
+
dsigCtx->flags |= XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES |
|
170
|
+
XMLSEC_DSIG_FLAGS_STORE_MANIFEST_REFERENCES;
|
171
|
+
}
|
172
|
+
|
173
|
+
// load private key, assuming that there is not password
|
174
|
+
dsigCtx->signKey = xmlSecCryptoAppKeyLoadMemory((xmlSecByte *)rsaKey,
|
175
|
+
rsaKeyLength,
|
176
|
+
xmlSecKeyDataFormatPem,
|
177
|
+
NULL, // password
|
178
|
+
NULL,
|
179
|
+
NULL);
|
180
|
+
if(dsigCtx->signKey == NULL) {
|
181
|
+
rb_exception_result = rb_eSigningError;
|
182
|
+
exception_message = "failed to load private key";
|
183
|
+
goto done;
|
184
|
+
}
|
185
|
+
|
186
|
+
if(keyName) {
|
187
|
+
// set key name
|
188
|
+
if(xmlSecKeySetName(dsigCtx->signKey, (xmlSecByte *)keyName) < 0) {
|
189
|
+
rb_exception_result = rb_eSigningError;
|
190
|
+
exception_message = "failed to set key name";
|
191
|
+
goto done;
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
if(certificate) {
|
196
|
+
// load certificate and add to the key
|
197
|
+
if(xmlSecCryptoAppKeyCertLoadMemory(dsigCtx->signKey,
|
198
|
+
(xmlSecByte *)certificate,
|
199
|
+
certificateLength,
|
200
|
+
xmlSecKeyDataFormatPem) < 0) {
|
201
|
+
rb_exception_result = rb_eSigningError;
|
202
|
+
exception_message = "failed to load certificate";
|
203
|
+
goto done;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
// sign the template
|
208
|
+
if(xmlSecDSigCtxSign(dsigCtx, signNode) < 0) {
|
209
|
+
rb_exception_result = rb_eSigningError;
|
210
|
+
exception_message = "signature failed";
|
211
|
+
goto done;
|
212
|
+
}
|
213
|
+
if (store_references) {
|
214
|
+
rb_pre_digest_buffer_sym = ID2SYM(rb_intern("pre_digest_buffer"));
|
215
|
+
rb_references = rb_ary_new2(xmlSecPtrListGetSize(&dsigCtx->signedInfoReferences));
|
216
|
+
rb_hash_aset(rb_opts, ID2SYM(rb_intern("references")), rb_references);
|
217
|
+
|
218
|
+
for(pos = 0; pos < xmlSecPtrListGetSize(&dsigCtx->signedInfoReferences); ++pos) {
|
219
|
+
rb_reference = rb_hash_new();
|
220
|
+
rb_ary_push(rb_references, rb_reference);
|
221
|
+
xmlSecDSigReferenceCtxPtr dsigRefCtx = (xmlSecDSigReferenceCtxPtr)xmlSecPtrListGetItem(&dsigCtx->signedInfoReferences, pos);
|
222
|
+
xmlSecBufferPtr pre_digest_buffer = xmlSecDSigReferenceCtxGetPreDigestBuffer(dsigRefCtx);
|
223
|
+
if (pre_digest_buffer && xmlSecBufferGetData(pre_digest_buffer)) {
|
224
|
+
rb_pre_digest_buffer = rb_str_new((const char *)xmlSecBufferGetData(pre_digest_buffer), xmlSecBufferGetSize(pre_digest_buffer));
|
225
|
+
rb_hash_aset(rb_reference, rb_pre_digest_buffer_sym, rb_pre_digest_buffer);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
done:
|
231
|
+
if(dsigCtx != NULL) {
|
232
|
+
xmlSecDSigCtxDestroy(dsigCtx);
|
233
|
+
}
|
234
|
+
|
235
|
+
if(rb_exception_result != Qnil) {
|
236
|
+
// remove the signature node before raising an exception, so that
|
237
|
+
// the document is untouched
|
238
|
+
if (signNode != NULL) {
|
239
|
+
xmlUnlinkNode(signNode);
|
240
|
+
xmlFreeNode(signNode);
|
241
|
+
}
|
242
|
+
|
243
|
+
if (hasXmlSecLastError()) {
|
244
|
+
rb_raise(rb_exception_result, "%s, XmlSec error: %s", exception_message,
|
245
|
+
getXmlSecLastError());
|
246
|
+
} else {
|
247
|
+
rb_raise(rb_exception_result, "%s", exception_message);
|
248
|
+
}
|
249
|
+
}
|
250
|
+
|
251
|
+
return Qnil;
|
252
|
+
}
|
@@ -0,0 +1,259 @@
|
|
1
|
+
#include "xmlsecrb.h"
|
2
|
+
#include "util.h"
|
3
|
+
|
4
|
+
// Constructs a xmlSecKeysMngrPtr and adds all the certs included in |rb_certs|
|
5
|
+
// array as trusted certificates.
|
6
|
+
static xmlSecKeysMngrPtr createKeyManagerWithRbCertArray(
|
7
|
+
VALUE rb_certs,
|
8
|
+
VALUE* rb_exception_result_out,
|
9
|
+
const char** exception_message_out) {
|
10
|
+
VALUE rb_exception_result = Qnil;
|
11
|
+
const char* exception_message = NULL;
|
12
|
+
|
13
|
+
int i = 0;
|
14
|
+
int numCerts = RARRAY_LEN(rb_certs);
|
15
|
+
xmlSecKeysMngrPtr keyManager = xmlSecKeysMngrCreate();
|
16
|
+
VALUE rb_cert = Qnil;
|
17
|
+
char *cert = NULL;
|
18
|
+
unsigned int certLength = 0;
|
19
|
+
int numSuccessful = 0;
|
20
|
+
|
21
|
+
if (keyManager == NULL) {
|
22
|
+
rb_exception_result = rb_eDecryptionError;
|
23
|
+
exception_message = "failed to create keys manager.";
|
24
|
+
goto done;
|
25
|
+
}
|
26
|
+
|
27
|
+
if (xmlSecCryptoAppDefaultKeysMngrInit(keyManager) < 0) {
|
28
|
+
rb_exception_result = rb_eKeystoreError;
|
29
|
+
exception_message = "could not initialize key manager";
|
30
|
+
goto done;
|
31
|
+
}
|
32
|
+
|
33
|
+
for (i = 0; i < numCerts; i++) {
|
34
|
+
rb_cert = RARRAY_PTR(rb_certs)[i];
|
35
|
+
rb_cert = rb_obj_as_string(rb_cert);
|
36
|
+
Check_Type(rb_cert, T_STRING);
|
37
|
+
cert = RSTRING_PTR(rb_cert);
|
38
|
+
certLength = RSTRING_LEN(rb_cert);
|
39
|
+
|
40
|
+
if(xmlSecCryptoAppKeysMngrCertLoadMemory(keyManager,
|
41
|
+
(xmlSecByte *)cert,
|
42
|
+
certLength,
|
43
|
+
xmlSecKeyDataFormatPem,
|
44
|
+
xmlSecKeyDataTypeTrusted) < 0) {
|
45
|
+
rb_warn("failed to load certificate at index %d", i);
|
46
|
+
} else {
|
47
|
+
numSuccessful++;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
// note, numCerts could be zero, meaning that we should use system SSL certs
|
52
|
+
if (numSuccessful == 0 && numCerts != 0) {
|
53
|
+
rb_exception_result = rb_eKeystoreError;
|
54
|
+
exception_message = "Could not load any of the specified certificates for signature verification";
|
55
|
+
goto done;
|
56
|
+
}
|
57
|
+
|
58
|
+
done:
|
59
|
+
if (!NIL_P(rb_exception_result)) {
|
60
|
+
if (keyManager) {
|
61
|
+
xmlSecKeysMngrDestroy(keyManager);
|
62
|
+
keyManager = NULL;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
*rb_exception_result_out = rb_exception_result;
|
67
|
+
*exception_message_out = exception_message;
|
68
|
+
return keyManager;
|
69
|
+
}
|
70
|
+
|
71
|
+
static int addRubyKeyToManager(VALUE rb_key, VALUE rb_value, VALUE rb_manager) {
|
72
|
+
xmlSecKeysMngrPtr keyManager = (xmlSecKeysMngrPtr)rb_manager;
|
73
|
+
char *keyName, *keyData;
|
74
|
+
unsigned int keyDataLength;
|
75
|
+
xmlSecKeyPtr key;
|
76
|
+
|
77
|
+
Check_Type(rb_key, T_STRING);
|
78
|
+
Check_Type(rb_value, T_STRING);
|
79
|
+
keyName = RSTRING_PTR(rb_key);
|
80
|
+
keyData = RSTRING_PTR(rb_value);
|
81
|
+
keyDataLength = RSTRING_LEN(rb_value);
|
82
|
+
|
83
|
+
// load key
|
84
|
+
key = xmlSecCryptoAppKeyLoadMemory((xmlSecByte *)keyData,
|
85
|
+
keyDataLength,
|
86
|
+
xmlSecKeyDataFormatPem,
|
87
|
+
NULL, // password
|
88
|
+
NULL, NULL);
|
89
|
+
if (key == NULL) {
|
90
|
+
rb_warn("failed to load '%s' public or private pem key", keyName);
|
91
|
+
return ST_CONTINUE;
|
92
|
+
}
|
93
|
+
|
94
|
+
// set key name
|
95
|
+
if (xmlSecKeySetName(key, BAD_CAST keyName) < 0) {
|
96
|
+
rb_warn("failed to set key name for key '%s'", keyName);
|
97
|
+
return ST_CONTINUE;
|
98
|
+
}
|
99
|
+
|
100
|
+
// add key to key manager; from now on the manager is responsible for
|
101
|
+
// destroying the key
|
102
|
+
if (xmlSecCryptoAppDefaultKeysMngrAdoptKey(keyManager, key) < 0) {
|
103
|
+
rb_warn("failed to add key '%s' to key manager", keyName);
|
104
|
+
return ST_CONTINUE;
|
105
|
+
}
|
106
|
+
|
107
|
+
return ST_CONTINUE;
|
108
|
+
}
|
109
|
+
|
110
|
+
// Constructs a xmlSecKeysMngr and adds all the named to key mappings
|
111
|
+
// specified by the |rb_hash| to the key manager.
|
112
|
+
//
|
113
|
+
// Caller takes ownership. Free with xmlSecKeysMngrDestroy().
|
114
|
+
static xmlSecKeysMngrPtr createKeyManagerFromNamedKeys(
|
115
|
+
VALUE rb_hash,
|
116
|
+
VALUE* rb_exception_result_out,
|
117
|
+
const char** exception_message_out) {
|
118
|
+
xmlSecKeysMngrPtr keyManager = xmlSecKeysMngrCreate();
|
119
|
+
if (keyManager == NULL) return NULL;
|
120
|
+
if (xmlSecCryptoAppDefaultKeysMngrInit(keyManager) < 0) {
|
121
|
+
*rb_exception_result_out = rb_eKeystoreError;
|
122
|
+
*exception_message_out = "could not initialize key manager";
|
123
|
+
xmlSecKeysMngrDestroy(keyManager);
|
124
|
+
return NULL;
|
125
|
+
}
|
126
|
+
|
127
|
+
rb_hash_foreach(rb_hash, addRubyKeyToManager, (VALUE)keyManager);
|
128
|
+
|
129
|
+
return keyManager;
|
130
|
+
}
|
131
|
+
|
132
|
+
VALUE verify_with(VALUE self, VALUE rb_opts) {
|
133
|
+
VALUE rb_exception_result = Qnil;
|
134
|
+
const char* exception_message = NULL;
|
135
|
+
|
136
|
+
xmlNodePtr node = NULL;
|
137
|
+
xmlSecDSigCtxPtr dsigCtx = NULL;
|
138
|
+
xmlSecKeysMngrPtr keyManager = NULL;
|
139
|
+
VALUE rb_certs, rb_cert;
|
140
|
+
VALUE rb_rsa_key;
|
141
|
+
VALUE rb_verification_time, rb_verification_depth, rb_verify_certificates;
|
142
|
+
char *rsa_key = NULL;
|
143
|
+
unsigned int rsa_key_length = 0;
|
144
|
+
VALUE result = Qfalse;
|
145
|
+
|
146
|
+
resetXmlSecError();
|
147
|
+
|
148
|
+
Check_Type(rb_opts, T_HASH);
|
149
|
+
Data_Get_Struct(self, xmlNode, node);
|
150
|
+
|
151
|
+
// verify start node
|
152
|
+
if(!xmlSecCheckNodeName(node, xmlSecNodeSignature, xmlSecDSigNs)) {
|
153
|
+
rb_exception_result = rb_eVerificationError;
|
154
|
+
exception_message = "Can only verify a Signature node";
|
155
|
+
goto done;
|
156
|
+
}
|
157
|
+
|
158
|
+
rb_certs = rb_hash_aref(rb_opts, ID2SYM(rb_intern("cert")));
|
159
|
+
if (NIL_P(rb_certs)) {
|
160
|
+
rb_certs = rb_hash_aref(rb_opts, ID2SYM(rb_intern("certs")));
|
161
|
+
}
|
162
|
+
|
163
|
+
rb_verification_depth = rb_hash_aref(rb_opts, ID2SYM(rb_intern("verification_depth")));
|
164
|
+
rb_verification_time = rb_hash_aref(rb_opts, ID2SYM(rb_intern("verification_time")));
|
165
|
+
rb_verify_certificates = rb_hash_aref(rb_opts, ID2SYM(rb_intern("verify_certificates")));
|
166
|
+
|
167
|
+
if (!NIL_P(rb_certs)) {
|
168
|
+
if(TYPE(rb_certs) != T_ARRAY) {
|
169
|
+
rb_cert = rb_certs;
|
170
|
+
rb_certs = rb_ary_new();
|
171
|
+
rb_ary_push(rb_certs, rb_cert);
|
172
|
+
}
|
173
|
+
|
174
|
+
keyManager = createKeyManagerWithRbCertArray(rb_certs, &rb_exception_result,
|
175
|
+
&exception_message);
|
176
|
+
if (keyManager == NULL) {
|
177
|
+
// Propagate exception.
|
178
|
+
goto done;
|
179
|
+
}
|
180
|
+
} else if (!NIL_P(rb_rsa_key = rb_hash_aref(rb_opts, ID2SYM(rb_intern("key"))))) {
|
181
|
+
Check_Type(rb_rsa_key, T_STRING);
|
182
|
+
rsa_key = RSTRING_PTR(rb_rsa_key);
|
183
|
+
rsa_key_length = RSTRING_LEN(rb_rsa_key);
|
184
|
+
} else {
|
185
|
+
keyManager = createKeyManagerFromNamedKeys(rb_opts, &rb_exception_result,
|
186
|
+
&exception_message);
|
187
|
+
if (keyManager == NULL) {
|
188
|
+
// Propagate exception.
|
189
|
+
goto done;
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
// Create signature context.
|
194
|
+
dsigCtx = createDSigContext(keyManager);
|
195
|
+
if(dsigCtx == NULL) {
|
196
|
+
rb_exception_result = rb_eVerificationError;
|
197
|
+
exception_message = "failed to create signature context";
|
198
|
+
goto done;
|
199
|
+
}
|
200
|
+
|
201
|
+
if(!NIL_P(rb_verification_time)) {
|
202
|
+
rb_verification_time = rb_Integer(rb_verification_time);
|
203
|
+
dsigCtx->keyInfoReadCtx.certsVerificationTime = (time_t)NUM2LONG(rb_verification_time);
|
204
|
+
}
|
205
|
+
|
206
|
+
if(rb_verify_certificates == Qfalse) {
|
207
|
+
dsigCtx->keyInfoReadCtx.flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
|
208
|
+
}
|
209
|
+
|
210
|
+
if(!NIL_P(rb_verification_depth)) {
|
211
|
+
rb_verification_depth = rb_Integer(rb_verification_depth);
|
212
|
+
dsigCtx->keyInfoReadCtx.certsVerificationDepth = (time_t)NUM2LONG(rb_verification_depth);
|
213
|
+
}
|
214
|
+
|
215
|
+
if(rsa_key) {
|
216
|
+
// load public key
|
217
|
+
dsigCtx->signKey = xmlSecCryptoAppKeyLoadMemory((xmlSecByte *)rsa_key,
|
218
|
+
rsa_key_length,
|
219
|
+
xmlSecKeyDataFormatPem,
|
220
|
+
NULL, // password
|
221
|
+
NULL, NULL);
|
222
|
+
if(dsigCtx->signKey == NULL) {
|
223
|
+
rb_exception_result = rb_eVerificationError;
|
224
|
+
exception_message = "failed to load public pem key";
|
225
|
+
goto done;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
// verify signature
|
230
|
+
if(xmlSecDSigCtxVerify(dsigCtx, node) < 0) {
|
231
|
+
rb_exception_result = rb_eVerificationError;
|
232
|
+
exception_message = "error occurred during signature verification";
|
233
|
+
goto done;
|
234
|
+
}
|
235
|
+
|
236
|
+
if(dsigCtx->status == xmlSecDSigStatusSucceeded) {
|
237
|
+
result = Qtrue;
|
238
|
+
}
|
239
|
+
|
240
|
+
done:
|
241
|
+
if(dsigCtx != NULL) {
|
242
|
+
xmlSecDSigCtxDestroy(dsigCtx);
|
243
|
+
}
|
244
|
+
|
245
|
+
if (keyManager != NULL) {
|
246
|
+
xmlSecKeysMngrDestroy(keyManager);
|
247
|
+
}
|
248
|
+
|
249
|
+
if(!NIL_P(rb_exception_result)) {
|
250
|
+
if (hasXmlSecLastError()) {
|
251
|
+
rb_raise(rb_exception_result, "%s, XmlSec error: %s", exception_message,
|
252
|
+
getXmlSecLastError());
|
253
|
+
} else {
|
254
|
+
rb_raise(rb_exception_result, "%s", exception_message);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
return result;
|
259
|
+
}
|