nokogiri-xmlsec-instructure 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +13 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +124 -0
  9. data/Rakefile +30 -0
  10. data/ext/nokogiri_ext_xmlsec/common.h +13 -0
  11. data/ext/nokogiri_ext_xmlsec/extconf.rb +27 -0
  12. data/ext/nokogiri_ext_xmlsec/init.c +76 -0
  13. data/ext/nokogiri_ext_xmlsec/nokogiri_decrypt_with_key.c +82 -0
  14. data/ext/nokogiri_ext_xmlsec/nokogiri_encrypt_with_key.c +169 -0
  15. data/ext/nokogiri_ext_xmlsec/nokogiri_helpers_set_attribute_id.c +94 -0
  16. data/ext/nokogiri_ext_xmlsec/nokogiri_init.c +30 -0
  17. data/ext/nokogiri_ext_xmlsec/nokogiri_sign.c +252 -0
  18. data/ext/nokogiri_ext_xmlsec/nokogiri_verify_with.c +259 -0
  19. data/ext/nokogiri_ext_xmlsec/options.c +166 -0
  20. data/ext/nokogiri_ext_xmlsec/options.h +36 -0
  21. data/ext/nokogiri_ext_xmlsec/shutdown.c +12 -0
  22. data/ext/nokogiri_ext_xmlsec/util.c +139 -0
  23. data/ext/nokogiri_ext_xmlsec/util.h +42 -0
  24. data/ext/nokogiri_ext_xmlsec/xmlsecrb.h +42 -0
  25. data/lib/nokogiri-xmlsec.rb +1 -0
  26. data/lib/xmlsec.rb +102 -0
  27. data/lib/xmlsec/version.rb +3 -0
  28. data/nokogiri-xmlsec-instructure.gemspec +39 -0
  29. data/spec/fixtures/cert/server.crt +14 -0
  30. data/spec/fixtures/cert/server.csr +11 -0
  31. data/spec/fixtures/cert/server.key.decrypted +15 -0
  32. data/spec/fixtures/cert/server.key.encrypted +18 -0
  33. data/spec/fixtures/hate.xml +7 -0
  34. data/spec/fixtures/pwned.xml +1 -0
  35. data/spec/fixtures/rsa.pem +15 -0
  36. data/spec/fixtures/rsa.pub +6 -0
  37. data/spec/fixtures/sign2-doc.xml +6 -0
  38. data/spec/fixtures/sign2-result.xml +25 -0
  39. data/spec/fixtures/sign3-result.xml +38 -0
  40. data/spec/lib/nokogiri/xml/document/encryption_and_decryption_spec.rb +34 -0
  41. data/spec/lib/nokogiri/xml/document/signing_and_verifying_spec.rb +122 -0
  42. data/spec/lib/nokogiri/xml/document/unsafe_xml_spec.rb +61 -0
  43. data/spec/spec_helper.rb +10 -0
  44. metadata +211 -0
