class Vcert::TPPConnection
Constants
- ALL_ALLOWED_REGEX
- TOKEN_HEADER_NAME
- URL_AUTHORIZE
- URL_CERTIFICATE_RENEW
- URL_CERTIFICATE_REQUESTS
- URL_CERTIFICATE_RETRIEVE
- URL_CERTIFICATE_SEARCH
- URL_SECRET_STORE_RETRIEVE
- URL_SECRET_STORE_SEARCH
- URL_ZONE_CONFIG
Public Class Methods
new(url, user, password, trust_bundle: nil)
click to toggle source
# File lib/tpp/tpp.rb, line 7 def initialize(url, user, password, trust_bundle: nil) @url = normalize_url url @user = user @password = password @token = nil @trust_bundle = trust_bundle end
Public Instance Methods
policy(zone_tag)
click to toggle source
# File lib/tpp/tpp.rb, line 41 def policy(zone_tag) code, response = post URL_ZONE_CONFIG, {:PolicyDN => policy_dn(zone_tag)} if code != 200 raise Vcert::ServerUnexpectedBehaviorError, "Status #{code}" end parse_policy_response response, zone_tag end
renew(request, generate_new_key: true)
click to toggle source
# File lib/tpp/tpp.rb, line 57 def renew(request, generate_new_key: true) if request.id.nil? && request.thumbprint.nil? raise('Either request ID or certificate thumbprint is required to renew the certificate') end request.id = search_by_thumbprint(request.thumbprint) unless request.thumbprint.nil? renew_req_data = {"CertificateDN": request.id} if generate_new_key csr_base64_data = retrieve request LOG.info("Retrieved certificate:\n#{csr_base64_data.cert}") parsed_csr = parse_csr_fields_tpp(csr_base64_data.cert) renew_request = Vcert::Request.new( common_name: parsed_csr.fetch(:CN, nil), san_dns: parsed_csr.fetch(:DNS, nil), country: parsed_csr.fetch(:C, nil), province: parsed_csr.fetch(:ST, nil), locality: parsed_csr.fetch(:L, nil), organization: parsed_csr.fetch(:O, nil), organizational_unit: parsed_csr.fetch(:OU, nil) ) renew_req_data.merge!(PKCS10: renew_request.csr) end LOG.info("Trying to renew certificate #{request.id}") _, d = post(URL_CERTIFICATE_RENEW, renew_req_data) raise 'Certificate renew error' unless d.key?('Success') if generate_new_key [request.id, renew_request.private_key] else [request.id, nil] end end
request(zone_tag, request)
click to toggle source
# File lib/tpp/tpp.rb, line 15 def request(zone_tag, request) data = {:PolicyDN => policy_dn(zone_tag), :PKCS10 => request.csr, :ObjectName => request.friendly_name, :DisableAutomaticRenewal => "true"} code, response = post URL_CERTIFICATE_REQUESTS, data if code != 200 raise Vcert::ServerUnexpectedBehaviorError, "Status #{code}" end request.id = response['CertificateDN'] end
retrieve(request)
click to toggle source
# File lib/tpp/tpp.rb, line 27 def retrieve(request) retrieve_request = {CertificateDN: request.id, Format: "base64", IncludeChain: 'true', RootFirstOrder: "false"} code, response = post URL_CERTIFICATE_RETRIEVE, retrieve_request if code != 200 return nil end full_chain = Base64.decode64(response['CertificateData']) cert = parse_full_chain full_chain if cert.private_key == nil cert.private_key = request.private_key end cert end
zone_configuration(zone_tag)
click to toggle source
# File lib/tpp/tpp.rb, line 49 def zone_configuration(zone_tag) code, response = post URL_ZONE_CONFIG, {:PolicyDN => policy_dn(zone_tag)} if code != 200 raise Vcert::ServerUnexpectedBehaviorError, "Status #{code}" end parse_zone_configuration response end
Private Instance Methods
addStartEnd(s)
click to toggle source
# File lib/tpp/tpp.rb, line 222 def addStartEnd(s) unless s.index("^") == 0 s = "^" + s end unless s.end_with?("$") s = s + "$" end s end
auth()
click to toggle source
# File lib/tpp/tpp.rb, line 104 def auth uri = URI.parse(@url) request = Net::HTTP.new(uri.host, uri.port) request.use_ssl = true if @trust_bundle != nil request.ca_file = @trust_bundle end url = uri.path + URL_AUTHORIZE data = {:Username => @user, :Password => @password} encoded_data = JSON.generate(data) response = request.post(url, encoded_data, {"Content-Type" => "application/json"}) if response.code.to_i != 200 raise Vcert::AuthenticationError end data = JSON.parse(response.body) token = data['APIKey'] valid_until = DateTime.strptime(data['ValidUntil'].gsub(/\D/, ''), '%Q') @token = token, valid_until end
escape(value)
click to toggle source
# File lib/tpp/tpp.rb, line 232 def escape(value) if value.kind_of? Array return value.map { |v| addStartEnd(Regexp.escape(v)) } else return addStartEnd(Regexp.escape(value)) end end
get(url)
click to toggle source
# File lib/tpp/tpp.rb, line 142 def get(url) if @token == nil || @token[1] < DateTime.now auth() end uri = URI.parse(@url) request = Net::HTTP.new(uri.host, uri.port) request.use_ssl = true if @trust_bundle != nil request.ca_file = @trust_bundle end url = uri.path + url LOG.info("#{Vcert::VCERT_PREFIX} GET request: #{request.inspect}\n\tpath: #{url}") response = request.get(url, { TOKEN_HEADER_NAME => @token[0] }) # TODO: check valid json data = JSON.parse(response.body) return response.code.to_i, data end
normalize_url(url)
click to toggle source
# File lib/tpp/tpp.rb, line 174 def normalize_url(url) if url.index('http://') == 0 url = "https://" + url[7..-1] elsif url.index('https://') != 0 url = 'https://' + url end unless url.end_with?('/') url = url + '/' end unless url.end_with?('/vedsdk/') url = url + 'vedsdk/' end unless url =~ /^https:\/\/[a-z\d]+[-a-z\d.]+[a-z\d][:\d]*\/vedsdk\/$/ raise Vcert::ClientBadDataError, "Invalid URL for TPP" end url end
parse_full_chain(full_chain)
click to toggle source
# File lib/tpp/tpp.rb, line 192 def parse_full_chain(full_chain) pems = parse_pem_list(full_chain) Vcert::Certificate.new cert: pems[0], chain: pems[1..-1], private_key: nil end
parse_policy_response(response, zone_tag)
click to toggle source
# File lib/tpp/tpp.rb, line 221 def parse_policy_response(response, zone_tag) def addStartEnd(s) unless s.index("^") == 0 s = "^" + s end unless s.end_with?("$") s = s + "$" end s end def escape(value) if value.kind_of? Array return value.map { |v| addStartEnd(Regexp.escape(v)) } else return addStartEnd(Regexp.escape(value)) end end policy = response["Policy"] s = policy["Subject"] if policy["WhitelistedDomains"].empty? subjectCNRegex = [ALL_ALLOWED_REGEX] else if policy["WildcardsAllowed"] subjectCNRegex = policy["WhitelistedDomains"].map { |d| addStartEnd('[\w\-*]+' + Regexp.escape("." + d)) } else subjectCNRegex = policy["WhitelistedDomains"].map { |d| addStartEnd('[\w\-]+' + Regexp.escape("." + d)) } end end if s["OrganizationalUnit"]["Locked"] subjectOURegexes = escape(s["OrganizationalUnit"]["Values"]) else subjectOURegexes = [ALL_ALLOWED_REGEX] end if s["Organization"]["Locked"] subjectORegexes = [escape(s["Organization"]["Value"])] else subjectORegexes = [ALL_ALLOWED_REGEX] end if s["City"]["Locked"] subjectLRegexes = [escape(s["City"]["Value"])] else subjectLRegexes = [ALL_ALLOWED_REGEX] end if s["State"]["Locked"] subjectSTRegexes = [escape(s["State"]["Value"])] else subjectSTRegexes = [ALL_ALLOWED_REGEX] end if s["Country"]["Locked"] subjectCRegexes = [escape(s["Country"]["Value"])] else subjectCRegexes = [ALL_ALLOWED_REGEX] end if policy["SubjAltNameDnsAllowed"] if policy["WhitelistedDomains"].length == 0 dnsSanRegExs = [ALL_ALLOWED_REGEX] else dnsSanRegExs = policy["WhitelistedDomains"].map { |d| addStartEnd('[\w-]+' + Regexp.escape("." + d)) } end else dnsSanRegExs = [] end if policy["SubjAltNameIpAllowed"] ipSanRegExs = [ALL_ALLOWED_REGEX] # todo: support else ipSanRegExs = [] end if policy["SubjAltNameEmailAllowed"] emailSanRegExs = [ALL_ALLOWED_REGEX] # todo: support else emailSanRegExs = [] end if policy["SubjAltNameUriAllowed"] uriSanRegExs = [ALL_ALLOWED_REGEX] # todo: support else uriSanRegExs = [] end if policy["SubjAltNameUpnAllowed"] upnSanRegExs = [ALL_ALLOWED_REGEX] # todo: support else upnSanRegExs = [] end unless policy["KeyPair"]["KeyAlgorithm"]["Locked"] key_types = [1024, 2048, 4096, 8192].map { |s| Vcert::KeyType.new("rsa", s) } + Vcert::SUPPORTED_CURVES.map { |c| Vcert::KeyType.new("ecdsa", c) } else if policy["KeyPair"]["KeyAlgorithm"]["Value"] == "RSA" if policy["KeyPair"]["KeySize"]["Locked"] key_types = [Vcert::KeyType.new("rsa", policy["KeyPair"]["KeySize"]["Value"])] else key_types = [1024, 2048, 4096, 8192].map { |s| Vcert::KeyType.new("rsa", s) } end elsif policy["KeyPair"]["KeyAlgorithm"]["Value"] == "EC" if policy["KeyPair"]["EllipticCurve"]["Locked"] curve = {"p224" => "secp224r1", "p256" => "prime256v1", "p521" => "secp521r1"}[policy["KeyPair"]["EllipticCurve"]["Value"].downcase] key_types = [Vcert::KeyType.new("ecdsa", curve)] else key_types = Vcert::SUPPORTED_CURVES.map { |c| Vcert::KeyType.new("ecdsa", c) } end end end Vcert::Policy.new(policy_id: policy_dn(zone_tag), name: zone_tag, system_generated: false, creation_date: nil, subject_cn_regexes: subjectCNRegex, subject_o_regexes: subjectORegexes, subject_ou_regexes: subjectOURegexes, subject_st_regexes: subjectSTRegexes, subject_l_regexes: subjectLRegexes, subject_c_regexes: subjectCRegexes, san_regexes: dnsSanRegExs, key_types: key_types) end
parse_zone_configuration(data)
click to toggle source
# File lib/tpp/tpp.rb, line 209 def parse_zone_configuration(data) s = data["Policy"]["Subject"] country = Vcert::CertField.new s["Country"]["Value"], locked: s["Country"]["Locked"] state = Vcert::CertField.new s["State"]["Value"], locked: s["State"]["Locked"] city = Vcert::CertField.new s["City"]["Value"], locked: s["City"]["Locked"] organization = Vcert::CertField.new s["Organization"]["Value"], locked: s["Organization"]["Locked"] organizational_unit = Vcert::CertField.new s["OrganizationalUnit"]["Values"], locked: s["OrganizationalUnit"]["Locked"] key_type = Vcert::KeyType.new data["Policy"]["KeyPair"]["KeyAlgorithm"]["Value"], data["Policy"]["KeyPair"]["KeySize"]["Value"] Vcert::ZoneConfiguration.new country: country, province: state, locality: city, organization: organization, organizational_unit: organizational_unit, key_type: Vcert::CertField.new(key_type) end
policy_dn(zone)
click to toggle source
# File lib/tpp/tpp.rb, line 160 def policy_dn(zone) if zone == nil || zone == '' raise Vcert::ClientBadDataError, "Zone should not be empty" end if zone =~ /^\\\\VED\\\\Poplicy/ return zone end if zone =~ /^\\\\/ return '\\VED\\Policy' + zone else return '\\VED\\Policy\\' + zone end end
post(url, data)
click to toggle source
# File lib/tpp/tpp.rb, line 124 def post(url, data) if @token == nil || @token[1] < DateTime.now auth() end uri = URI.parse(@url) request = Net::HTTP.new(uri.host, uri.port) request.use_ssl = true if @trust_bundle != nil request.ca_file = @trust_bundle end url = uri.path + url encoded_data = JSON.generate(data) LOG.info("#{Vcert::VCERT_PREFIX} POST request: #{request.inspect}\n\tpath: #{url}\n\tdata: #{encoded_data}") response = request.post(url, encoded_data, {TOKEN_HEADER_NAME => @token[0], "Content-Type" => "application/json"}) data = JSON.parse(response.body) return response.code.to_i, data end
search_by_thumbprint(thumbprint)
click to toggle source
# File lib/tpp/tpp.rb, line 197 def search_by_thumbprint(thumbprint) # thumbprint = re.sub(r'[^\dabcdefABCDEF]', "", thumbprint) thumbprint = thumbprint.upcase status, data = get(URL_CERTIFICATE_SEARCH+"?Thumbprint=#{thumbprint}") # TODO: check that data have valid certificate in it if status != 200 raise Vcert::ServerUnexpectedBehaviorError, "Status: #{status}. Message:\n #{data.body.to_s}" end # TODO: check valid data return data['Certificates'][0]['DN'] end