Skip to content

Instantly share code, notes, and snippets.

@huseyin

huseyin/kps.rb Secret

Last active September 23, 2018 20:54
Show Gist options
  • Save huseyin/ae608ada8c7ed4c2ee78ef5b9f9b5fcc to your computer and use it in GitHub Desktop.
Save huseyin/ae608ada8c7ed4c2ee78ef5b9f9b5fcc to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'erb'
require 'logger'
require 'net/http'
require 'openssl'
require 'savon'
require 'securerandom'
require 'time'
require 'uri'
class KPS
KPS_URL = 'https://kpsv2.nvi.gov.tr/Services/RoutingService.svc'.freeze
STS_URL = 'https://kimlikdogrulama.nvi.gov.tr/Services/Issuer.svc/IWSTrust13'.freeze
attr_reader :username, :password
def initialize(username, password)
@username = username
@password = password
end
def created_time
timestamp
end
def expires_time
timestamp(300)
end
def requests
kps_request
end
def timestamp_header
<<~EOF.chomp
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="_0">
<wsu:Created>#{created_time}</wsu:Created>
<wsu:Expires>#{expires_time}</wsu:Expires>
</wsu:Timestamp>
EOF
end
def security_content
<<~EOF.chomp
<wsse:UsernameToken wsu:Id="Me">
<wsse:Username>#{@username}</wsse:Username>
<wsse:Password>#{@password}</wsse:Password>
</wsse:UsernameToken>
EOF
end
def security_header(timestamp_header, security_content, method)
<<~EOF.chomp
<wsse:Security>
#{timestamp_header}
#{security_content}
</wsse:Security>
<wsa:To>#{STS_URL}</wsa:To>
<wsa:Action>#{method}</wsa:Action>
EOF
end
def token_body
<<~EOF.chomp
<wst:RequestSecurityToken>
<wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>
<wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsa:EndpointReference>
<wsa:Address>#{KPS_URL}</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>
</wst:RequestSecurityToken>
EOF
end
def full_body(header, body)
<<~EOF.chomp
<s:Envelope
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
xmlns:b="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"
>
<s:Header>#{header}</s:Header>
<s:Body>#{body}</s:Body>
</s:Envelope>
EOF
end
def token_request
header = security_header(timestamp_header, security_content, 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue')
request STS_URL, full_body(header, token_body)
end
def kps_request(method, body)
doc = Nokogiri::XML(token_request)
doc.root.add_namespace('o', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd')
doc.root.add_namespace('s', 'http://www.w3.org/2003/05/soap-envelope')
doc.root.add_namespace('wst', 'http://docs.oasis-open.org/ws-sx/ws-trust/200512')
doc.root.add_namespace('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd')
doc.root.add_namespace('trust', 'http://docs.oasis-open.org/ws-sx/ws-trust/200512')
doc.root.add_namespace('xenc', 'http://www.w3.org/2001/04/xmlenc#')
saml_assign_id = doc.xpath('/s:Envelope/s:Body/trust:RequestSecurityTokenResponseCollection/trust:RequestSecurityTokenResponse/trust:RequestedAttachedReference/o:SecurityTokenReference/o:KeyIdentifier').text
proof_key = doc.xpath('/s:Envelope/s:Body/wst:RequestSecurityTokenResponseCollection/wst:RequestSecurityTokenResponse/wst:RequestedProofToken/wst:BinarySecret').text
token = doc.at_xpath('/s:Envelope/s:Body/wst:RequestSecurityTokenResponseCollection/wst:RequestSecurityTokenResponse/wst:RequestedSecurityToken/xenc:EncryptedData').serialize(save_with: 0)
proof_key = Base64.decode64(proof_key)
timestamp_xml = timestamp_header
digest_value = Base64.encode64(Digest::SHA1.digest(timestamp_xml)).chomp
signed_info = <<~EOF.chomp
<dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
<dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"/>
<dsig:Reference URI="#_0">
<dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></dsig:Transform></dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<dsig:DigestValue>#{digest_value}</dsig:DigestValue>
</dsig:Reference>
</dsig:SignedInfo>
EOF
signature_value = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), proof_key, signed_info)).chomp
token_signature = <<~EOF.chomp
<dsig:Signature>
#{signed_info}
<dsig:SignatureValue>#{signature_value}</dsig:SignatureValue>
<dsig:KeyInfo>
<wsse:SecurityTokenReference b:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">#{saml_assign_id}</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</dsig:KeyInfo>
</dsig:Signature>
EOF
header = security_header(timestamp_xml, token + token_signature, method)
body = full_body(header, body)
request KPS_URL, body
end
def request(url, body)
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
req = Net::HTTP::Post.new(uri.path)
req['Content-Type'] = 'application/soap+xml; charset=utf-8'
req['Connection'] = 'keep-alive'
req.body = body
http.request(req).body
end
def timestamp(range = 0)
(Time.now.utc + range).strftime("%Y-%m-%d\T%H:%M:%S\Z")
end
end
def main
client = KPS.new(ENV['KPS_USERNAME'], ENV['KPS_PASSWORD'])
body = <<~EOF.chomp
<Sorgula xmlns="http://kps.nvi.gov.tr/2011/01/01" xmlns:ns2="http://schemas.microsoft.com/2003/10/Serialization/">
<kriterListesi>
<BilesikKutukSorgulaKimlikNoSorguKriteri>
<KimlikNo>10858434902</KimlikNo>
</BilesikKutukSorgulaKimlikNoSorguKriteri>
</kriterListesi>
</Sorgula>
EOF
method = 'http://kps.nvi.gov.tr/2011/01/01/BilesikKutukSorgulaKimlikNoServis/Sorgula'
puts client.kps_request(method, body)
end
main if $PROGRAM_NAME == __FILE__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment