xml_security 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/phinze/xml_security.png?branch=master)](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:
|