nokogiri-xmlsec-me-harder 0.9.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 +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 +123 -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 +76 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_init.c +32 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_sign_certificate.c +186 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_sign_rsa.c +167 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_verify_signature_certificates.c +138 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_verify_signature_named_keys.c +133 -0
- data/ext/nokogiri_ext_xmlsec/nokogiri_verify_signature_rsa.c +76 -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 +44 -0
- data/lib/nokogiri-xmlsec.rb +1 -0
- data/lib/xmlsec.rb +104 -0
- data/lib/xmlsec/version.rb +3 -0
- data/nokogiri-xmlsec-me-harder.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 +123 -0
- data/spec/lib/nokogiri/xml/document/unsafe_xml_spec.rb +61 -0
- data/spec/spec_helper.rb +10 -0
- metadata +213 -0
@@ -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,44 @@
|
|
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_with_key(VALUE self, VALUE rb_opts);
|
26
|
+
VALUE sign_with_certificate(VALUE self, VALUE rb_opts);
|
27
|
+
VALUE verify_signature_with_rsa_key(VALUE self, VALUE rb_rsa_key);
|
28
|
+
VALUE verify_signature_with_named_keys(VALUE self, VALUE rb_keys);
|
29
|
+
VALUE verify_signature_with_certificates(VALUE self, VALUE rb_certs);
|
30
|
+
VALUE encrypt_with_key(VALUE self, VALUE rb_rsa_key_name, VALUE rb_rsa_key,
|
31
|
+
VALUE rb_opts);
|
32
|
+
VALUE decrypt_with_key(VALUE self, VALUE rb_key_name, VALUE rb_key);
|
33
|
+
VALUE set_id_attribute(VALUE self, VALUE rb_attr_name);
|
34
|
+
|
35
|
+
void Init_Nokogiri_ext(void);
|
36
|
+
|
37
|
+
extern VALUE rb_cNokogiri_XML_Document;
|
38
|
+
extern VALUE rb_eSigningError;
|
39
|
+
extern VALUE rb_eVerificationError;
|
40
|
+
extern VALUE rb_eKeystoreError;
|
41
|
+
extern VALUE rb_eEncryptionError;
|
42
|
+
extern VALUE rb_eDecryptionError;
|
43
|
+
|
44
|
+
#endif // NOKOGIRI_EXT_XMLSEC_XMLSECRB_H
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'xmlsec'
|
data/lib/xmlsec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require "xmlsec/version"
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'nokogiri_ext_xmlsec'
|
4
|
+
|
5
|
+
class Nokogiri::XML::Document
|
6
|
+
# Signs this document, and then returns it.
|
7
|
+
#
|
8
|
+
# Examples:
|
9
|
+
#
|
10
|
+
# doc.sign! key: 'rsa-private-key'
|
11
|
+
# doc.sign! key: 'rsa-private-key', name: 'key-name'
|
12
|
+
# doc.sign! cert: 'x509 certificate', key: 'cert private key'
|
13
|
+
# doc.sign! cert: 'x509 certificate', key: 'cert private key',
|
14
|
+
# name: 'key-name'
|
15
|
+
def sign! opts
|
16
|
+
if opts.has_key? :cert
|
17
|
+
raise "need a private :key" unless opts[:key]
|
18
|
+
sign_with_certificate opts
|
19
|
+
elsif opts[:key]
|
20
|
+
sign_with_key opts
|
21
|
+
else
|
22
|
+
raise "No private :key was given"
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Verifies the signature on the current document.
|
28
|
+
#
|
29
|
+
# Returns `true` if the signature is valid, `false` otherwise.
|
30
|
+
#
|
31
|
+
# Examples:
|
32
|
+
#
|
33
|
+
# # Try to validate with the given public or private key
|
34
|
+
# doc.verify_with key: 'rsa-key'
|
35
|
+
#
|
36
|
+
# # Try to validate with a set of keys. It will try to match
|
37
|
+
# # based on the contents of the `KeyName` element.
|
38
|
+
# doc.verify_with({
|
39
|
+
# 'key-name' => 'x509 certificate',
|
40
|
+
# 'another-key-name' => 'rsa-public-key'
|
41
|
+
# })
|
42
|
+
#
|
43
|
+
# # Try to validate with a trusted certificate
|
44
|
+
# doc.verify_with(x509: 'certificate')
|
45
|
+
#
|
46
|
+
# # Try to validate with a set of certificates, any one of which
|
47
|
+
# # can match
|
48
|
+
# doc.verify_with(x509: ['cert1', 'cert2'])
|
49
|
+
#
|
50
|
+
# You can also use `:cert` or `:certificate` or `:certs` or
|
51
|
+
# `:certificates` as aliases for `:x509`.
|
52
|
+
#
|
53
|
+
def verify_with opts_or_keys
|
54
|
+
if (certs = opts_or_keys[:cert]) ||
|
55
|
+
(certs = opts_or_keys[:certs])
|
56
|
+
certs = [certs] unless certs.kind_of?(Array)
|
57
|
+
verify_with_certificates certs
|
58
|
+
elsif opts_or_keys[:key]
|
59
|
+
verify_with_rsa_key opts_or_keys[:key]
|
60
|
+
else
|
61
|
+
verify_with_named_keys opts_or_keys
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Attempts to verify the signature of this document using only certificates
|
66
|
+
# installed on the system. This is equivalent to calling
|
67
|
+
# `verify_with certificates: []` (that is, an empty array).
|
68
|
+
#
|
69
|
+
def verify_signature
|
70
|
+
verify_with_certificates []
|
71
|
+
end
|
72
|
+
|
73
|
+
# Encrypts the current document, then returns it.
|
74
|
+
#
|
75
|
+
# Examples:
|
76
|
+
#
|
77
|
+
# # encrypt with a public key and optional key name
|
78
|
+
# doc.encrypt! key: 'public-key', name: 'name'
|
79
|
+
#
|
80
|
+
def encrypt! opts
|
81
|
+
if opts[:key]
|
82
|
+
encrypt_with_key opts[:name].to_s, opts[:key], opts.select { |key, _| key != :key && key != :name }
|
83
|
+
else
|
84
|
+
raise "public :key is required for encryption"
|
85
|
+
end
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
# Decrypts the current document, then returns it.
|
90
|
+
#
|
91
|
+
# Examples:
|
92
|
+
#
|
93
|
+
# # decrypt with a specific private key
|
94
|
+
# doc.decrypt! key: 'private-key'
|
95
|
+
#
|
96
|
+
def decrypt! opts
|
97
|
+
if opts[:key]
|
98
|
+
decrypt_with_key opts[:name].to_s, opts[:key]
|
99
|
+
else
|
100
|
+
raise 'inadequate options specified for decryption'
|
101
|
+
end
|
102
|
+
self
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'xmlsec/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "nokogiri-xmlsec-me-harder"
|
8
|
+
spec.version = Xmlsec::VERSION
|
9
|
+
spec.authors = ["Albert J. Wong"]
|
10
|
+
spec.email = ["awong.dev@gmail.com"]
|
11
|
+
spec.description = %q{Adds support to Ruby for encrypting, decrypting,
|
12
|
+
signing and validating the signatures of XML documents, according to the
|
13
|
+
[XML Encryption Syntax and Processing](http://www.w3.org/TR/xmlenc-core/)
|
14
|
+
standard, by wrapping around the [xmlsec](http://www.aleksey.com/xmlsec) C
|
15
|
+
library and adding relevant methods to `Nokogiri::XML::Document`.
|
16
|
+
Implementation is based off nokogiri-xmlsec by
|
17
|
+
"Colin MacKenzie IV" <inisterchipmunk@gmail.com> with heavy modifications
|
18
|
+
and some API changes.}
|
19
|
+
spec.summary = %q{Wrapper around http://www.aleksey.com/xmlsec to
|
20
|
+
support XML encryption, decryption, signing and signature validation in
|
21
|
+
Ruby}
|
22
|
+
spec.homepage = "https://github.com/omb-awong/xmlsec"
|
23
|
+
spec.license = "MIT"
|
24
|
+
|
25
|
+
spec.files = `git ls-files`.split($/)
|
26
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
27
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
spec.extensions = %w{ext/nokogiri_ext_xmlsec/extconf.rb}
|
30
|
+
|
31
|
+
spec.add_dependency 'nokogiri'
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
34
|
+
spec.add_development_dependency "rake"
|
35
|
+
spec.add_development_dependency "rake-compiler"
|
36
|
+
spec.add_development_dependency "rspec"
|
37
|
+
spec.add_development_dependency "guard-rspec"
|
38
|
+
spec.add_development_dependency "guard-rake"
|
39
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIICLzCCAZgCCQCVuhhQ38rw0TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQGEwJV
|
3
|
+
UzEQMA4GA1UECAwHR2VvcmdpYTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
|
4
|
+
dHkgTHRkMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTAgFw0xMzA1MjUxODQwMDRa
|
5
|
+
GA8zMDEyMDkyNTE4NDAwNFowWzELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0dlb3Jn
|
6
|
+
aWExITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEXMBUGA1UEAwwO
|
7
|
+
d3d3Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALE4oSql
|
8
|
+
eymfHtzOeY86WyvfsjZmaz2XnIo9dzZsK71yMEKkgvXQnnYy9pK0NaYcG0B0hcii
|
9
|
+
3fqGBiHMkZY2BOGWwCC/wOmJCzLq9q6caPWUs71Zko+h59LaqV93vzDmZaXYfFoQ
|
10
|
+
gSVEWpEpCSo560x0mSuLnJYdQQzZ/L6xvxZ1AgMBAAEwDQYJKoZIhvcNAQEFBQAD
|
11
|
+
gYEATyK/RlfpohUVimgFkycTF2hyusjctseXoZDCctgg/STMsL8iA0P9YB6k91GC
|
12
|
+
kWpwevuiwarD1MfSUV6goPINFkIBvfK+5R9lpHaTqqs615z8T9R5VJgaLcFe3tWd
|
13
|
+
7oq3V2q5Nl6MrZfXj2N07qe6/9zfdauxYO26vAEKCvIkbMo=
|
14
|
+
-----END CERTIFICATE-----
|