xml_security 0.0.1
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.
- data/README.md +33 -0
- data/lib/xml_security/c/lib_xml.rb +48 -0
- data/lib/xml_security/c/xml_sec.rb +259 -0
- data/lib/xml_security.rb +250 -0
- metadata +97 -0
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# XML may not be fun, but at least we can make it secure!
|
2
|
+
|
3
|
+
[](https://travis-ci.org/phinze/xml_security)
|
4
|
+
|
5
|
+
```xml
|
6
|
+
<secrets keep-them="safe" with="xml!" />
|
7
|
+
```
|
8
|
+
|
9
|
+
You too can enjoy all the glory of `xmlsec` brought to your ruby runtime!
|
10
|
+
|
11
|
+
This library is being built with an eye towards building it in a proper SAML
|
12
|
+
integration on a Ruby-based platform.
|
13
|
+
|
14
|
+
## Working features
|
15
|
+
|
16
|
+
* Basic XML Document Signing
|
17
|
+
* Basic XML Signature Verification
|
18
|
+
* Basic XML Document Decryption
|
19
|
+
|
20
|
+
## Lots left to do!
|
21
|
+
|
22
|
+
* XML Encryption
|
23
|
+
* Non-happy-path testing.
|
24
|
+
* Memory leak squashing.
|
25
|
+
* Testing with a SAML-layer library
|
26
|
+
* We'll need TONS of cleanup at the Ruby/C API layer. Goal is to connect the dots, then make the constellations beautiful.
|
27
|
+
|
28
|
+
## References
|
29
|
+
|
30
|
+
* Using FFI for ruby/c interop: https://github.com/ffi/ffi.git
|
31
|
+
* Wrapping XMLSec: http://www.aleksey.com/xmlsec/
|
32
|
+
* XMLSec also used libXML2: http://www.xmlsoft.org/
|
33
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'ffi/libc'
|
3
|
+
|
4
|
+
module XMLSecurity
|
5
|
+
module C
|
6
|
+
module LibXML
|
7
|
+
extend FFI::Library
|
8
|
+
ffi_lib 'libxml2'
|
9
|
+
|
10
|
+
# libxml functions
|
11
|
+
attach_function :xmlInitParser, [], :void
|
12
|
+
attach_function :xmlDocGetRootElement, [ :pointer ], :pointer
|
13
|
+
attach_function :xmlDocDumpFormatMemory, [ :pointer, :pointer, :pointer, :int ], :void
|
14
|
+
attach_function :xmlFreeDoc, [ :pointer ], :void
|
15
|
+
attach_function :xmlParseFile, [ :string ], :pointer
|
16
|
+
attach_function :xmlParseMemory, [ :pointer, :int ], :pointer
|
17
|
+
attach_function :xmlAddChild, [ :pointer, :pointer ], :pointer
|
18
|
+
attach_function :xmlCleanupParser, [], :void
|
19
|
+
attach_function :xmlNodeGetContent, [ :pointer ], :pointer # xmlChar *
|
20
|
+
|
21
|
+
#
|
22
|
+
# Patching over to straight libc malloc/free until we can figure out why
|
23
|
+
# xml{Malloc,Free} cause ruby to blow up so spectacularly. See the
|
24
|
+
# following thread for more info:
|
25
|
+
#
|
26
|
+
# https://groups.google.com/d/topic/ruby-ffi/wClez3YsLQE/discussion
|
27
|
+
#
|
28
|
+
# attach_function :xmlFree, [ :pointer ], :void
|
29
|
+
# attach_function :xmlMalloc, [ :int ], :pointer
|
30
|
+
|
31
|
+
def self.xmlMalloc(*args)
|
32
|
+
FFI::LibC.malloc(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.xmlFree(*args)
|
36
|
+
FFI::LibC.free(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.init
|
40
|
+
xmlInitParser
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.shutdown
|
44
|
+
xmlCleanupParser
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module XMLSecurity
|
4
|
+
module C
|
5
|
+
module XMLSec
|
6
|
+
extend FFI::Library
|
7
|
+
ffi_lib_flags :now, :global
|
8
|
+
ffi_lib 'xmlsec1-openssl'
|
9
|
+
|
10
|
+
enum :xmlSecKeyDataFormat, [
|
11
|
+
:xmlSecKeyDataFormatUnknown,
|
12
|
+
:xmlSecKeyDataFormatBinary,
|
13
|
+
:xmlSecKeyDataFormatPem,
|
14
|
+
:xmlSecKeyDataFormatDer,
|
15
|
+
:xmlSecKeyDataFormatPkcs8Pem,
|
16
|
+
:xmlSecKeyDataFormatPkcs8Der,
|
17
|
+
:xmlSecKeyDataFormatPkcs12,
|
18
|
+
:xmlSecKeyDataFormatCertPem,
|
19
|
+
:xmlSecKeyDataFormatCertDer
|
20
|
+
]
|
21
|
+
|
22
|
+
enum :xmlSecKeyInfoMode, [
|
23
|
+
:xmlSecKeyInfoModeRead,
|
24
|
+
:xmlSecKeyInfoModeWrite
|
25
|
+
]
|
26
|
+
|
27
|
+
enum :xmlSecAllocMode, [
|
28
|
+
:xmlSecAllocModeExact,
|
29
|
+
:xmlSecAllocModeDouble
|
30
|
+
]
|
31
|
+
|
32
|
+
enum :xmlSecTransformStatus, [
|
33
|
+
:xmlSecTransformStatusNone,
|
34
|
+
:xmlSecTransformStatusWorking,
|
35
|
+
:xmlSecTransformStatusFinished,
|
36
|
+
:xmlSecTransformStatusOk,
|
37
|
+
:xmlSecTransformStatusFail
|
38
|
+
]
|
39
|
+
|
40
|
+
enum :xmlSecTransformOperation, [
|
41
|
+
:xmlSecTransformOperationNone, 0,
|
42
|
+
:xmlSecTransformOperationEncode,
|
43
|
+
:xmlSecTransformOperationDecode,
|
44
|
+
:xmlSecTransformOperationSign,
|
45
|
+
:xmlSecTransformOperationVerify,
|
46
|
+
:xmlSecTransformOperationEncrypt,
|
47
|
+
:xmlSecTransformOperationDecrypt
|
48
|
+
]
|
49
|
+
|
50
|
+
enum :xmlSecDSigStatus, [
|
51
|
+
:xmlSecDSigStatusUnknown, 0,
|
52
|
+
:xmlSecDSigStatusSucceeded,
|
53
|
+
:xmlSecDSigStatusInvalid
|
54
|
+
]
|
55
|
+
|
56
|
+
class XmlSecPtrList < FFI::Struct
|
57
|
+
layout \
|
58
|
+
:id, :string,
|
59
|
+
:data, :pointer, # xmlSecPtr*
|
60
|
+
:use, :uint,
|
61
|
+
:max, :uint,
|
62
|
+
:allocMode, :xmlSecAllocMode
|
63
|
+
end
|
64
|
+
|
65
|
+
class XmlSecKeyReq < FFI::Struct
|
66
|
+
layout \
|
67
|
+
:keyId, :string, # xmlSecKeyDataId
|
68
|
+
:keyType, :uint, # xmlSecKeyDataType
|
69
|
+
:keyUsage, :uint, # xmlSecKeyUsage
|
70
|
+
:keyBitsSize, :uint, # xmlSecSize
|
71
|
+
:keyUseWithList, XmlSecPtrList,
|
72
|
+
:reserved1, :pointer, # void *
|
73
|
+
:reserved2, :pointer # void *
|
74
|
+
end
|
75
|
+
|
76
|
+
class XmlSecTransformCtx < FFI::Struct
|
77
|
+
layout \
|
78
|
+
:userData, :pointer, # void *
|
79
|
+
:flags, :uint,
|
80
|
+
:flags2, :uint,
|
81
|
+
:enabledUris, :uint,
|
82
|
+
:enabledTransforms, XmlSecPtrList,
|
83
|
+
:preExecCallback, :pointer, # xmlSecTransformCtxPreExecuteCallback
|
84
|
+
:result, :pointer, # xmlSecBufferPtr
|
85
|
+
:status, :xmlSecTransformStatus,
|
86
|
+
:uri, :string,
|
87
|
+
:xptrExpr, :string,
|
88
|
+
:first, :pointer, # xmlSecTransformPtr
|
89
|
+
:last, :pointer, # xmlSecTransformPtr
|
90
|
+
:reserved0, :pointer, # void *
|
91
|
+
:reserved1, :pointer # void *
|
92
|
+
end
|
93
|
+
|
94
|
+
class XmlSecKeyInfoCtx < FFI::Struct
|
95
|
+
layout \
|
96
|
+
:userDate, :pointer,
|
97
|
+
:flags, :uint,
|
98
|
+
:flags2, :uint,
|
99
|
+
:keysMngr, :pointer,
|
100
|
+
:mode, :xmlSecKeyInfoMode,
|
101
|
+
:enabledKeyData, XmlSecPtrList,
|
102
|
+
:base64LineSize, :int,
|
103
|
+
:retrievalMethodCtx, XmlSecTransformCtx,
|
104
|
+
:maxRetrievalMethodLevel, :int,
|
105
|
+
:encCtx, :pointer,
|
106
|
+
:maxEncryptedKeyLevel, :int,
|
107
|
+
:certsVerificationTime, :time_t,
|
108
|
+
:certsVerificationDepth, :int,
|
109
|
+
:pgpReserved, :pointer,
|
110
|
+
:curRetrievalMethodLevel, :int,
|
111
|
+
:curEncryptedKeyLevel, :int,
|
112
|
+
:keyReq, XmlSecKeyReq,
|
113
|
+
:reserved0, :pointer,
|
114
|
+
:reserved1, :pointer
|
115
|
+
end
|
116
|
+
|
117
|
+
# Would like to use this eventually, but something is wrong below; get segfaults when trying to use it.
|
118
|
+
class XmlSecKeyPtr < FFI::Struct
|
119
|
+
layout \
|
120
|
+
:name, :string, # xmlChar *
|
121
|
+
:value, :pointer, # xmlSecKeyDataPtr
|
122
|
+
:dataList, :pointer, # xmlSecPtrListPtr
|
123
|
+
:usage, :pointer, # xmlSecKeyUsage
|
124
|
+
:notValidBefore, :pointer, # time_t
|
125
|
+
:notValidAfter, :pointer # time_t
|
126
|
+
end
|
127
|
+
|
128
|
+
class XmlSecDSigCtx < FFI::Struct
|
129
|
+
layout \
|
130
|
+
:userData, :pointer, # void *
|
131
|
+
:flags, :uint,
|
132
|
+
:flags2, :uint,
|
133
|
+
:keyInfoReadCtx, XmlSecKeyInfoCtx.by_value,
|
134
|
+
:keyInfoWriteCtx, XmlSecKeyInfoCtx.by_value,
|
135
|
+
:transformCtx, XmlSecTransformCtx.by_value,
|
136
|
+
:enabledReferenceUris, :uint, # xmlSecTransformUriType
|
137
|
+
:enabledReferenceTransforms, :pointer, # xmlSecPtrListPtr
|
138
|
+
:referencePreExecuteCallback, :pointer, # xmlSecTransformCtxPreExecuteCallback
|
139
|
+
:defSignMethodId, :string, # xmlSecTransformId
|
140
|
+
:defC14NMethodId, :string, # xmlSecTransformId
|
141
|
+
:defDigestMethodId, :string, # xmlSecTransformId
|
142
|
+
|
143
|
+
:signKey, :pointer, # xmlSecKeyPtr
|
144
|
+
:operation, :xmlSecTransformOperation,
|
145
|
+
:result, :pointer, # xmlSecBufferPtr
|
146
|
+
:status, :xmlSecDSigStatus,
|
147
|
+
:signMethod, :pointer, # xmlSecTransformPtr
|
148
|
+
:c14nMethod, :pointer, # xmlSecTransformPtr
|
149
|
+
:preSignMemBufMethod, :pointer, # xmlSecTransformPtr
|
150
|
+
:signValueNode, :pointer, # xmlNodePtr
|
151
|
+
:id, :string,
|
152
|
+
:signedInfoReferences, XmlSecPtrList,
|
153
|
+
:manifestReferences, XmlSecPtrList,
|
154
|
+
:reserved0, :pointer,
|
155
|
+
:reserved1, :pointer
|
156
|
+
end
|
157
|
+
|
158
|
+
# xmlsec functions
|
159
|
+
attach_function :xmlSecInit, [], :int
|
160
|
+
attach_function :xmlSecParseMemory, [ :pointer, :uint, :int ], :pointer
|
161
|
+
attach_function :xmlSecFindNode, [ :pointer, :string, :string ], :pointer
|
162
|
+
attach_function :xmlSecFindChild, [ :pointer, :string, :string ], :pointer
|
163
|
+
attach_function :xmlSecDSigCtxCreate, [ :pointer ], XmlSecDSigCtx.by_ref
|
164
|
+
attach_function :xmlSecDSigCtxVerify, [ XmlSecDSigCtx.by_ref, :pointer ], :int
|
165
|
+
attach_function :xmlSecOpenSSLInit, [], :int
|
166
|
+
attach_function :xmlSecOpenSSLShutdown, [], :int
|
167
|
+
attach_function :xmlSecOpenSSLAppShutdown, [], :int
|
168
|
+
attach_function :xmlSecOpenSSLAppInit, [ :pointer ], :int
|
169
|
+
attach_function :xmlSecAddIDs, [ :pointer, :pointer, :pointer ], :void
|
170
|
+
attach_function :xmlSecDSigCtxDestroy, [ XmlSecDSigCtx.by_ref ], :void
|
171
|
+
|
172
|
+
attach_function :xmlSecKeysMngrCreate, [], :pointer
|
173
|
+
attach_function :xmlSecOpenSSLAppDefaultKeysMngrInit, [ :pointer ], :int
|
174
|
+
attach_function :xmlSecOpenSSLAppKeyLoad, [ :string, :xmlSecKeyDataFormat, :pointer, :pointer, :pointer ], :pointer
|
175
|
+
attach_function :xmlSecOpenSSLAppKeyLoadMemory, [ :pointer, :uint, :xmlSecKeyDataFormat, :pointer, :pointer, :pointer ], :pointer
|
176
|
+
attach_function :xmlSecOpenSSLAppKeysMngrCertLoadMemory, [ :pointer, :pointer, :uint, :xmlSecKeyDataFormat, :uint ], :int
|
177
|
+
|
178
|
+
attach_function :xmlSecOpenSSLAppDefaultKeysMngrAdoptKey, [ :pointer, :pointer ], :int
|
179
|
+
attach_function :xmlSecKeysMngrDestroy, [ :pointer ], :void
|
180
|
+
|
181
|
+
attach_function :xmlSecEncCtxCreate, [ :pointer ], :pointer
|
182
|
+
attach_function :xmlSecEncCtxDecrypt, [ :pointer, :pointer ], :int
|
183
|
+
attach_function :xmlSecEncCtxDestroy, [ :pointer ], :void
|
184
|
+
|
185
|
+
attach_function :xmlSecTmplSignatureCreate, [ :pointer, :pointer, :pointer, :string ], :pointer
|
186
|
+
attach_function :xmlSecTmplSignatureAddReference, [ :pointer, :pointer, :pointer, :pointer, :pointer ], :pointer
|
187
|
+
|
188
|
+
attach_function :xmlSecTransformExclC14NGetKlass, [], :pointer
|
189
|
+
attach_function :xmlSecOpenSSLTransformRsaSha1GetKlass, [], :pointer
|
190
|
+
attach_function :xmlSecOpenSSLTransformSha1GetKlass, [], :pointer
|
191
|
+
attach_function :xmlSecTransformEnvelopedGetKlass, [], :pointer
|
192
|
+
attach_function :xmlSecTmplSignatureEnsureKeyInfo, [ :pointer, :pointer ], :pointer
|
193
|
+
|
194
|
+
attach_function :xmlSecTmplReferenceAddTransform, [ :pointer, :pointer ], :pointer
|
195
|
+
|
196
|
+
attach_function :xmlSecKeySetName, [ :pointer, :string ], :int
|
197
|
+
|
198
|
+
attach_function :xmlSecDSigCtxSign, [ :pointer, :pointer ], :int
|
199
|
+
|
200
|
+
attach_function :xmlSecTmplKeyInfoAddKeyName, [ :pointer, :pointer ], :pointer
|
201
|
+
attach_function :xmlSecKeyInfoCtxCreate, [ :pointer ], XmlSecKeyInfoCtx.by_ref
|
202
|
+
attach_function :xmlSecKeyInfoCtxDestroy, [ XmlSecKeyInfoCtx.by_ref ], :void
|
203
|
+
attach_function :xmlSecKeyInfoNodeRead, [ :pointer, :pointer, :pointer ], :int
|
204
|
+
|
205
|
+
attach_function :xmlSecKeyCreate, [], :pointer
|
206
|
+
attach_function :xmlSecKeyDestroy, [ :pointer ], :void
|
207
|
+
|
208
|
+
attach_function :xmlSecBase64Decode, [ :pointer, :pointer, :uint ], :int
|
209
|
+
|
210
|
+
attach_function :xmlSecShutdown, [], :void
|
211
|
+
|
212
|
+
XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS = 0x00000200
|
213
|
+
|
214
|
+
def self.xmlSecNodeSignature
|
215
|
+
'Signature'
|
216
|
+
end
|
217
|
+
|
218
|
+
def self.xmlSecNodeKeyInfo
|
219
|
+
'KeyInfo'
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.xmlSecNodeX509Certificate
|
223
|
+
'X509Certificate'
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.xmlSecDSigNs
|
227
|
+
'http://www.w3.org/2000/09/xmldsig#'
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.xmlSecEncNs
|
231
|
+
'http://www.w3.org/2001/04/xmlenc#'
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.xmlSecKeyDataTypeTrusted
|
235
|
+
0x0100
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.xmlSecNodeEncryptedData
|
239
|
+
'EncryptedData'
|
240
|
+
end
|
241
|
+
|
242
|
+
def self.xmlSecNodeX509Certificate
|
243
|
+
'X509Certificate'
|
244
|
+
end
|
245
|
+
|
246
|
+
def self.init
|
247
|
+
raise "Failed initializing XMLSec" if xmlSecInit < 0
|
248
|
+
raise "Failed initializing app crypto" if xmlSecOpenSSLAppInit(nil) < 0
|
249
|
+
raise "Failed initializing crypto" if xmlSecOpenSSLInit < 0
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.shutdown
|
253
|
+
xmlSecOpenSSLShutdown
|
254
|
+
xmlSecOpenSSLAppShutdown
|
255
|
+
xmlSecShutdown
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
data/lib/xml_security.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
# The contents of this file are subject to the terms
|
2
|
+
# of the Common Development and Distribution License
|
3
|
+
# (the License). You may not use this file except in
|
4
|
+
# compliance with the License.
|
5
|
+
#
|
6
|
+
# You can obtain a copy of the License at
|
7
|
+
# https://opensso.dev.java.net/public/CDDLv1.0.html or
|
8
|
+
# opensso/legal/CDDLv1.0.txt
|
9
|
+
# See the License for the specific language governing
|
10
|
+
# permission and limitations under the License.
|
11
|
+
#
|
12
|
+
# When distributing Covered Code, include this CDDL
|
13
|
+
# Header Notice in each file and include the License file
|
14
|
+
# at opensso/legal/CDDLv1.0.txt.
|
15
|
+
# If applicable, add the following below the CDDL Header,
|
16
|
+
# with the fields enclosed by brackets [] replaced by
|
17
|
+
# your own identifying information:
|
18
|
+
# "Portions Copyrighted [year] [name of copyright owner]"
|
19
|
+
#
|
20
|
+
# $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
|
21
|
+
#
|
22
|
+
# Copyright 2007 Sun Microsystems Inc. All Rights Reserved
|
23
|
+
# Portions Copyrighted 2007 Todd W Saxton.
|
24
|
+
#
|
25
|
+
require 'rubygems'
|
26
|
+
require 'ffi'
|
27
|
+
|
28
|
+
require 'xml_security/c/lib_xml'
|
29
|
+
require 'xml_security/c/xml_sec'
|
30
|
+
|
31
|
+
require 'ruby-debug'
|
32
|
+
|
33
|
+
module XMLSecurity
|
34
|
+
NAMESPACES = {
|
35
|
+
"xenc" => "http://www.w3.org/2001/04/xmlenc#",
|
36
|
+
"ds" => "http://www.w3.org/2000/09/xmldsig#"
|
37
|
+
}
|
38
|
+
|
39
|
+
def self.init
|
40
|
+
unless initialized?
|
41
|
+
C::LibXML.init
|
42
|
+
C::XMLSec.init
|
43
|
+
@initialized = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.shutdown
|
48
|
+
if initialized?
|
49
|
+
C::XMLSec.shutdown
|
50
|
+
C::LibXML.shutdown
|
51
|
+
@initialized = false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.initialized?
|
56
|
+
!!@initialized
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.sign(xml_document, private_key)
|
60
|
+
init
|
61
|
+
|
62
|
+
doc = C::LibXML.xmlParseMemory(xml_document, xml_document.size)
|
63
|
+
raise "could not parse XML document" if doc.null?
|
64
|
+
|
65
|
+
canonicalization_method_id = C::XMLSec.xmlSecTransformExclC14NGetKlass
|
66
|
+
sign_method_id = C::XMLSec.xmlSecOpenSSLTransformRsaSha1GetKlass
|
67
|
+
|
68
|
+
sign_node = C::XMLSec.xmlSecTmplSignatureCreate(doc, canonicalization_method_id, sign_method_id, nil)
|
69
|
+
|
70
|
+
raise "failed to create signature template" if sign_node.null?
|
71
|
+
C::LibXML.xmlAddChild(C::LibXML.xmlDocGetRootElement(doc), sign_node)
|
72
|
+
|
73
|
+
ref_node = C::XMLSec.xmlSecTmplSignatureAddReference(sign_node, C::XMLSec.xmlSecOpenSSLTransformSha1GetKlass, nil, nil, nil)
|
74
|
+
raise "failed to add a reference" if ref_node.null?
|
75
|
+
|
76
|
+
envelope_result = C::XMLSec.xmlSecTmplReferenceAddTransform(ref_node, C::XMLSec.xmlSecTransformEnvelopedGetKlass)
|
77
|
+
raise "failed to add envelope transform to reference" if envelope_result.null?
|
78
|
+
|
79
|
+
key_info_node = C::XMLSec.xmlSecTmplSignatureEnsureKeyInfo(sign_node, nil)
|
80
|
+
raise "failed to add key info" if key_info_node.null?
|
81
|
+
|
82
|
+
digital_signature_context = C::XMLSec.xmlSecDSigCtxCreate(nil)
|
83
|
+
raise "failed to create signature context" if digital_signature_context.null?
|
84
|
+
|
85
|
+
digital_signature_context[:signKey] = C::XMLSec.xmlSecOpenSSLAppKeyLoad(private_key, :xmlSecKeyDataFormatPem, nil, nil, nil)
|
86
|
+
raise "failed to load private pem ley from #{private_key}" if digital_signature_context[:signKey].null?
|
87
|
+
|
88
|
+
if C::XMLSec.xmlSecKeySetName(digital_signature_context[:signKey], File.basename(private_key)) < 0
|
89
|
+
raise "failed to set key name for key of #{private_key}"
|
90
|
+
end
|
91
|
+
|
92
|
+
if C::XMLSec.xmlSecTmplKeyInfoAddKeyName(key_info_node, nil).null?
|
93
|
+
raise "failed to add key info"
|
94
|
+
end
|
95
|
+
|
96
|
+
if C::XMLSec.xmlSecDSigCtxSign(digital_signature_context, sign_node) < 0
|
97
|
+
raise "signature failed!"
|
98
|
+
end
|
99
|
+
|
100
|
+
_dump_doc(doc)
|
101
|
+
ensure
|
102
|
+
C::LibXML.xmlFreeDoc(doc) if defined?(doc) && !doc.null?
|
103
|
+
C::XMLSec.xmlSecDSigCtxDestroy(digital_signature_context) if defined?(digital_signature_context) && !digital_signature_context.null?
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.verify_signature(signed_xml_document, cert_fingerprint=nil)
|
107
|
+
init
|
108
|
+
if cert_fingerprint
|
109
|
+
return false unless _fingerprint_matches?(cert_fingerprint, cert)
|
110
|
+
end
|
111
|
+
|
112
|
+
doc = C::LibXML.xmlParseMemory(signed_xml_document, signed_xml_document.size)
|
113
|
+
raise "could not parse XML document" if doc.null?
|
114
|
+
|
115
|
+
doc_root = C::LibXML.xmlDocGetRootElement(doc)
|
116
|
+
raise "could not get doc root" if doc_root.null?
|
117
|
+
|
118
|
+
keys_manager = _init_keys_manager
|
119
|
+
|
120
|
+
digital_signature_context = C::XMLSec.xmlSecDSigCtxCreate(keys_manager)
|
121
|
+
raise "failed to create signature context" if digital_signature_context.null?
|
122
|
+
|
123
|
+
key_info_context = C::XMLSec.xmlSecKeyInfoCtxCreate(keys_manager)
|
124
|
+
raise "could not create key info context" if key_info_context.null?
|
125
|
+
|
126
|
+
signature_node = C::XMLSec.xmlSecFindNode(doc_root, C::XMLSec.xmlSecNodeSignature, C::XMLSec.xmlSecDSigNs)
|
127
|
+
raise "signature node not found" if signature_node.null?
|
128
|
+
|
129
|
+
key_info_node = C::XMLSec.xmlSecFindNode(signature_node, C::XMLSec.xmlSecNodeKeyInfo, C::XMLSec.xmlSecDSigNs)
|
130
|
+
raise "key_info node not found" if key_info_node.null?
|
131
|
+
|
132
|
+
certificate_node = C::XMLSec.xmlSecFindNode(signature_node, C::XMLSec.xmlSecNodeX509Certificate, C::XMLSec.xmlSecDSigNs)
|
133
|
+
raise "certificate node not found" if certificate_node.null?
|
134
|
+
|
135
|
+
key = C::XMLSec.xmlSecKeyCreate
|
136
|
+
raise "error while allocating security key" if key.null?
|
137
|
+
|
138
|
+
certptr = C::LibXML.xmlNodeGetContent(certificate_node)
|
139
|
+
raise "error while reading certificate node" if certptr.null?
|
140
|
+
cert64 = certptr.read_string
|
141
|
+
# C::LibXML.xmlFree(certptr)
|
142
|
+
|
143
|
+
certptr = FFI::MemoryPointer.new(:uchar, cert64.size)
|
144
|
+
|
145
|
+
bytesout = C::XMLSec.xmlSecBase64Decode(cert64, certptr, certptr.size)
|
146
|
+
cert = certptr.read_bytes(bytesout)
|
147
|
+
|
148
|
+
key_add_result = C::XMLSec.xmlSecOpenSSLAppKeysMngrCertLoadMemory(keys_manager, cert, cert.size, :xmlSecKeyDataFormatDer, C::XMLSec.xmlSecKeyDataTypeTrusted)
|
149
|
+
raise "failed to add key to keys manager" if key_add_result < 0
|
150
|
+
|
151
|
+
if C::XMLSec.xmlSecDSigCtxVerify(digital_signature_context, signature_node) < 0
|
152
|
+
raise "error during signature verification"
|
153
|
+
end
|
154
|
+
|
155
|
+
digital_signature_context[:status] == :xmlSecDSigStatusSucceeded
|
156
|
+
ensure
|
157
|
+
C::LibXML.xmlFreeDoc(doc) if defined?(doc) && doc && !doc.null?
|
158
|
+
C::XMLSec.xmlSecDSigCtxDestroy(digital_signature_context) if defined?(digital_signature_context) && digital_signature_context && !digital_signature_context.null?
|
159
|
+
C::XMLSec.xmlSecKeysMngrDestroy(keys_manager) if defined?(keys_manager) && keys_manager && !keys_manager.null?
|
160
|
+
C::XMLSec.xmlSecKeyInfoCtxDestroy(key_info_context) if defined?(key_info_context) && key_info_context && !key_info_context.null?
|
161
|
+
C::XMLSec.xmlSecKeyDestroy(key) if defined?(key) && key && !key.null?
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.decrypt(encrypted_xml, private_key)
|
165
|
+
init
|
166
|
+
|
167
|
+
keys_manager = _init_keys_manager
|
168
|
+
|
169
|
+
key = C::XMLSec.xmlSecOpenSSLAppKeyLoad(private_key, :xmlSecKeyDataFormatPem, nil, nil, nil)
|
170
|
+
raise "failed to load private pem ley from #{private_key}" if key.null?
|
171
|
+
|
172
|
+
key_add_result = C::XMLSec.xmlSecOpenSSLAppDefaultKeysMngrAdoptKey(keys_manager, key)
|
173
|
+
raise "failed to add key to keys manager" if key_add_result < 0
|
174
|
+
|
175
|
+
doc = C::LibXML.xmlParseMemory(encrypted_xml, encrypted_xml.size)
|
176
|
+
raise "could not parse XML document" if doc.null?
|
177
|
+
|
178
|
+
doc_root = C::LibXML.xmlDocGetRootElement(doc)
|
179
|
+
raise "could not get root element" if doc_root.null?
|
180
|
+
|
181
|
+
start_node = C::XMLSec.xmlSecFindNode(doc_root, C::XMLSec.xmlSecNodeEncryptedData, C::XMLSec.xmlSecEncNs)
|
182
|
+
raise "start node not found" if start_node.null?
|
183
|
+
|
184
|
+
encryption_context = C::XMLSec.xmlSecEncCtxCreate(keys_manager)
|
185
|
+
raise "failed to create encryption context" if encryption_context.null?
|
186
|
+
|
187
|
+
encryption_result = C::XMLSec.xmlSecEncCtxDecrypt(encryption_context, start_node)
|
188
|
+
raise "decryption failed" if (encryption_result < 0)
|
189
|
+
|
190
|
+
_dump_doc(doc)
|
191
|
+
end
|
192
|
+
|
193
|
+
def self._init_keys_manager
|
194
|
+
keys_manager = C::XMLSec.xmlSecKeysMngrCreate
|
195
|
+
raise "failed to create keys manager" if keys_manager.null?
|
196
|
+
|
197
|
+
if C::XMLSec.xmlSecOpenSSLAppDefaultKeysMngrInit(keys_manager) < 0
|
198
|
+
raise "failed to init and load default openssl keys into keys manager"
|
199
|
+
end
|
200
|
+
|
201
|
+
keys_manager
|
202
|
+
end
|
203
|
+
|
204
|
+
def self._format_cert(cert)
|
205
|
+
# re-encode the certificate in the proper format
|
206
|
+
# this snippet is from http://bugs.ruby-lang.org/issues/4421
|
207
|
+
rsa = cert.public_key
|
208
|
+
modulus = rsa.n
|
209
|
+
exponent = rsa.e
|
210
|
+
oid = OpenSSL::ASN1::ObjectId.new("rsaEncryption")
|
211
|
+
alg_id = OpenSSL::ASN1::Sequence.new([oid, OpenSSL::ASN1::Null.new(nil)])
|
212
|
+
ary = [OpenSSL::ASN1::Integer.new(modulus), OpenSSL::ASN1::Integer.new(exponent)]
|
213
|
+
pub_key = OpenSSL::ASN1::Sequence.new(ary)
|
214
|
+
enc_pk = OpenSSL::ASN1::BitString.new(pub_key.to_der)
|
215
|
+
subject_pk_info = OpenSSL::ASN1::Sequence.new([alg_id, enc_pk])
|
216
|
+
base64 = Base64.encode64(subject_pk_info.to_der)
|
217
|
+
|
218
|
+
# This is the equivalent to the X.509 encoding used in >= 1.9.3
|
219
|
+
"-----BEGIN PUBLIC KEY-----\n#{base64}-----END PUBLIC KEY-----"
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
def self._fingerprint_matches?(expected_fingerprint, cert)
|
224
|
+
cert_fingerprint = Digest::SHA1.hexdigest(cert.to_der)
|
225
|
+
expected_fingerprint = idp_cert_fingerprint.gsub(":", "").downcase
|
226
|
+
return fingerprint == expected_fingerprint
|
227
|
+
end
|
228
|
+
|
229
|
+
def self._extract_embedded_certificate(xml_document)
|
230
|
+
parsed_document = LibXML::XML::Parser.string(xml_document).parse
|
231
|
+
base64_cert = parsed_document.find_first("//ds:X509Certificate", NAMESPACES).content
|
232
|
+
cert_text = Base64.decode64(base64_cert)
|
233
|
+
cert = OpenSSL::X509::Certificate.new(cert_text)
|
234
|
+
cert
|
235
|
+
end
|
236
|
+
|
237
|
+
def self._dump_doc(doc)
|
238
|
+
ptr = FFI::MemoryPointer.new(:pointer, 1)
|
239
|
+
sizeptr = FFI::MemoryPointer.new(:pointer, 1)
|
240
|
+
C::LibXML.xmlDocDumpFormatMemory(doc, ptr, sizeptr, 1)
|
241
|
+
strptr = ptr.read_pointer
|
242
|
+
result = strptr.null? ? nil : strptr.read_string
|
243
|
+
|
244
|
+
result
|
245
|
+
ensure
|
246
|
+
ptr.free if defined?(ptr) && ptr
|
247
|
+
sizeptr.free if defined?(sizeptr) && sizeptr
|
248
|
+
C::LibXML.xmlFree(strptr) if defined?(strptr) && strptr && !strptr.null?
|
249
|
+
end
|
250
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xml_security
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paul Hinze
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ffi
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: ruby-debug
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - '='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.3.2
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.3.2
|
62
|
+
description: See http://github.com/phinze/xml_security
|
63
|
+
email: paul.t.hinze@gmail.com
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- README.md
|
69
|
+
- lib/xml_security/c/lib_xml.rb
|
70
|
+
- lib/xml_security/c/xml_sec.rb
|
71
|
+
- lib/xml_security.rb
|
72
|
+
homepage: http://github.com/phinze/xml_security
|
73
|
+
licenses: []
|
74
|
+
post_install_message:
|
75
|
+
rdoc_options: []
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 1.8.23
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Ruby bindings into the XMLSec library using ffi.
|
96
|
+
test_files: []
|
97
|
+
has_rdoc:
|