wss4r 0.5

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.
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