@@ -0,0 +1,166 @@
1
+ #include "options.h"
2
+
3
+ #include "common.h"
4
+
5
+ #if (XMLSEC_VERSION_MAJOR > 1) || (XMLSEC_VERSION_MAJOR == 1 && (XMLSEC_VERSION_MINOR > 2 || (XMLSEC_VERSION_MINOR == 2 && XMLSEC_VERSION_SUBMINOR >= 20)))
6
+ # define HAS_ECDSA 1
7
+ #else
8
+ # define HAS_ECDSA 0
9
+ #endif
10
+
11
+ // Key Transport Strings.
12
+ static const char RSA1_5[] = "rsa-1_5";
13
+ static const char RSA_OAEP_MGF1P[] = "rsa-oaep-mgf1p";
14
+
15
+ // Block Encryption Strings.
16
+ static const char TRIPLEDES_CBC[] = "tripledes-cbc";
17
+ static const char AES128_CBC[] = "aes128-cbc";
18
+ static const char AES256_CBC[] = "aes256-cbc";
19
+ static const char AES192_CBC[] = "aes192-cbc";
20
+
21
+ // Supported signature algorithms taken from #6 of
22
+ // http://www.w3.org/TR/xmldsig-core1/
23
+ static const char RSA_SHA1[] = "rsa-sha1";
24
+ static const char RSA_SHA224[] = "rsa-sha224";
25
+ static const char RSA_SHA256[] = "rsa-sha256";
26
+ static const char RSA_SHA384[] = "rsa-sha384";
27
+ static const char RSA_SHA512[] = "rsa-sha512";
28
+ static const char DSA_SHA1[] = "dsa-sha1";
29
+
30
+ #if HAS_ECDSA
31
+ static const char ECDSA_SHA1[] = "ecdsa-sha1";
32
+ static const char ECDSA_SHA224[] = "ecdsa-sha224";
33
+ static const char ECDSA_SHA256[] = "ecdsa-sha256";
34
+ static const char ECDSA_SHA384[] = "ecdsa-sha384";
35
+ static const char ECDSA_SHA512[] = "ecdsa-sha512";
36
+ static const char DSA_SHA256[] = "dsa-sha256";
37
+ #endif // HAS_ECDSA
38
+
39
+ // Supported digest algorithms taken from #6 of
40
+ // http://www.w3.org/TR/xmldsig-core1/
41
+ static const char DIGEST_SHA1[] = "sha1";
42
+ static const char DIGEST_SHA224[] = "sha224";
43
+ static const char DIGEST_SHA256[] = "sha256";
44
+ static const char DIGEST_SHA384[] = "sha384";
45
+ static const char DIGEST_SHA512[] = "sha512";
46
+
47
+ BOOL GetXmlEncOptions(VALUE rb_opts,
48
+ XmlEncOptions* options,
49
+ VALUE* rb_exception_result,
50
+ const char** exception_message) {
51
+ VALUE rb_block_encryption = rb_hash_aref(rb_opts, ID2SYM(rb_intern("block_encryption")));
52
+ VALUE rb_key_transport = rb_hash_aref(rb_opts, ID2SYM(rb_intern("key_transport")));
53
+ memset(options, 0, sizeof(XmlEncOptions));
54
+
55
+ if (NIL_P(rb_block_encryption) ||
56
+ TYPE(rb_block_encryption) != T_STRING ||
57
+ NIL_P(rb_key_transport) ||
58
+ TYPE(rb_key_transport) != T_STRING) {
59
+ *rb_exception_result = rb_eArgError;
60
+ *exception_message = "Must supply :block_encryption & :key_transport";
61
+ return FALSE;
62
+ }
63
+
64
+ char* blockEncryptionValue = RSTRING_PTR(rb_block_encryption);
65
+ int blockEncryptionLen = RSTRING_LEN(rb_block_encryption);
66
+ char* keyTransportValue = RSTRING_PTR(rb_key_transport);
67
+ int keyTransportLen = RSTRING_LEN(rb_key_transport);
68
+
69
+ if (strncmp(AES256_CBC, blockEncryptionValue, blockEncryptionLen) == 0) {
70
+ options->block_encryption = xmlSecTransformAes256CbcId;
71
+ options->key_type = "aes";
72
+ options->key_bits = 256;
73
+ } else if (strncmp(AES128_CBC, blockEncryptionValue, blockEncryptionLen) == 0) {
74
+ options->block_encryption = xmlSecTransformAes128CbcId;
75
+ options->key_type = "aes";
76
+ options->key_bits = 128;
77
+ } else if (strncmp(AES192_CBC, blockEncryptionValue, blockEncryptionLen) == 0) {
78
+ options->block_encryption = xmlSecTransformAes192CbcId;
79
+ options->key_type = "aes";
80
+ options->key_bits = 192;
81
+ } else if (strncmp(TRIPLEDES_CBC, blockEncryptionValue, blockEncryptionLen) == 0) {
82
+ options->block_encryption = xmlSecTransformDes3CbcId;
83
+ options->key_type = "des";
84
+ options->key_bits = 192;
85
+ } else {
86
+ *rb_exception_result = rb_eArgError;
87
+ *exception_message = "Unknown :block_encryption value";
88
+ return FALSE;
89
+ }
90
+
91
+ if (strncmp(RSA1_5, keyTransportValue, keyTransportLen) == 0) {
92
+ options->key_transport = xmlSecTransformRsaPkcs1Id;
93
+ } else if (strncmp(RSA_OAEP_MGF1P, keyTransportValue, keyTransportLen) == 0) {
94
+ options->key_transport = xmlSecTransformRsaOaepId;
95
+ } else {
96
+ *rb_exception_result = rb_eArgError;
97
+ *exception_message = "Unknown :key_transport value";
98
+ return FALSE;
99
+ }
100
+
101
+ return TRUE;
102
+ }
103
+
104
+ xmlSecTransformId GetSignatureMethod(VALUE rb_signature_alg,
105
+ VALUE* rb_exception_result,
106
+ const char** exception_message) {
107
+ const char* signatureAlgorithm = RSTRING_PTR(rb_signature_alg);
108
+ unsigned int signatureAlgorithmLength = RSTRING_LEN(rb_signature_alg);
109
+
110
+ if (strncmp(RSA_SHA1, signatureAlgorithm, signatureAlgorithmLength) == 0) {
111
+ return xmlSecTransformRsaSha1Id;
112
+ } else if (strncmp(RSA_SHA224, signatureAlgorithm, signatureAlgorithmLength) == 0) {
113
+ return xmlSecTransformRsaSha224Id;
114
+ } else if (strncmp(RSA_SHA256, signatureAlgorithm, signatureAlgorithmLength) == 0) {
115
+ return xmlSecTransformRsaSha256Id;
116
+ } else if (strncmp(RSA_SHA384, signatureAlgorithm, signatureAlgorithmLength) == 0) {
117
+ return xmlSecTransformRsaSha384Id;
118
+ } else if (strncmp(RSA_SHA512, signatureAlgorithm, signatureAlgorithmLength) == 0) {
119
+ return xmlSecTransformRsaSha512Id;
120
+
121
+ }
122
+ #if HAS_ECDSA
123
+ else if (strncmp(ECDSA_SHA1, signatureAlgorithm, signatureAlgorithmLength) == 0) {
124
+ return xmlSecTransformEcdsaSha1Id;
125
+ } else if (strncmp(ECDSA_SHA224, signatureAlgorithm, signatureAlgorithmLength) == 0) {
126
+ return xmlSecTransformEcdsaSha224Id;
127
+ } else if (strncmp(ECDSA_SHA256, signatureAlgorithm, signatureAlgorithmLength) == 0) {
128
+ return xmlSecTransformEcdsaSha256Id;
129
+ } else if (strncmp(ECDSA_SHA384, signatureAlgorithm, signatureAlgorithmLength) == 0) {
130
+ return xmlSecTransformEcdsaSha384Id;
131
+ } else if (strncmp(ECDSA_SHA512, signatureAlgorithm, signatureAlgorithmLength) == 0) {
132
+ return xmlSecTransformEcdsaSha512Id;
133
+ } else if (strncmp(DSA_SHA1, signatureAlgorithm, signatureAlgorithmLength) == 0) {
134
+ return xmlSecTransformDsaSha1Id;
135
+ } else if (strncmp(DSA_SHA256, signatureAlgorithm, signatureAlgorithmLength) == 0) {
136
+ return xmlSecTransformDsaSha256Id;
137
+ }
138
+ #endif // HAS_ECDSA
139
+
140
+ *rb_exception_result = rb_eArgError;
141
+ *exception_message = "Unknown :signature_alg";
142
+ return xmlSecTransformIdUnknown;
143
+ }
144
+
145
+ xmlSecTransformId GetDigestMethod(VALUE rb_digest_alg,
146
+ VALUE* rb_exception_result,
147
+ const char** exception_message) {
148
+ const char* digestAlgorithm = RSTRING_PTR(rb_digest_alg);
149
+ unsigned int digestAlgorithmLength = RSTRING_LEN(rb_digest_alg);
150
+
151
+ if (strncmp(DIGEST_SHA1, digestAlgorithm, digestAlgorithmLength) == 0) {
152
+ return xmlSecTransformSha1Id;
153
+ } else if (strncmp(DIGEST_SHA224, digestAlgorithm, digestAlgorithmLength) == 0) {
154
+ return xmlSecTransformSha224Id;
155
+ } else if (strncmp(DIGEST_SHA256, digestAlgorithm, digestAlgorithmLength) == 0) {
156
+ return xmlSecTransformSha256Id;
157
+ } else if (strncmp(DIGEST_SHA384, digestAlgorithm, digestAlgorithmLength) == 0) {
158
+ return xmlSecTransformSha384Id;
159
+ } else if (strncmp(DIGEST_SHA512, digestAlgorithm, digestAlgorithmLength) == 0) {
160
+ return xmlSecTransformSha512Id;
161
+ }
162
+
163
+ *rb_exception_result = rb_eArgError;
164
+ *exception_message = "Unknown :digest_algorithm";
165
+ return xmlSecTransformIdUnknown;
166
+ }
@@ -0,0 +1,36 @@
1
+ #ifndef NOKOGIRI_EXT_XMLSEC_OPTIONS_H
2
+ #define NOKOGIRI_EXT_XMLSEC_OPTIONS_H
3
+
4
+ #include "common.h"
5
+
6
+ #include <ruby.h>
7
+ #include <xmlsec/crypto.h>
8
+
9
+ typedef struct {
10
+ // From :block_encryption
11
+ xmlSecTransformId block_encryption;
12
+ const char* key_type;
13
+ int key_bits;
14
+
15
+ // From :key_transport
16
+ xmlSecTransformId key_transport;
17
+ } XmlEncOptions;
18
+
19
+ // Supported algorithms taken from #5.1 of
20
+ // http://www.w3.org/TR/xmlenc-core
21
+ //
22
+ // For options, only use the URL fragment (stuff post #)
23
+ // since that's unique enough and it removes a lot of typing.
24
+ BOOL GetXmlEncOptions(VALUE rb_opts, XmlEncOptions* options,
25
+ VALUE* rb_exception_result,
26
+ const char** exception_message);
27
+
28
+ // XML DSIG helpers.
29
+ xmlSecTransformId GetSignatureMethod(VALUE rb_method,
30
+ VALUE* rb_exception_result,
31
+ const char** exception_message);
32
+ xmlSecTransformId GetDigestMethod(VALUE rb_digest_method,
33
+ VALUE* rb_exception_result,
34
+ const char** exception_message);
35
+
36
+ #endif // NOKOGIRI_EXT_XMLSEC_OPTIONS_H
@@ -0,0 +1,12 @@
1
+ #include "xmlsecrb.h"
2
+
3
+ /* not actually called anywhere right now, but here for posterity */
4
+ void Shutdown_xmlsecrb() {
5
+ xmlSecCryptoShutdown();
6
+ xmlSecCryptoAppShutdown();
7
+ xmlSecShutdown();
8
+ xsltCleanupGlobals();
9
+ #ifndef XMLSEC_NO_XSLT
10
+ xsltCleanupGlobals();
11
+ #endif /* XMLSEC_NO_XSLT */
12
+ }
@@ -0,0 +1,139 @@
1
+ #include "util.h"
2
+
3
+ #include <xmlsec/errors.h>
4
+
5
+ xmlSecKeysMngrPtr createKeyManagerWithSingleKey(
6
+ char* keyStr,
7
+ unsigned int keyLength,
8
+ char *keyName,
9
+ VALUE* rb_exception_result_out,
10
+ const char** exception_message_out) {
11
+ VALUE rb_exception_result = Qnil;
12
+ const char* exception_message = NULL;
13
+ xmlSecKeysMngrPtr mngr = NULL;
14
+ xmlSecKeyPtr key = NULL;
15
+
16
+ /* create and initialize keys manager, we use a simple list based
17
+ * keys manager, implement your own xmlSecKeysStore klass if you need
18
+ * something more sophisticated
19
+ */
20
+ mngr = xmlSecKeysMngrCreate();
21
+ if(mngr == NULL) {
22
+ rb_exception_result = rb_eDecryptionError;
23
+ exception_message = "failed to create keys manager.";
24
+ goto done;
25
+ }
26
+ if(xmlSecCryptoAppDefaultKeysMngrInit(mngr) < 0) {
27
+ rb_exception_result = rb_eDecryptionError;
28
+ exception_message = "failed to initialize keys manager.";
29
+ goto done;
30
+ }
31
+
32
+ /* load private RSA key */
33
+ key = xmlSecCryptoAppKeyLoadMemory((xmlSecByte *)keyStr,
34
+ keyLength,
35
+ xmlSecKeyDataFormatPem,
36
+ NULL, // the key file password
37
+ NULL, // the key password callback
38
+ NULL);// the user context for password callback
39
+ if(key == NULL) {
40
+ rb_exception_result = rb_eDecryptionError;
41
+ exception_message = "failed to load rsa key";
42
+ goto done;
43
+ }
44
+
45
+ if(xmlSecKeySetName(key, BAD_CAST keyName) < 0) {
46
+ rb_exception_result = rb_eDecryptionError;
47
+ exception_message = "failed to set key name";
48
+ goto done;
49
+ }
50
+
51
+ /* add key to keys manager, from now on keys manager is responsible
52
+ * for destroying key
53
+ */
54
+ if(xmlSecCryptoAppDefaultKeysMngrAdoptKey(mngr, key) < 0) {
55
+ rb_exception_result = rb_eDecryptionError;
56
+ exception_message = "failed to add key to keys manager";
57
+ goto done;
58
+ }
59
+
60
+ done:
61
+ if(rb_exception_result != Qnil) {
62
+ if (key) {
63
+ xmlSecKeyDestroy(key);
64
+ }
65
+
66
+ if (mngr) {
67
+ xmlSecKeysMngrDestroy(mngr);
68
+ mngr = NULL;
69
+ }
70
+ }
71
+
72
+ *rb_exception_result_out = rb_exception_result;
73
+ *exception_message_out = exception_message;
74
+ return mngr;
75
+ }
76
+
77
+ xmlSecDSigCtxPtr createDSigContext(xmlSecKeysMngrPtr keyManager) {
78
+ xmlSecDSigCtxPtr dsigCtx = xmlSecDSigCtxCreate(keyManager);
79
+ if (!dsigCtx) {
80
+ return NULL;
81
+ }
82
+
83
+ // Restrict ReferenceUris to same document or empty to avoid XXE attacks.
84
+ dsigCtx->enabledReferenceUris = xmlSecTransformUriTypeEmpty |
85
+ xmlSecTransformUriTypeSameDocument;
86
+
87
+ return dsigCtx;
88
+ }
89
+
90
+ #define ERROR_STACK_SIZE 4096
91
+ static char g_errorStack[ERROR_STACK_SIZE];
92
+ static size_t g_errorStackPos;
93
+
94
+ char* getXmlSecLastError() {
95
+ return g_errorStack;
96
+ }
97
+
98
+ int hasXmlSecLastError() {
99
+ return g_errorStack[0] != '\0';
100
+ }
101
+
102
+ void resetXmlSecError() {
103
+ g_errorStack[0] = '\0';
104
+ g_errorStackPos = 0;
105
+ }
106
+
107
+ void storeErrorCallback(const char *file,
108
+ int line,
109
+ const char *func,
110
+ const char *errorObject,
111
+ const char *errorSubject,
112
+ int reason,
113
+ const char *msg) {
114
+ int i = 0;
115
+ const char* error_msg = NULL;
116
+ int amt = 0;
117
+ if (g_errorStackPos >= ERROR_STACK_SIZE) {
118
+ // Just bail. Earlier errors are more interesting usually anyway.
119
+ return;
120
+ }
121
+
122
+ for(i = 0; (i < XMLSEC_ERRORS_MAX_NUMBER) && (xmlSecErrorsGetMsg(i) != NULL); ++i) {
123
+ if(xmlSecErrorsGetCode(i) == reason) {
124
+ error_msg = xmlSecErrorsGetMsg(i);
125
+ break;
126
+ }
127
+ }
128
+
129
+ amt = snprintf(
130
+ &g_errorStack[g_errorStackPos],
131
+ ERROR_STACK_SIZE - g_errorStackPos,
132
+ "func=%s:file=%s:line=%d:obj=%s:subj=%s:error=%d:%s:%s\n",
133
+ func, file, line, errorObject, errorSubject, reason,
134
+ error_msg ? error_msg : "", msg);
135
+
136
+ if (amt > 0) {
137
+ g_errorStackPos += amt;
138
+ }
139
+ }
@@ -0,0 +1,42 @@
1
+ #ifndef NOKOGIRI_EXT_XMLSEC_UTIL_H
2
+ #define NOKOGIRI_EXT_XMLSEC_UTIL_H
3
+
4
+ #include "xmlsecrb.h"
5
+
6
+ // Constructs a xmlSecKeysMngr and adds the given named key to the manager.
7
+ //
8
+ // Caller takes ownership. Free with xmlSecKeysMngrDestroy().
9
+ xmlSecKeysMngrPtr createKeyManagerWithSingleKey(
10
+ char* keyStr,
11
+ unsigned int keyLength,
12
+ char *keyName,
13
+ VALUE* rb_exception_result_out,
14
+ const char** exception_message_out);
15
+
16
+ // Creates a xmlSecDSigCtx with defaults locked down to prevent XXE.
17
+ //
18
+ // Caller takes ownership of the context. Free with xmlSecDSigCtxDestroy().
19
+ xmlSecDSigCtxPtr createDSigContext(xmlSecKeysMngrPtr keyManager);
20
+
21
+ // Retrieves the recorded error strings from libxmlsec1. Ensure resetXmlSecError()
22
+ // is called at the start of the range of error collection.
23
+ char* getXmlSecLastError();
24
+
25
+ // Reset the recording of errors. After this getXmlSecLastError() will return
26
+ // an empty string. Call at the start of a logical interaction with libxmlsec.
27
+ void resetXmlSecError();
28
+
29
+ // Return false if there are no errors. If false, getXmlSecLastError() will
30
+ // return an empty string.
31
+ int hasXmlSecLastError();
32
+
33
+ // Error reporting hooks to redirect Xmlsec1 library errors away from stdout.
34
+ void storeErrorCallback(const char *file,
35
+ int line,
36
+ const char *func,
37
+ const char *errorObject,
38
+ const char *errorSubject,
39
+ int reason,
40
+ const char *msg);
41
+
42
+ #endif // NOKOGIRI_EXT_XMLSEC_UTIL_H
@@ -0,0 +1,42 @@
1
+ #ifndef NOKOGIRI_EXT_XMLSEC_XMLSECRB_H
2
+ #define NOKOGIRI_EXT_XMLSEC_XMLSECRB_H
3
+
4
+ #include "common.h"
5
+
6
+ #include <ruby.h>
7
+
8
+ #include <libxml/tree.h>
9
+ #include <libxml/xmlmemory.h>
10
+ #include <libxml/parser.h>
11
+ #include <libxml/xmlstring.h>
12
+
13
+ #include <libxslt/xslt.h>
14
+
15
+ #include <xmlsec/xmlsec.h>
16
+ #include <xmlsec/xmltree.h>
17
+ #include <xmlsec/xmldsig.h>
18
+ #include <xmlsec/xmlenc.h>
19
+ #include <xmlsec/templates.h>
20
+ #include <xmlsec/crypto.h>
21
+
22
+ // TODO(awong): Support non-gcc and non-clang compilers.
23
+ #define EXTENSION_EXPORT __attribute__((visibility("default")))
24
+
25
+ VALUE sign(VALUE self, VALUE rb_opts);
26
+ VALUE verify_with(VALUE self, VALUE rb_opts);
27
+ VALUE encrypt_with_key(VALUE self, VALUE rb_rsa_key_name, VALUE rb_rsa_key,
28
+ VALUE rb_opts);
29
+ VALUE decrypt_with_key(VALUE self, VALUE rb_key_name, VALUE rb_key);
30
+ VALUE set_id_attribute(VALUE self, VALUE rb_attr_name);
31
+ VALUE get_id(VALUE self, VALUE rb_id);
32
+
33
+ void Init_Nokogiri_ext(void);
34
+
35
+ extern VALUE rb_cNokogiri_XML_Document;
36
+ extern VALUE rb_eSigningError;
37
+ extern VALUE rb_eVerificationError;
38
+ extern VALUE rb_eKeystoreError;
39
+ extern VALUE rb_eEncryptionError;
40
+ extern VALUE rb_eDecryptionError;
41
+
42
+ #endif // NOKOGIRI_EXT_XMLSEC_XMLSECRB_H
@@ -0,0 +1 @@
1
+ require 'xmlsec'
@@ -0,0 +1,102 @@
1
+ require "xmlsec/version"
2
+ require 'nokogiri'
3
+ require 'nokogiri_ext_xmlsec'
4
+
5
+ class Nokogiri::XML::Document
6
+ def sign! opts
7
+ root.sign! opts
8
+ self
9
+ end
10
+
11
+ # Verifies the signature on the current document.
12
+ #
13
+ # Returns `true` if the signature is valid, `false` otherwise.
14
+ #
15
+ # Examples:
16
+ #
17
+ # # Try to validate with the given public or private key
18
+ # doc.verify_with key: 'rsa-key'
19
+ #
20
+ # # Try to validate with a set of keys. It will try to match
21
+ # # based on the contents of the `KeyName` element.
22
+ # doc.verify_with({
23
+ # 'key-name' => 'x509 certificate',
24
+ # 'another-key-name' => 'rsa-public-key'
25
+ # })
26
+ #
27
+ # # Try to validate with a trusted certificate
28
+ # doc.verify_with(cert: 'certificate')
29
+ #
30
+ # # Try to validate with a set of certificates, any one of which
31
+ # # can match
32
+ # doc.verify_with(certs: ['cert1', 'cert2'])
33
+ #
34
+ # # Validate the signature, checking the certificate validity as of
35
+ # # a certain time (anything that's convertible to an integer, such as a Time)
36
+ # doc.verify_with(cert: 'certificate', verification_time: message_creation_timestamp)
37
+ #
38
+ # # Validate the signature, but don't validate that the certificate is valid,
39
+ # # or has a full trust chain
40
+ # doc.verify_with(cert: 'certificate', verify_certificates: false)
41
+ #
42
+ def verify_with opts_or_keys
43
+ first_signature = root.at_xpath("//ds:Signature", 'ds' => "http://www.w3.org/2000/09/xmldsig#")
44
+ raise XMLSec::VerificationError("start node not found") unless first_signature
45
+
46
+ first_signature.verify_with(opts_or_keys)
47
+ end
48
+
49
+ # Attempts to verify the signature of this document using only certificates
50
+ # installed on the system. This is equivalent to calling
51
+ # `verify_with certificates: []` (that is, an empty array).
52
+ #
53
+ def verify_signature
54
+ verify_with(certs: [])
55
+ end
56
+
57
+ # Encrypts the current document, then returns it.
58
+ #
59
+ # Examples:
60
+ #
61
+ # # encrypt with a public key and optional key name
62
+ # doc.encrypt! key: 'public-key', name: 'name'
63
+ #
64
+ def encrypt! opts
65
+ if opts[:key]
66
+ encrypt_with_key opts[:name].to_s, opts[:key], opts.select { |key, _| key != :key && key != :name }
67
+ else
68
+ raise "public :key is required for encryption"
69
+ end
70
+ self
71
+ end
72
+
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
85
+
86
+ first_encrypted_node.decrypt_with opts
87
+ self
88
+ end
89
+ end
90
+
91
+ class Nokogiri::XML::Node
92
+ def decrypt_with(opts)
93
+ raise 'inadequate options specified for decryption' unless opts[:key]
94
+
95
+ parent = self.parent
96
+ previous = self.previous
97
+ key = opts[:key]
98
+ key = key.to_pem if key.respond_to?(:to_pem)
99
+ decrypt_with_key(opts[:name].to_s, key)
100
+ previous ? previous.next : parent.children.first
101
+ end
102
+ end