wss4r 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/README +300 -0
  2. data/lib/wss4r/aws/utils.rb +37 -0
  3. data/lib/wss4r/config/config.rb +105 -0
  4. data/lib/wss4r/rpc/proxy.rb +26 -0
  5. data/lib/wss4r/rpc/router.rb +46 -0
  6. data/lib/wss4r/rpc/wssdriver.rb +19 -0
  7. data/lib/wss4r/security/crypto/certificate.rb +21 -0
  8. data/lib/wss4r/security/crypto/cipher.rb +161 -0
  9. data/lib/wss4r/security/crypto/hash.rb +35 -0
  10. data/lib/wss4r/security/exceptions/exceptions.rb +62 -0
  11. data/lib/wss4r/security/resolver.rb +23 -0
  12. data/lib/wss4r/security/security.rb +148 -0
  13. data/lib/wss4r/security/util/hash_util.rb +39 -0
  14. data/lib/wss4r/security/util/names.rb +38 -0
  15. data/lib/wss4r/security/util/namespaces.rb +21 -0
  16. data/lib/wss4r/security/util/reference_elements.rb +15 -0
  17. data/lib/wss4r/security/util/soap_parser.rb +73 -0
  18. data/lib/wss4r/security/util/transformer_factory.rb +29 -0
  19. data/lib/wss4r/security/util/types.rb +25 -0
  20. data/lib/wss4r/security/util/xmlcanonicalizer.rb +427 -0
  21. data/lib/wss4r/security/util/xmlutils.rb +58 -0
  22. data/lib/wss4r/security/xml/encrypted_data.rb +110 -0
  23. data/lib/wss4r/security/xml/encrypted_key.rb +74 -0
  24. data/lib/wss4r/security/xml/key_info.rb +52 -0
  25. data/lib/wss4r/security/xml/reference.rb +53 -0
  26. data/lib/wss4r/security/xml/reference_list.rb +24 -0
  27. data/lib/wss4r/security/xml/security.rb +92 -0
  28. data/lib/wss4r/security/xml/signature.rb +69 -0
  29. data/lib/wss4r/security/xml/signature_value.rb +26 -0
  30. data/lib/wss4r/security/xml/signed_info.rb +83 -0
  31. data/lib/wss4r/security/xml/timestamp.rb +47 -0
  32. data/lib/wss4r/security/xml/tokentypes.rb +180 -0
  33. data/lib/wss4r/server/wssstandaloneserver.rb +27 -0
  34. data/lib/wss4r/soap/processor.rb +92 -0
  35. data/lib/wss4r/tokenresolver/authenticateuserresolver.rb +34 -0
  36. data/lib/wss4r/tokenresolver/certificateresolver.rb +62 -0
  37. data/lib/wss4r/tokenresolver/databaseresolver.rb +56 -0
  38. data/lib/wss4r/tokenresolver/resolver.rb +13 -0
  39. metadata +95 -0
