nokogiri-xmlsec-instructure 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- 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,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'
|
data/lib/xmlsec.rb
ADDED
@@ -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
|