XMLCanonicalizer 0.1 → 1.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 +274 -31
- data/lib/xml/util/xmlcanonicalizer.rb +5 -1
- data/lib/xml/util/xmlcanonicalizer.rb~ +438 -0
- metadata +4 -3
data/README
CHANGED
@@ -1,61 +1,304 @@
|
|
1
|
-
==
|
1
|
+
== WSS4R
|
2
2
|
|
3
|
-
Project home: www.rubyforge.org/projects/
|
3
|
+
Project home: www.rubyforge.org/projects/wss4r
|
4
4
|
Author : Roland Schmitt, roland.schmitt@web.de
|
5
|
-
Date :
|
5
|
+
Date : 01.02.2006
|
6
6
|
|
7
|
-
Version : 0.
|
7
|
+
Version : 0.3
|
8
8
|
|
9
9
|
= Contents
|
10
10
|
1. What is it (and what is it not)
|
11
11
|
2. Requirements
|
12
12
|
3. Installation
|
13
13
|
4. Usage
|
14
|
+
4.1. Resolver
|
15
|
+
4.2. UsernameToken
|
16
|
+
4.3. Encryption
|
17
|
+
4.4. Signature
|
18
|
+
4.5. Signature/Encryption
|
19
|
+
4.6. Interoperability with WSE 2.0
|
20
|
+
4.7. Interoperability with JWSDP 2.0
|
21
|
+
4.8. Integration into rails
|
14
22
|
5. Samples
|
23
|
+
5.1. A simple example
|
24
|
+
5.2. Rails example
|
15
25
|
6. License
|
16
26
|
7. Support
|
17
27
|
8. URLs
|
18
28
|
|
29
|
+
|
30
|
+
|
19
31
|
= 1. What is it (and what is it not)
|
20
|
-
|
21
|
-
|
22
|
-
|
32
|
+
WSS4R (Web Service Security For Ruby) is a library that implements some of the
|
33
|
+
standards for web service security defined by the oasis open consortium [1].
|
34
|
+
It is a proof of concept (or "Can i do it with ruby?") and not an fully featured
|
35
|
+
implementation, so it's focussed on the encryption and signature aspects of
|
36
|
+
web services. WSS4R sits on top of the famous soap4r library of NAKAMURA Hiroshi [2],
|
37
|
+
so implementing servers or clients that uses data encryption or signatures is easy
|
38
|
+
when you know how to use soap4r.
|
39
|
+
Besides that there is a integration into the rails [3] application server, so you
|
40
|
+
can use the features of WSS4R in rails driven web services.
|
23
41
|
|
24
|
-
|
42
|
+
I've tested WSS4R with the following counterparts:
|
43
|
+
- Web Service Enhancements 2.0 [8]
|
44
|
+
- Java Web Service Development Pack 2.0 [9]
|
45
|
+
|
25
46
|
|
47
|
+
WSS4R is my first ruby project, so it is not very ruby like in most places nor is it
|
48
|
+
a example of good ruby programming in general. It was started mostly to learn ruby,
|
49
|
+
not to implement an full featured WS-Security conform library.
|
50
|
+
|
51
|
+
If you like it or hate it or want more features, drop me an email at Roland.Schmitt@web.de.
|
52
|
+
|
53
|
+
|
26
54
|
= 2. Requirements
|
27
|
-
|
55
|
+
- Ruby [4] with compiled bindings to openssl [6]
|
56
|
+
(tested only with Ruby 1.8.4)
|
57
|
+
- soap4r [2]
|
58
|
+
(tested with soap4r version from 04. Dez. 2005),
|
59
|
+
- Log4r [5]
|
60
|
+
(tested with version 1.0.5)
|
61
|
+
|
62
|
+
Optional (for the examples):
|
63
|
+
- Rails [3]
|
64
|
+
(tested with version 1.0.0)
|
65
|
+
- Sqlite3-Ruby [7]
|
66
|
+
(tested with version 1.1.0)
|
67
|
+
|
68
|
+
|
28
69
|
|
29
70
|
= 3. Installation
|
30
|
-
Simly do
|
31
|
-
gem install --local XMLCanonicalizer-0.1.gem
|
71
|
+
Simly do
|
32
72
|
|
73
|
+
ruby setup.rb
|
74
|
+
|
75
|
+
|
33
76
|
= 4. Usage
|
34
|
-
require
|
35
|
-
|
36
|
-
|
77
|
+
To use WSS4R, one have to require the new driver class
|
78
|
+
|
79
|
+
require "wss4r/rpc/driver"
|
80
|
+
|
81
|
+
instead of
|
82
|
+
require "soap/rpc/driver"
|
83
|
+
|
84
|
+
|
85
|
+
= 4.1. Resolver
|
86
|
+
Resolver objects are used to find certificates, keys and to authenticate users.
|
87
|
+
Resolver objects know how to get the corresponding private key for a certificate or how to load
|
88
|
+
a certificate identified by a name.
|
89
|
+
For example, when a client sends a encrypted request to the server, there is only the certificate
|
90
|
+
embedded in the message. A resolver object is responsible for loading the private key of the
|
91
|
+
certificate.
|
92
|
+
|
93
|
+
There two implementations of resolvers:
|
94
|
+
- CertificateDirectoryResolver
|
95
|
+
Loads keys/certificates from files in a specified directory in the file system.
|
96
|
+
|
97
|
+
- DatabaseResolver
|
98
|
+
Loads keys/certificates from sqlite3 databases.
|
99
|
+
|
100
|
+
|
101
|
+
= 4.2. UsernameToken
|
102
|
+
Example:
|
103
|
+
|
104
|
+
...
|
105
|
+
driver = Driver.new('http://localhost:8070/','urn:multiply')
|
106
|
+
driver.add_method('multiply','a','b')
|
107
|
+
...
|
108
|
+
usernametoken = UsernameToken.new("username", "password")
|
109
|
+
driver.security().add_security_token(usernametoken)
|
110
|
+
...
|
111
|
+
|
112
|
+
The client is authenticated with his username and password. The password is not sended as clear
|
113
|
+
text, instead a hash function is used.
|
114
|
+
|
115
|
+
|
116
|
+
= 4.3. Encryption
|
117
|
+
Example:
|
118
|
+
|
119
|
+
...
|
120
|
+
resolver = CertificateDirectoryResolver.new("../certs/ca")
|
121
|
+
certificate = resolver.certificate_by_subject("/C=DE/ST=Rheinland-Pfalz/L=Trier/O=FF/OU=Development/CN=Server/emailAddress=server@web.de")
|
122
|
+
x509 = X509SecurityToken.new(certificate)
|
123
|
+
enc_data = EncryptedData.new(x509)
|
124
|
+
driver.security().add_security_token(enc_data)
|
125
|
+
...
|
126
|
+
|
127
|
+
The soap body is encrypted with the TripleDES algorithm. The used encryption key is then
|
128
|
+
encrypted with the certificates public key and placed in the soap header.
|
129
|
+
|
130
|
+
Actually only TripleDES and AES are supported by WSS4R as encryption algorithms. To use AES instead of the default
|
131
|
+
TripleDES, set:
|
132
|
+
enc_data.sessionkey_algorithm=(Types::ALGORITHM_AES_CBC)
|
133
|
+
|
134
|
+
|
135
|
+
= 4.4. Signature
|
136
|
+
Example:
|
137
|
+
|
138
|
+
...
|
139
|
+
sign_cert = OpenSSL::X509::Certificate.new(File.read("../certs/wse/wse-client.cer"))
|
140
|
+
pkey = OpenSSL::PKey::RSA.new(File.read("../certs/wse/wse-client.key"))
|
141
|
+
x509 = X509SecurityToken.new(sign_cert, pkey)
|
142
|
+
signature = Signature.new(x509)
|
143
|
+
driver.security().add_security_token(signature)
|
144
|
+
...
|
145
|
+
|
146
|
+
The soap body and the soap header timestamp elements are signed with the supplied certificate. The signature
|
147
|
+
is embedded in the soap header.
|
148
|
+
|
149
|
+
|
150
|
+
= 4.5. Signature/Encryption
|
151
|
+
Example:
|
152
|
+
|
153
|
+
...
|
154
|
+
resolver = CertificateDirectoryResolver.new("../certs/wse")
|
155
|
+
driver.security().add_security_resolver(resolver)
|
156
|
+
sign_cert = OpenSSL::X509::Certificate.new(File.read("../certs/wse/wse-client.cer"))
|
157
|
+
pkey = OpenSSL::PKey::RSA.new(File.read("../certs/wse/wse-client.key"))
|
158
|
+
x509 = X509SecurityToken.new(sign_cert, pkey)
|
159
|
+
signature = Signature.new(x509)
|
160
|
+
driver.security().add_security_token(signature)
|
161
|
+
encrypt_cert = OpenSSL::X509::Certificate.new(File.read("../certs/wse/wse-server.cer"))
|
162
|
+
x509 = X509SecurityToken.new(encrypt_cert)
|
163
|
+
enc_data = EncryptedData.new(x509)
|
164
|
+
enc_data.sessionkey_algorithm=(Types::ALGORITHM_AES_CBC)
|
165
|
+
driver.security().add_security_token(enc_data)
|
166
|
+
...
|
167
|
+
|
168
|
+
You can combine signature and encryption. In most cases, for encryption the clients certificate is used and
|
169
|
+
for signature the certificate of the server.
|
170
|
+
If you first apply the encryption, the signature is generated over the encrypted data. When signing is the first step, the
|
171
|
+
the signature is generated from the plain content. So it is important to know which token is first added to the driver object.
|
172
|
+
|
173
|
+
|
174
|
+
= 4.6. Interoperability with WSE 2.0
|
175
|
+
Should work with usernames, encryption and signatures.
|
176
|
+
Sample Visual Studio projects are provided in examples/WebServiceTest (client) and examples/WebService (server).
|
177
|
+
|
178
|
+
|
179
|
+
= 4.7. Interoperability with JWSDP 2.0
|
180
|
+
Should work with usernames, encryption and signatures.
|
181
|
+
Per default the examples the JWSDP works with keyIdendifiers for finding certificates and keys.
|
182
|
+
WSS4R does not support the keyIdentifier mechanism to identify certificates, because it is not a standard.
|
183
|
+
|
184
|
+
|
185
|
+
= 4.8. Integration into rails
|
186
|
+
To use WSS4R in rails one have to modify the controller that implements the web service:
|
187
|
+
|
188
|
+
require "wss4r/aws/utils"
|
189
|
+
require "activerecordresolver"
|
190
|
+
|
191
|
+
class SimpleServiceController < ApplicationController
|
192
|
+
wsdl_service_name 'SimpleService'
|
193
|
+
web_service_scaffold :invoke
|
194
|
+
web_service_api SimpleServiceApi
|
195
|
+
|
196
|
+
wss_add_resolvers([ActiveRecordResolver.new()])
|
197
|
+
|
198
|
+
|
199
|
+
I've only tested the direct dispatching mode of action web service with WSS4R.
|
200
|
+
|
37
201
|
|
38
202
|
= 5. Examples
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
203
|
+
All examples are placed in the examples subfolder.
|
204
|
+
|
205
|
+
certs Folder with various certificates and keys for the different examples
|
206
|
+
|-- ca Generated certificates and keys for the WSS4R examples
|
207
|
+
|-- jwsdp_15 Certificates and keys for to use with the JWSDP 1.5
|
208
|
+
|-- jwsdp_16 Certificates and keys for to use with the JWSDP 1.6/2.0
|
209
|
+
|-- wse Certificates and keys for to use with the WSE 2.0
|
210
|
+
|
211
|
+
clients
|
212
|
+
PlainNET.rb Example for using a .NET web service without web service security features.
|
213
|
+
PlainXWS.rb Example for using a JWSDP web service without web service security features.
|
214
|
+
TestNET.rb Example for using a WSE 2.0 web service. Shows usenametoken, encryption and signature.
|
215
|
+
Client for the project in the WebService folder.
|
216
|
+
TestXWS.rb Example for using a JWSDP 2.0 web service. Shows usenametoken, encryption and signature.
|
217
|
+
Client for the examples bundled with the JWSDP 2.0 package. The service is found in
|
218
|
+
%JWSDP_HOME%/xws-security/samples/simple. To work, set the the keyReferenceType in the
|
219
|
+
JWSDP xml config files under config/ to "Direct".
|
220
|
+
TestWSS4R.rb Example for using a WSS4R/soap4r web service. Shows usenametoken, encryption and signature.
|
221
|
+
Used with the server in the server folder.
|
222
|
+
|
223
|
+
rails
|
224
|
+
|-- simple Example for rails integration. Requires sqlite3-ruby.
|
225
|
+
|-- client Test client for the rails web service.
|
226
|
+
|-- app Files for the web service
|
227
|
+
|-- controllers Implementration of the web service and setup for the resolver, encryption and signing.
|
228
|
+
|-- helpers Resolver implementation that uses ActiveRecord to load certificates and keys.
|
229
|
+
|-- databases Sqlite3 database with certificates and keys.
|
230
|
+
|
231
|
+
server
|
232
|
+
|-- TestServer.rb WebRick server using soap4r and WSS4R.
|
233
|
+
|
234
|
+
WebService Visual Studio project for a WSE 2.0 enabled web service. Created with Visual Studio 2003 and
|
235
|
+
C#.
|
236
|
+
|
237
|
+
WebServiceTest Visual Studio project with clients for the WebService project and for the rails example. Created with
|
238
|
+
Visual Studio 2003 and C#.
|
239
|
+
|
240
|
+
|
241
|
+
= 5.1. A simple example
|
242
|
+
At the command line, go to examples/server and type:
|
243
|
+
|
244
|
+
ruby TestServer.rb user
|
245
|
+
|
246
|
+
A WebRick server starts that requires a username/password from the client, where the password is the username reversed.
|
247
|
+
In another shell, change the working dir to examples/clients and type:
|
248
|
+
|
249
|
+
ruby TestWSS4R.rb user
|
250
|
+
|
251
|
+
It show the result of the multiplication of the 2 arguments. The client uses "Ron" as username and "noR" as password. Setting
|
252
|
+
the password to other values than "noR" in the TestWSS4R.rb file results in a "User not authenticatd!" message.
|
253
|
+
|
254
|
+
Stop the TestServer with CTRL-Z (or CTRL-C). Restart it with
|
255
|
+
|
256
|
+
ruby TestServer.rb enc sign
|
257
|
+
|
258
|
+
The server now will first encrypt any request, then signing it.
|
51
259
|
|
260
|
+
Start the client by typing:
|
261
|
+
|
262
|
+
ruby TestClient.rb sign enc
|
263
|
+
|
264
|
+
The client first signs the request, then encrypts it and shows the response.
|
265
|
+
|
266
|
+
You can use various monitoring tools like the tcpmon utility from the axis project [10] to view the
|
267
|
+
resulting request/response messages.
|
268
|
+
|
269
|
+
= 5.2. Rails example
|
270
|
+
Go to examples/rails/simple and type
|
271
|
+
|
272
|
+
ruby script/server
|
273
|
+
|
274
|
+
To start the client, open a new shell and go to examples/rails/simple/client.
|
275
|
+
|
276
|
+
ruby client.rb
|
277
|
+
|
278
|
+
The output shows the reversed string that the client sents to the server. The client signs his request and the server encrypts
|
279
|
+
and signs the response.
|
280
|
+
|
281
|
+
|
52
282
|
= 6. License
|
53
|
-
|
283
|
+
|
284
|
+
WSS4R is licensed under GPL and Ruby's custom license. See GPL and RUBYS.
|
285
|
+
|
54
286
|
|
55
287
|
= 7. Support
|
56
|
-
|
57
|
-
|
288
|
+
|
289
|
+
The RubyForge mailing list is at www.rubyforge.org/projects/wss4r.
|
290
|
+
|
291
|
+
Or, to contact the author, send mail to Roland.Schmitt@web.de
|
292
|
+
|
58
293
|
|
59
294
|
= 8. URLs
|
60
|
-
[1] - http://www.
|
61
|
-
[2] - http://
|
295
|
+
[1] - http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=wss
|
296
|
+
[2] - http://dev.ctor.org/soap4r/
|
297
|
+
[3] - http://www.rubyonrails.org
|
298
|
+
[4] - http://www.ruby-lang.org
|
299
|
+
[5] - http://log4r.sourceforge.net/
|
300
|
+
[6] - http://www.openssl.org
|
301
|
+
[7] - http://rubyforge.org/projects/sqlite-ruby/
|
302
|
+
[8] - http://msdn.microsoft.com/webservices/webservices/building/wse/default.aspx
|
303
|
+
[9] - https://jwsdp.dev.java.net/
|
304
|
+
[10] - http://ws.apache.org/axis/
|
@@ -256,7 +256,11 @@ module XML
|
|
256
256
|
|
257
257
|
def write_attribute_axis(node)
|
258
258
|
list = Array.new()
|
259
|
-
|
259
|
+
#Sorting-Bug
|
260
|
+
#node.attributes().each_attribute{|attr|
|
261
|
+
# list.push(attr) if (!is_namespace_node(attr.value()) && !is_namespace_decl(attr)) # && is_node_visible(
|
262
|
+
#}
|
263
|
+
node.attributes().sort().each{|key,attr|
|
260
264
|
list.push(attr) if (!is_namespace_node(attr.value()) && !is_namespace_decl(attr)) # && is_node_visible(
|
261
265
|
}
|
262
266
|
if (!@exclusive && node.parent() != nil && node.parent().parent() != nil)
|
@@ -0,0 +1,438 @@
|
|
1
|
+
#require "rexml/document"
|
2
|
+
#require "base64"
|
3
|
+
#require "log4r"
|
4
|
+
|
5
|
+
#include REXML
|
6
|
+
#include Log4r
|
7
|
+
|
8
|
+
module XML
|
9
|
+
module Util
|
10
|
+
|
11
|
+
class REXML::Instruction
|
12
|
+
def write(writer, indent=-1, transitive=false, ie_hack=false)
|
13
|
+
indent(writer, indent)
|
14
|
+
writer << START.sub(/\\/u, '')
|
15
|
+
writer << @target
|
16
|
+
writer << ' '
|
17
|
+
writer << @content if @content != nil
|
18
|
+
writer << STOP.sub(/\\/u, '')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class REXML::Attribute
|
23
|
+
def <=>(a2)
|
24
|
+
if (self === a2)
|
25
|
+
return 0
|
26
|
+
elsif (self == nil)
|
27
|
+
return -1
|
28
|
+
elsif (a2 == nil)
|
29
|
+
return 1
|
30
|
+
elsif (self.prefix() == a2.prefix())
|
31
|
+
return self.name()<=>a2.name()
|
32
|
+
end
|
33
|
+
if (self.prefix() == nil)
|
34
|
+
return -1
|
35
|
+
elsif (a2.prefix() == nil)
|
36
|
+
return 1
|
37
|
+
end
|
38
|
+
ret = self.namespace()<=>a2.namespace()
|
39
|
+
if (ret == 0)
|
40
|
+
ret = self.prefix()<=>a2.prefix()
|
41
|
+
end
|
42
|
+
return ret
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class REXML::Element
|
47
|
+
def search_namespace(prefix)
|
48
|
+
if (self.namespace(prefix) == nil)
|
49
|
+
return (self.parent().search_namespace(prefix)) if (self.parent() != nil)
|
50
|
+
else
|
51
|
+
return self.namespace(prefix)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
def rendered=(rendered)
|
55
|
+
@rendered = rendered
|
56
|
+
end
|
57
|
+
def rendered?()
|
58
|
+
return @rendered
|
59
|
+
end
|
60
|
+
def node_namespaces()
|
61
|
+
ns = Array.new()
|
62
|
+
ns.push(self.prefix())
|
63
|
+
self.attributes().each_attribute{|a|
|
64
|
+
if (a.prefix() != nil)
|
65
|
+
ns.push(a.prefix())
|
66
|
+
end
|
67
|
+
if (a.prefix() == "" && a.local_name() == "xmlns")
|
68
|
+
ns.push("xmlns")
|
69
|
+
end
|
70
|
+
}
|
71
|
+
ns
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class NamespaceNode
|
76
|
+
attr_reader :prefix, :uri
|
77
|
+
def initialize(prefix, uri)
|
78
|
+
@prefix = prefix
|
79
|
+
@uri = uri
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class XmlCanonicalizer
|
84
|
+
attr_accessor :prefix_list, :logger
|
85
|
+
|
86
|
+
BEFORE_DOC_ELEMENT = 0
|
87
|
+
INSIDE_DOC_ELEMENT = 1
|
88
|
+
AFTER_DOC_ELEMENT = 2
|
89
|
+
|
90
|
+
NODE_TYPE_ATTRIBUTE = 3
|
91
|
+
NODE_TYPE_WHITESPACE = 4
|
92
|
+
NODE_TYPE_COMMENT = 5
|
93
|
+
NODE_TYPE_PI = 6
|
94
|
+
NODE_TYPE_TEXT = 7
|
95
|
+
|
96
|
+
|
97
|
+
def initialize(with_comments, excl_c14n)
|
98
|
+
@with_comments = with_comments
|
99
|
+
@exclusive = excl_c14n
|
100
|
+
@res = ""
|
101
|
+
@state = BEFORE_DOC_ELEMENT
|
102
|
+
@xnl = Array.new()
|
103
|
+
@prevVisibleNamespacesStart = 0
|
104
|
+
@prevVisibleNamespacesEnd = 0
|
105
|
+
@visibleNamespaces = Array.new()
|
106
|
+
@inclusive_namespaces = Array.new()
|
107
|
+
@prefix_list = nil
|
108
|
+
@rendered_prefixes = Array.new()
|
109
|
+
end
|
110
|
+
|
111
|
+
def add_inclusive_namespaces(prefix_list, element, visible_namespaces)
|
112
|
+
namespaces = element.attributes()
|
113
|
+
namespaces.each_attribute{|ns|
|
114
|
+
if (ns.prefix=="xmlns")
|
115
|
+
if (prefix_list.include?(ns.local_name()))
|
116
|
+
visible_namespaces.push(NamespaceNode.new("xmlns:"+ns.local_name(), ns.value()))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
}
|
120
|
+
parent = element.parent()
|
121
|
+
add_inclusive_namespaces(prefix_list, parent, visible_namespaces) if (parent)
|
122
|
+
visible_namespaces
|
123
|
+
end
|
124
|
+
|
125
|
+
def canonicalize(document)
|
126
|
+
write_document_node(document)
|
127
|
+
@res
|
128
|
+
end
|
129
|
+
|
130
|
+
def canonicalize_element(element, logging = true)
|
131
|
+
logging=(true) if logging
|
132
|
+
@logger.debug("Canonicalize element:\n " + element.to_s()) if @logger
|
133
|
+
@inclusive_namespaces = add_inclusive_namespaces(@prefix_list, element, @inclusive_namespaces) if (@prefix_list)
|
134
|
+
@preserve_document = element.document()
|
135
|
+
tmp_parent = element.parent()
|
136
|
+
body_string = remove_whitespace(element.to_s().gsub("\n","").gsub("\t","").gsub("\r",""))
|
137
|
+
document = Document.new(body_string)
|
138
|
+
tmp_parent.delete_element(element)
|
139
|
+
element = tmp_parent.add_element(document.root())
|
140
|
+
@preserve_element = element
|
141
|
+
document = Document.new(element.to_s())
|
142
|
+
ns = element.namespace(element.prefix())
|
143
|
+
document.root().add_namespace(element.prefix(), ns)
|
144
|
+
write_document_node(document)
|
145
|
+
@logger.debug("Canonicalized result:\n " + @res.to_s()) if @logger
|
146
|
+
@res
|
147
|
+
end
|
148
|
+
|
149
|
+
def write_document_node(document)
|
150
|
+
@state = BEFORE_DOC_ELEMENT
|
151
|
+
if (document.class().to_s() == "REXML::Element")
|
152
|
+
write_node(document)
|
153
|
+
else
|
154
|
+
document.each_child{|child|
|
155
|
+
write_node(child)
|
156
|
+
}
|
157
|
+
end
|
158
|
+
@res
|
159
|
+
end
|
160
|
+
|
161
|
+
def write_node(node)
|
162
|
+
visible = is_node_visible(node)
|
163
|
+
if ((node.node_type() == :text) && white_text?(node.value()))
|
164
|
+
res = node.value()
|
165
|
+
res.gsub("\r\n","\n")
|
166
|
+
#res = res.delete(" ").delete("\t")
|
167
|
+
res.delete("\r")
|
168
|
+
@res = @res + res
|
169
|
+
#write_text_node(node,visible) if (@state == INSIDE_DOC_ELEMENT)
|
170
|
+
return
|
171
|
+
end
|
172
|
+
if (node.node_type() == :text)
|
173
|
+
write_text_node(node, visible)
|
174
|
+
return
|
175
|
+
end
|
176
|
+
if (node.node_type() == :element)
|
177
|
+
write_element_node(node, visible) if (!node.rendered?())
|
178
|
+
node.rendered=(true)
|
179
|
+
end
|
180
|
+
if (node.node_type() == :processing_instruction)
|
181
|
+
end
|
182
|
+
if (node.node_type() == :comment)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def write_element_node(node, visible)
|
187
|
+
savedPrevVisibleNamespacesStart = @prevVisibleNamespacesStart
|
188
|
+
savedPrevVisibleNamespacesEnd = @prevVisibleNamespacesEnd
|
189
|
+
savedVisibleNamespacesSize = @visibleNamespaces.size()
|
190
|
+
state = @state
|
191
|
+
state = INSIDE_DOC_ELEMENT if (visible && state == BEFORE_DOC_ELEMENT)
|
192
|
+
@res = @res + "<" + node.expanded_name() if (visible)
|
193
|
+
write_namespace_axis(node, visible)
|
194
|
+
write_attribute_axis(node)
|
195
|
+
@res = @res + ">" if (visible)
|
196
|
+
node.each_child{|child|
|
197
|
+
write_node(child)
|
198
|
+
}
|
199
|
+
@res = @res + "</" +node.expanded_name() + ">" if (visible)
|
200
|
+
@state = AFTER_DOC_ELEMENT if (visible && state == BEFORE_DOC_ELEMENT)
|
201
|
+
@prevVisibleNamespacesStart = savedPrevVisibleNamespacesStart
|
202
|
+
@prevVisibleNamespacesEnd = savedPrevVisibleNamespacesEnd
|
203
|
+
@visibleNamespaces.slice!(savedVisibleNamespacesSize, @visibleNamespaces.size() - savedVisibleNamespacesSize) if (@visibleNamespaces.size() > savedVisibleNamespacesSize)
|
204
|
+
end
|
205
|
+
|
206
|
+
def write_namespace_axis(node, visible)
|
207
|
+
doc = node.document()
|
208
|
+
has_empty_namespace = false
|
209
|
+
list = Array.new()
|
210
|
+
cur = node
|
211
|
+
#while ((cur != nil) && (cur != doc) && (cur.node_type() != :document))
|
212
|
+
namespaces = cur.node_namespaces()
|
213
|
+
namespaces.each{|prefix|
|
214
|
+
next if ((prefix == "xmlns") && (node.namespace(prefix) == ""))
|
215
|
+
namespace = cur.namespace(prefix)
|
216
|
+
next if (is_namespace_node(namespace))
|
217
|
+
next if (node.namespace(prefix) != cur.namespace(prefix))
|
218
|
+
next if (prefix == "xml" && namespace == "http://www.w3.org/XML/1998/namespace")
|
219
|
+
next if (!is_node_visible(cur))
|
220
|
+
rendered = is_namespace_rendered(prefix, namespace)
|
221
|
+
@visibleNamespaces.push(NamespaceNode.new("xmlns:"+prefix,namespace)) if (visible)
|
222
|
+
if ((!rendered) && !list.include?(prefix))
|
223
|
+
list.push(prefix)
|
224
|
+
end
|
225
|
+
has_empty_namespace = true if (prefix == nil)
|
226
|
+
}
|
227
|
+
if (visible && !has_empty_namespace && !is_namespace_rendered(nil, nil))
|
228
|
+
@res = @res + ' xmlns=""'
|
229
|
+
end
|
230
|
+
#TODO: ns of inclusive_list
|
231
|
+
#=begin
|
232
|
+
if ((@prefix_list) && (node.to_s() == node.parent().to_s()))
|
233
|
+
#list.push(node.prefix())
|
234
|
+
@inclusive_namespaces.each{|ns|
|
235
|
+
prefix = ns.prefix().split(":")[1]
|
236
|
+
list.push(prefix) if (!list.include?(prefix) && (!node.attributes.prefixes.include?(prefix)))
|
237
|
+
}
|
238
|
+
@prefix_list = nil
|
239
|
+
end
|
240
|
+
#=end
|
241
|
+
list.sort!()
|
242
|
+
list.each{|prefix|
|
243
|
+
next if (prefix == "")
|
244
|
+
next if (@rendered_prefixes.include?(prefix))
|
245
|
+
@rendered_prefixes.push(prefix)
|
246
|
+
ns = node.namespace(prefix)
|
247
|
+
ns = @preserve_element.namespace(prefix) if (ns == nil)
|
248
|
+
@res = @res + normalize_string(" " + prefix + '="' + ns + '"', NODE_TYPE_TEXT) if (prefix == "xmlns")
|
249
|
+
@res = @res + normalize_string(" xmlns:" + prefix + '="' + ns + '"', NODE_TYPE_TEXT) if (prefix != nil && prefix != "xmlns")
|
250
|
+
}
|
251
|
+
if (visible)
|
252
|
+
@prevVisibleNamespacesStart = @prevVisibleNamespacesEnd
|
253
|
+
@prevVisibleNamespacesEnd = @visibleNamespaces.size()
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def write_attribute_axis(node)
|
258
|
+
list = Array.new()
|
259
|
+
node.attributes().each_attribute{|attr|
|
260
|
+
list.push(attr) if (!is_namespace_node(attr.value()) && !is_namespace_decl(attr)) # && is_node_visible(
|
261
|
+
}
|
262
|
+
if (!@exclusive && node.parent() != nil && node.parent().parent() != nil)
|
263
|
+
cur = node.parent()
|
264
|
+
while (cur != nil)
|
265
|
+
#next if (cur.attributes() == nil)
|
266
|
+
cur.each_attribute{|attribute|
|
267
|
+
next if (attribute.prefix() != "xml")
|
268
|
+
next if (attribute.prefix().index("xmlns") == 0)
|
269
|
+
next if (node.namespace(attribute.prefix()) == attribute.value())
|
270
|
+
found = true
|
271
|
+
list.each{|n|
|
272
|
+
if (n.prefix() == "xml" && n.value() == attritbute.value())
|
273
|
+
found = true
|
274
|
+
break
|
275
|
+
end
|
276
|
+
}
|
277
|
+
next if (found)
|
278
|
+
list.push(attribute)
|
279
|
+
}
|
280
|
+
end
|
281
|
+
end
|
282
|
+
list.each{|attribute|
|
283
|
+
if (attribute != nil)
|
284
|
+
if (attribute.name() != "xmlns")
|
285
|
+
@res = @res + " " + normalize_string(attribute.to_string(), NODE_TYPE_ATTRIBUTE).gsub("'",'"')
|
286
|
+
end
|
287
|
+
# else
|
288
|
+
# @res = @res + " " + normalize_string(attribute.name()+'="'+attribute.to_s()+'"', NODE_TYPE_ATTRIBUTE).gsub("'",'"')
|
289
|
+
#end
|
290
|
+
end
|
291
|
+
}
|
292
|
+
end
|
293
|
+
|
294
|
+
def is_namespace_node(namespace_uri)
|
295
|
+
return (namespace_uri == "http://www.w3.org/2000/xmlns/")
|
296
|
+
end
|
297
|
+
|
298
|
+
def is_namespace_rendered(prefix, uri)
|
299
|
+
is_empty_ns = prefix == nil && uri == nil
|
300
|
+
if (is_empty_ns)
|
301
|
+
start = 0
|
302
|
+
else
|
303
|
+
start = @prevVisibleNamespacesStart
|
304
|
+
end
|
305
|
+
@visibleNamespaces.each{|ns|
|
306
|
+
if (ns.prefix() == "xmlns:"+prefix.to_s() && ns.uri() == uri)
|
307
|
+
return true
|
308
|
+
end
|
309
|
+
}
|
310
|
+
return is_empty_ns
|
311
|
+
#(@visibleNamespaces.size()-1).downto(start) {|i|
|
312
|
+
# ns = @visibleNamespaces[i]
|
313
|
+
# return true if (ns.prefix() == "xmlns:"+prefix.to_s() && ns.uri() == uri)
|
314
|
+
# #p = ns.prefix() if (ns.prefix().index("xmlns") == 0)
|
315
|
+
# #return ns.uri() == uri if (p == prefix)
|
316
|
+
#}
|
317
|
+
#return is_empty_ns
|
318
|
+
end
|
319
|
+
|
320
|
+
def is_node_visible(node)
|
321
|
+
return true if (@xnl.size() == 0)
|
322
|
+
@xnl.each{|element|
|
323
|
+
return true if (element == node)
|
324
|
+
}
|
325
|
+
return false
|
326
|
+
end
|
327
|
+
|
328
|
+
def normalize_string(input, type)
|
329
|
+
sb = ""
|
330
|
+
return input
|
331
|
+
end
|
332
|
+
#input.each_byte{|b|
|
333
|
+
# if (b ==60 && (type == NODE_TYPE_ATTRIBUTE || is_text_node(type)))
|
334
|
+
# sb = sb + "<"
|
335
|
+
# elsif (b == 62 && is_text_node(type))
|
336
|
+
# sb = sb + ">"
|
337
|
+
# elsif (b == 38 && (is_text_node(type) || is_text_node(type))) #Ampersand
|
338
|
+
# sb = sb + "&"
|
339
|
+
# elsif (b == 34 && is_text_node(type)) #Quote
|
340
|
+
# sb = sb + """
|
341
|
+
# elsif (b == 9 && is_text_node(type)) #Tabulator
|
342
|
+
# sb = sb + "	"
|
343
|
+
# elsif (b == 11 && is_text_node(type)) #CR
|
344
|
+
# sb = sb + "
"
|
345
|
+
# elsif (b == 13 && (type == NODE_TYPE_ATTRIBUTE || (is_text_node(type) && type != NODE_TYPE_WHITESPACE) || type == NODE_TYPE_COMMENT || type == NODE_TYPE_PI))
|
346
|
+
# sb = sb + "
"
|
347
|
+
# elsif (b == 13)
|
348
|
+
# next
|
349
|
+
# else
|
350
|
+
# sb = sb.concat(b)
|
351
|
+
# end
|
352
|
+
#}
|
353
|
+
#sb
|
354
|
+
#end
|
355
|
+
|
356
|
+
def write_text_node(node, visible)
|
357
|
+
if (visible)
|
358
|
+
@res = @res + normalize_string(node.value(), node.node_type())
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def white_text?(text)
|
363
|
+
return true if ((text.strip() == "") || (text.strip() == nil))
|
364
|
+
return false
|
365
|
+
end
|
366
|
+
|
367
|
+
def is_namespace_decl(attribute)
|
368
|
+
#return true if (attribute.name() == "xmlns")
|
369
|
+
return true if (attribute.prefix().index("xmlns") == 0)
|
370
|
+
return false
|
371
|
+
end
|
372
|
+
|
373
|
+
def is_text_node(type)
|
374
|
+
return true if (type == NODE_TYPE_TEXT || type == NODE_TYPE_CDATA || type == NODE_TYPE_WHITESPACE)
|
375
|
+
return false
|
376
|
+
end
|
377
|
+
|
378
|
+
def remove_whitespace(string)
|
379
|
+
new_string = ""
|
380
|
+
in_white = false
|
381
|
+
string.each_byte{|b|
|
382
|
+
#if (in_white && b == 32)
|
383
|
+
#else
|
384
|
+
if !(in_white && b == 32)
|
385
|
+
new_string = new_string + b.chr()
|
386
|
+
end
|
387
|
+
if (b == 62) #>
|
388
|
+
in_white = true
|
389
|
+
end
|
390
|
+
if (b == 60) #<
|
391
|
+
in_white = false
|
392
|
+
end
|
393
|
+
}
|
394
|
+
new_string
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def logging=(state)
|
399
|
+
if (state)
|
400
|
+
@logger = Logger.new("xmlcanonicalizer")
|
401
|
+
@logger.level = DEBUG
|
402
|
+
@logger.trace = false
|
403
|
+
p = PatternFormatter.new(:pattern => "[%l] %d :: %.100m %15t")
|
404
|
+
@logger.add(FileOutputter.new("wss4r", {:filename => "xmlcanonicalizer.log", :formatter => BasicFormatter}))
|
405
|
+
else
|
406
|
+
@logger = nil
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end #Util
|
410
|
+
end #XML
|
411
|
+
|
412
|
+
|
413
|
+
if __FILE__ == $0
|
414
|
+
document = Document.new(File.new(ARGV[0]))
|
415
|
+
body = nil
|
416
|
+
c = WSS4R::Security::Util::XmlCanonicalizer.new(false, true)
|
417
|
+
|
418
|
+
if (ARGV.size() == 3)
|
419
|
+
body = ARGV[2]
|
420
|
+
if (body == "true")
|
421
|
+
element = XPath.match(document, "/soap:Envelope/soap:Body")[0]
|
422
|
+
element = XPath.first(document, "/soap:Envelope/soap:Header/wsse:Security/Signature/SignedInfo")
|
423
|
+
result = c.canonicalize_element(element)
|
424
|
+
puts("-----")
|
425
|
+
puts(result)
|
426
|
+
puts("-----")
|
427
|
+
puts(result.size())
|
428
|
+
puts("-----")
|
429
|
+
puts(CryptHash.new().digest_b64(result))
|
430
|
+
end
|
431
|
+
else
|
432
|
+
result = c.canonicalize(document)
|
433
|
+
end
|
434
|
+
|
435
|
+
file = File.new(ARGV[1], "wb")
|
436
|
+
file.write(result)
|
437
|
+
file.close()
|
438
|
+
end
|
metadata
CHANGED
@@ -3,13 +3,13 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: XMLCanonicalizer
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version:
|
7
|
-
date: 2007-05-
|
6
|
+
version: 1.0.1
|
7
|
+
date: 2007-05-29 00:00:00 +02:00
|
8
8
|
summary: Implementation of w3c xml canonicalizer standart.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: Roland.Schmitt@web.de
|
12
|
-
homepage: http://www.rubyforge.org/projects/
|
12
|
+
homepage: http://www.rubyforge.org/projects/xmlcanonicalizer
|
13
13
|
rubyforge_project:
|
14
14
|
description:
|
15
15
|
autorequire: xmlanonicalizer
|
@@ -33,6 +33,7 @@ files:
|
|
33
33
|
- lib/xmlcanonicalizer.rb
|
34
34
|
- lib/xml/util
|
35
35
|
- lib/xml/util/xmlcanonicalizer.rb
|
36
|
+
- lib/xml/util/xmlcanonicalizer.rb~
|
36
37
|
- README
|
37
38
|
test_files: []
|
38
39
|
|