@@ -0,0 +1,69 @@
1
+ module WSS4R
2
+ module Security
3
+ module Xml
4
+
5
+ class Signature
6
+ def initialize(security_token)
7
+ @security_token = security_token
8
+ end
9
+
10
+ def process(document)
11
+ security = Security.new()
12
+ security = security.process(document)
13
+ security_token = @security_token.process(document)
14
+ children = security.children()
15
+ #children.each{|child|
16
+ # security.delete(child)
17
+ #}
18
+ security.add_element(security_token)
19
+ signature_element = security.add_element(Names::SIGNATURE)
20
+ #children.each{|child|
21
+ # security.add_element(child)
22
+ #}
23
+ signature_element.add_namespace("xmlns:ds", Namespaces::DS)
24
+ signed_info = SignedInfo.new()
25
+ signed_info_element = signed_info.process(signature_element)
26
+ signature_value = SignatureValue.new(@security_token, signed_info_element)
27
+ signature_value.process(document)
28
+ key_info = KeyInfo.new(@security_token, KeyInfo::REFERENCE).get_xml(signature_element)
29
+ document
30
+ end
31
+
32
+ def unprocess(signature)
33
+ @signature_value = XPath.first(signature, "ds:SignatureValue", {"ds" => Namespaces::DS}).text().gsub("\n","")
34
+ key_info = XPath.first(signature, "ds:KeyInfo", {"ds" => Namespaces::DS})
35
+ @key_info = KeyInfo.new(key_info)
36
+ @signed_info = SignedInfo.new()
37
+ @signed_info.unprocess(signature.document())
38
+ @signature = signature
39
+ end
40
+
41
+ def verify_signature()
42
+ signed_info = XPath.first(@signature, "ds:SignedInfo", {"ds" => Namespaces::DS})
43
+ inclusive_namespaces = XPath.first(signed_info, "ds:CanonicalizationMethod/InclusiveNamespaces", {"ds" => Namespaces::DS})
44
+ prefix_list = inclusive_namespaces.attribute("PrefixList") if (inclusive_namespaces)
45
+ if (prefix_list)
46
+ prefix_list = prefix_list.value().split()
47
+ end
48
+ transformer = TransformerFactory::get_instance(@signed_info.canonicalizer_method())
49
+ transformer.prefix_list=(prefix_list)
50
+ result = transformer.canonicalize_element(signed_info)
51
+ signature_value = Base64.decode64(@signature_value)#.strip()
52
+ public_key = @key_info.security_token().certificate().public_key()
53
+ #TODO: check certificate
54
+ certificate = @key_info.security_token().certificate()
55
+
56
+ verify = public_key.verify(OpenSSL::Digest::SHA1.new(), signature_value, result)
57
+ raise FaultError.new(VerificationFault.new()) if !(verify)
58
+ certitificate = @key_info.security_token().certificate()
59
+ end
60
+
61
+ def verify()
62
+ @signed_info.verify()
63
+ verify_signature()
64
+ end
65
+ end
66
+
67
+ end #Xml
68
+ end #Security
69
+ end #WSS4R
@@ -0,0 +1,26 @@
1
+ module WSS4R
2
+ module Security
3
+ module Xml
4
+
5
+ class SignatureValue
6
+ def initialize(security_token, signed_info)
7
+ @security_token = security_token
8
+ @signed_info = signed_info
9
+ end
10
+
11
+ def process(document)
12
+ canonicalizer = TransformerFactory::get_instance("http://www.w3.org/2001/10/xml-exc-c14n#")
13
+ #esult = canonicalizer.write_document_node(@signed_info) #Broken
14
+ result = canonicalizer.canonicalize_element(@signed_info)
15
+ signature_value = @security_token.sign_b64(result)
16
+ @signed_info = XPath.first(document, "//ds:SignedInfo", {"ds" => Namespaces::DS})
17
+ signature_value_element = @signed_info.parent().add_element(Names::SIGNATURE_VALUE)
18
+ signature_value.strip!
19
+ signature_value_element.text=(signature_value)
20
+ @signed_info.document()
21
+ end
22
+ end
23
+
24
+ end #Xml
25
+ end #Security
26
+ end #WSS4R
@@ -0,0 +1,83 @@
1
+ module WSS4R
2
+ module Security
3
+ module Xml
4
+
5
+ class SignedInfo
6
+ attr_accessor :canonicalizer_method, :signature_method
7
+
8
+ def initialize()
9
+ @reference_elements = ReferenceElements.new()
10
+ end
11
+
12
+ def process(parent)
13
+ @par = parent
14
+ body = SOAPParser.part(SOAPParser::BODY)
15
+ body.add_namespace("xmlns:wsu", Namespaces::WSU)
16
+ signed_info_element = parent.add_element(Names::SIGNED_INFO)
17
+ canonicalization_element = signed_info_element.add_element(Names::CANONICALIZATION_METHOD)
18
+ canonicalization_element.add_attribute("Algorithm", Types::CANON_C14N_EXCL)
19
+ signature_method_element = signed_info_element.add_element(Names::SIGNATURE_METHOD)
20
+ signature_method_element.add_attribute("Algorithm", Types::SIG_ALG_RSA_SHA1)
21
+ @reference_elements.each{|xpath|
22
+ element = parent.document.select(xpath)
23
+ wsu_id = nil
24
+ element.attributes().each_attribute{|attr|
25
+ wsu_id = attr if (attr.name() == "Id") #attr.prefix() == "wsu" &&
26
+ }
27
+ if (wsu_id == nil)
28
+ wsu_id = REXML::Attribute.new("wsu:Id", element.object_id().to_s())
29
+ element.add_attribute(wsu_id)
30
+ end
31
+ canonicalizer = TransformerFactory::get_instance(nil)
32
+ c14n_element = canonicalizer.canonicalize_element(element)
33
+ reference_element = signed_info_element.add_element(Names::REFERENCE_DS)
34
+ reference_element.add_attribute("URI","#"+wsu_id.value())
35
+ transforms_element = reference_element.add_element(Names::TRANSFORMS)
36
+ transform_element = transforms_element.add_element(Names::TRANSFORM)
37
+ transform_element.add_attribute("Algorithm", Types::CANON_C14N_EXCL)
38
+ digest_method_element = reference_element.add_element(Names::DIGEST_METHOD)
39
+ digest_method_element.add_attribute("Algorithm", Types::DIG_METHOD_SHA1)
40
+ digest_value_element = reference_element.add_element(Names::DIGEST_VALUE)
41
+
42
+ sha = OpenSSL::Digest::SHA1.new(c14n_element)
43
+ #Bug from Tony Baines
44
+ digest_value_element.text=(Base64.encode64(sha.digest()))
45
+ }
46
+ signed_info_element
47
+ end
48
+
49
+ def unprocess(document)
50
+ @reference_list = Array.new()
51
+
52
+ signed_info = XPath.first(document, "//ds:SignedInfo", {"ds" => Namespaces::DS})
53
+
54
+ inclusive_namespaces = XPath.first(signed_info, "ds:CanonicalizationMethod/InclusiveNamespaces", {"ds" => Namespaces::DS})
55
+ prefix_list = inclusive_namespaces.attribute("PrefixList") if (inclusive_namespaces)
56
+ if (prefix_list)
57
+ prefix_list = prefix_list.value().split()
58
+ end
59
+
60
+ canonicalization_method = XPath.first(signed_info, Names::CANONICALIZATION_METHOD)
61
+ @canonicalization_method = canonicalization_method.attribute("Algorithm").value()
62
+
63
+ signature_method = XPath.first(signed_info, Names::SIGNATURE_METHOD)
64
+ @signature_method = signature_method.attribute("Algorithm")
65
+
66
+ signed_info.each_element("ds:Reference"){|reference|
67
+ @reference_list.push(Reference.new(reference, prefix_list))
68
+ }
69
+ end
70
+
71
+ def verify()
72
+ @reference_list.each{|reference|
73
+ if (reference.verify() == false)
74
+ fault = SOAP::SOAPFault.new(SOAP::SOAPString.new("wsse:FailedCheck"), SOAP::SOAPString.new("The signature or decryption was invalid."),SOAP::SOAPString.new(self.class().name())) #,"Error verifying reference: " + reference.uri())
75
+ raise SOAP::FaultError.new(fault)
76
+ end
77
+ }
78
+ end
79
+ end
80
+
81
+ end #Xml
82
+ end #Security
83
+ end #WSS4R
@@ -0,0 +1,47 @@
1
+ module WSS4R
2
+ module Security
3
+ module Xml
4
+
5
+ class Timestamp
6
+
7
+ def process(security)
8
+ timestamp = security.add_element(Names::TIMESTAMP)
9
+ timestamp.add_attribute("wsu:Id", timestamp.object_id().to_s())
10
+ created = timestamp.add_element(Names::CREATED)
11
+ expires = timestamp.add_element(Names::EXPIRES)
12
+
13
+
14
+ #BUG #4400-------------------------------
15
+ #created_time = Time.new().gmtime()
16
+ #expired_time = created_time+5*60 #
17
+ created_time = Time.new().getutc()
18
+ expired_time = created_time+(60*5)
19
+
20
+ created_time = created_time.iso8601()
21
+ expired_time = expired_time.iso8601()
22
+
23
+ created.text=(created_time.to_s())
24
+ expires.text=(expired_time.to_s())
25
+ security
26
+ end
27
+
28
+ def unprocess(timestamp)
29
+ created = XPath.first(timestamp, "wsu:Created", {"wsu"=>Namespaces::WSU}).text()
30
+ expires = XPath.first(timestamp, "wsu:Expires", {"wsu"=>Namespaces::WSU}).text()
31
+ created_parms = ParseDate::parsedate(created)
32
+ expires_parms = ParseDate::parsedate(expires)
33
+ @created_time = Time.gm(created_parms[0], created_parms[1],created_parms[2],created_parms[3], created_parms[4], created_parms[5])
34
+ @expires_time = Time.gm(expires_parms[0], expires_parms[1],expires_parms[2],expires_parms[3], expires_parms[4], expires_parms[5])
35
+ end
36
+
37
+ def verify()
38
+ time = Time.new().gmtime()
39
+ if !(@created_time <= time && time <= @expires_time)
40
+ raise WSS4R::Security::Exceptions::TimestampFault.new()
41
+ end
42
+ end
43
+ end
44
+
45
+ end #Xml
46
+ end #Security
47
+ end #WSS4R
@@ -0,0 +1,180 @@
1
+ module WSS4R
2
+ module Security
3
+ module Xml
4
+
5
+ class SecurityToken
6
+ def add_namespace(document, prefix, ns)
7
+ document.root().add_namespace("xmlns:"+prefix, ns)
8
+ end
9
+ end
10
+
11
+ class BinarySecurityToken < SecurityToken
12
+ end
13
+
14
+ class X509SecurityToken < BinarySecurityToken
15
+ attr_reader :certificate
16
+ attr_accessor :private_key
17
+
18
+ def initialize(x509certificate, private_key = nil)
19
+ if (x509certificate.kind_of?(Certificate))
20
+ @certificate = x509certificate
21
+ elsif x509certificate.instance_of?(String)
22
+ @certificate = Certificate.new(Base64.decode64(x509certificate))
23
+ end
24
+ @private_key = private_key
25
+ end
26
+
27
+ def process(document)
28
+ e = Element.new(Names::BINARY_SECURITY_TOKEN)
29
+ e.add_namespace("xmlns:wsu", Namespaces::WSU)
30
+ der_certificate_string = Base64.encode64(@certificate.to_der())
31
+ der_certificate_string.delete!("\n\r")
32
+
33
+ e.add_text(der_certificate_string)
34
+ e.add_attribute("wsu:Id", get_id())
35
+
36
+ e.add_attribute("ValueType", Types::REFERENCE_VALUETYPE_X509)
37
+ e.add_attribute("EncodingType", Types::ENCODING_X509V3)
38
+ return e
39
+ end
40
+
41
+ def get_id()
42
+ unless @id
43
+ @id = Crypto::CryptHash.new().digest_b64(@certificate.public_key().to_s()+Time.new().to_s()).to_s().strip()
44
+ end
45
+ @id
46
+ end
47
+
48
+ def key_identifier()
49
+ if (@key_identifier == nil)
50
+ ext = @certificate.extensions()[2]
51
+ return (Base64.encode64(ext.to_der()[11..30]))
52
+ else
53
+ return @key_identifier
54
+ end
55
+ end
56
+
57
+ def key_identifier=(id)
58
+ @key_identifier = id
59
+ end
60
+
61
+ def public_encrypt_b64(text)
62
+ ciphervalue = @certificate.public_key().public_encrypt(text)
63
+ return Base64.encode64(ciphervalue)
64
+ end
65
+
66
+ def private_decrypt_b64(text)
67
+ @private_key.private_decrypt(Base64.decode64(text.strip()))
68
+ end
69
+
70
+ def serial_number()
71
+ @certificate.serial()
72
+ end
73
+
74
+ def get_issuer_name()
75
+ @certificate.issuer()
76
+ end
77
+
78
+ def sign_b64(to_sign)
79
+ plain_signature = @private_key.sign(OpenSSL::Digest::SHA1.new(), to_sign)
80
+ signature = Base64.encode64(plain_signature)
81
+ signature.strip!
82
+ signature
83
+ end
84
+
85
+ def public_key()
86
+ return @certificate.public_key()
87
+ end
88
+ end
89
+
90
+ class UsernameToken < SecurityToken
91
+ PLAIN = "PLAIN"
92
+ HASHED = "HASHED"
93
+ attr_accessor :username, :password, :type, :nonce, :created, :hash, :type
94
+
95
+ def initialize(username = nil, password = nil, type = HASHED)
96
+ @username = username
97
+ @password = password
98
+ @type = type
99
+ end
100
+
101
+ def unprocess(usernametoken)
102
+ @username = XPath.first(usernametoken, "wsse:Username", {"wsse"=>Namespaces::WSSE}).text()
103
+ @password = XPath.first(usernametoken, "wsse:Password", {"wsse"=>Namespaces::WSSE}).text()
104
+ password_type = XPath.first(usernametoken, "wsse:Password", {"wsse"=>Namespaces::WSSE}).attribute("Type").value()
105
+ if password_type == Types::PASSWORD_DIGEST
106
+ @type = HASHED
107
+ @nonce = XPath.first(usernametoken, "wsse:Nonce", {"wsse"=>Namespaces::WSSE}).text()
108
+ @created = XPath.first(usernametoken, "wsu:Created", {"wsu"=>Namespaces::WSU}).text()
109
+ else
110
+ @type = PLAIN
111
+ end
112
+ @hash = @password
113
+ end
114
+
115
+ def process(document)
116
+ wsse_security = XPath.first(document, "/env:Envelope/env:Header/wsse:Security")
117
+ username_token = wsse_security.add_element("wsse:UsernameToken")
118
+ username_token.add_namespace("xmlns:wsu", Namespaces::WSU)
119
+ username_token.add_attribute("wsu:Id", "SecurityToken-" + username_token.object_id().to_s())
120
+ username = username_token.add_element("wsse:Username")
121
+ username.text=(@username)
122
+
123
+ if @password.nil?
124
+ # no password provided
125
+ elsif @type == HASHED
126
+ password = username_token.add_element("wsse:Password")
127
+ #BUG #4400
128
+ #password.add_attribute("Type", Types::PASSWORD_DIGEST)
129
+ #Solution--------------------------------------------------
130
+ created = username_token.add_element("wsu:Created")
131
+ created_time = Time.new.getutc()
132
+ #created_time = (Time.new()-(60*60*1)).getutc.iso8601()
133
+ #----------------------------------------------------------
134
+ created.text=(created_time)
135
+
136
+ password.add_attribute("Type", Types::PASSWORD_DIGEST)
137
+ nonce = username_token.add_element("wsse:Nonce")
138
+ nonce_text = OpenSSL::Random.random_bytes(20).to_s().strip()
139
+ nonce.text=(Base64.encode64(nonce_text))
140
+ stamp = nonce_text.to_s() + created_time.to_s() + @password.to_s()
141
+ hash = CryptHash.new().digest_b64(stamp)
142
+ password.text=(hash.to_s())
143
+ else
144
+ password = username_token.add_element("wsse:Password")
145
+ password.add_attribute("Type", Types::PASSWORD_TEXT)
146
+ password.text=@password
147
+ end
148
+
149
+ # BUG #5877 -----------------------------------------------
150
+ #created_time = (Time.new()-(60*60*1)).iso8601()
151
+ #created_time = created_time[0..created_time.index("+")]
152
+ #created_time[-1]="Z"
153
+ #----------------------------------------------------------
154
+
155
+ end
156
+ end
157
+
158
+ end #Xml
159
+ end #Security
160
+ end #WSS4R
161
+
162
+
163
+ if __FILE__ == $0
164
+ require "rexml/document"
165
+ require "pp"
166
+ require "wss4r/rpc/wssdriver"
167
+ include REXML
168
+ document = Document.new(File.new(ARGV[1]))
169
+ if ARGV[0] == "p"
170
+ usernametoken = WSS4R::Security::Xml::UsernameToken.new("Ron","noR")
171
+ usernametoken.process(document)
172
+ pp(document.to_s())
173
+ else
174
+ element = XPath.match(document, "/soap:Envelope/soap:Header/wsse:Security/wsse:UsernameToken")[0]
175
+ usernametoken = WSS4R::Security::Xml::UsernameToken.new()
176
+ usernametoken.unprocess(element)
177
+ end
178
+ end
179
+
180
+
@@ -0,0 +1,27 @@
1
+ require "soap/rpc/standaloneServer"
2
+ require "wss4r/security/security"
3
+ require "wss4r/rpc/wssdriver"
4
+
5
+ include WSS4R::Security
6
+
7
+ class SOAP::RPC::StandaloneServer
8
+ def get_soaplet
9
+ @soaplet
10
+ end
11
+ end
12
+
13
+ module WSS4R
14
+ module Server
15
+
16
+ class WSSStandaloneServer < SOAP::RPC::StandaloneServer
17
+ def security()
18
+ security = get_soaplet().app_scope_router().security()
19
+ if (security == nil)
20
+ security = WSS4R::Security::Security.new()
21
+ end
22
+ security
23
+ end
24
+ end
25
+
26
+ end #Server
27
+ end #WSS4R
@@ -0,0 +1,92 @@
1
+ require "wss4r/config/config"
2
+ require "wss4r/security/security"
3
+ require "pp"
4
+
5
+ include WSS4R
6
+ include Security
7
+
8
+ module SOAP
9
+ module Header
10
+ class HandlerSet
11
+ def on_inbound(headers)
12
+ headers.each do |name, item|
13
+ handler = @store.find { |handler|
14
+ handler.elename == item.element.elename
15
+ }
16
+ if handler
17
+ handler.on_inbound_headeritem(item)
18
+ elsif item.mustunderstand
19
+ #raise UnhandledMustUnderstandHeaderError.new(item.element.elename.to_s)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ module Processor
27
+ class << self
28
+ public
29
+ alias old_marshal marshal
30
+ alias old_unmarshal unmarshal
31
+
32
+ def security=(s)
33
+ @@security = s
34
+ end
35
+ def security()
36
+ @@security = WSS4R::Security::Security.new() if (!defined?(@@security))
37
+ @@security
38
+ end
39
+
40
+ def config()
41
+ store = WSS4R::Config::Store.new()
42
+ config = store.load()
43
+ if (config != nil)
44
+ @security = WSS4R::Security::Security.new()
45
+ config.build_security(@security)
46
+ end
47
+ end
48
+
49
+ def marshal(env,opt = {}, io = nil)
50
+ #config()
51
+ xml = Processor.old_marshal(env, opt, io)
52
+
53
+ security = opt[:security]
54
+ if (defined?(@@security) && @@security != nil)
55
+ security = @@security if (@@security)
56
+ end
57
+ document = Document.new(xml)
58
+ if (security != nil)
59
+ security.process_document_marshal(document)
60
+ end
61
+ document.to_s()
62
+ end
63
+
64
+ def unmarshal(stream, opt = {})
65
+ #config()
66
+ security = opt[:security]
67
+ if (defined?(@@security) && @@security != nil)
68
+ security = @@security if (@@security)
69
+ end
70
+ doc = Document.new(stream)
71
+ if (security != nil)
72
+ SOAPParser::document=(doc)
73
+ soap_ns = doc.root().prefix()
74
+ wsseElement = SOAPParser.part(SOAPParser::SECURITY)
75
+ if (wsseElement != nil)
76
+ security.process_document_unmarshal(doc)
77
+ if (wsseElement.parent() != nil)
78
+ wsseElement.parent().delete_element(wsseElement)
79
+ end
80
+ else
81
+ #TODO: What to do when no security tokens in header?
82
+ #if security.tokens().size() > 0
83
+ # raise Exception.new("No security elements received!")
84
+ #end
85
+ end
86
+ end
87
+ xml = Processor.old_unmarshal(doc.to_s(), opt)
88
+ xml
89
+ end
90
+ end
91
+ end
92
+ end