3 # swede - A tool to create DANE/TLSA records.
4 # This tool is really simple and not foolproof, it doesn't check the CN in the
5 # Subject field of the certificate. It also doesn't check if the supplied
6 # certificate is a CA certificate if usage 1 is specified (or any other
7 # checking for that matter).
9 # Usage is explained when running this program with --help
11 # This tool is loosly based on the dane tool in the sshfp package by Paul
12 # Wouters and Christopher Olah from xelerance.com.
14 # Copyright Pieter Lexis (pieter.lexis@os3.nl)
16 # License: GNU GENERAL PUBLIC LICENSE Version 2 or later
23 from M2Crypto import X509, SSL
24 from binascii import a2b_hex, b2a_hex
25 from hashlib import sha256, sha512
26 from ipaddr import IPv4Address, IPv6Address
29 def genTLSA(hostname, protocol, port, certificate, output='generic', usage=1, selector=0, mtype=1):
30 """This function generates a TLSARecord object using the data passed in the parameters,
31 it then validates the record and returns the RR as a string.
33 # check if valid vars were passed
34 if hostname[-1] != '.':
37 certificate = loadCert(certificate)
39 raise Exception('Cannot load certificate from disk')
41 # Create the record without a certificate
43 record = TLSARecord(name='%s._%s.%s'%(port,protocol,hostname), usage=usage, selector=selector, mtype=mtype, cert ='')
45 record = TLSARecord(name='_%s._%s.%s'%(port,protocol,hostname), usage=usage, selector=selector, mtype=mtype, cert ='')
46 # Check if the record is valid
48 if record.selector == 0:
49 # Hash the Full certificate
50 record.cert = getHash(certificate, record.mtype)
52 # Hash only the SubjectPublicKeyInfo
53 record.cert = getHash(certificate.get_pubkey(), record.mtype)
55 record.isValid(raiseException=True)
57 if output == 'generic':
58 return record.getRecord(generic=True)
59 return record.getRecord()
61 def getA(hostname, secure=True):
62 """Gets a list of A records for hostname, returns a list of ARecords"""
64 records = getRecords(hostname, rrtype='A', secure=secure)
65 except InsecureLookupException, e:
68 except DNSLookupError, e:
69 print 'Unable to resolve %s: %s' % (hostname, str(e))
72 for record in records:
73 ret.append(ARecord(hostname, str(IPv4Address(int(b2a_hex(record),16)))))
76 def getAAAA(hostname, secure=True):
77 """Gets a list of A records for hostname, returns a list of AAAARecords"""
79 records = getRecords(hostname, rrtype='AAAA', secure=secure)
80 except InsecureLookupException, e:
83 except DNSLookupError, e:
84 print 'Unable to resolve %s: %s' % (hostname, str(e))
87 for record in records:
88 ret.append(AAAARecord(hostname, str(IPv6Address(int(b2a_hex(record),16)))))
91 def getVerificationErrorReason(num):
92 """This function returns the name of the X509 Error based on int(num)
94 # These were taken from the M2Crypto.m2 code
96 50: "X509_V_ERR_APPLICATION_VERIFICATION",
97 22: "X509_V_ERR_CERT_CHAIN_TOO_LONG",
98 10: "X509_V_ERR_CERT_HAS_EXPIRED",
99 9: "X509_V_ERR_CERT_NOT_YET_VALID",
100 28: "X509_V_ERR_CERT_REJECTED",
101 23: "X509_V_ERR_CERT_REVOKED",
102 7: "X509_V_ERR_CERT_SIGNATURE_FAILURE",
103 27: "X509_V_ERR_CERT_UNTRUSTED",
104 12: "X509_V_ERR_CRL_HAS_EXPIRED",
105 11: "X509_V_ERR_CRL_NOT_YET_VALID",
106 8: "X509_V_ERR_CRL_SIGNATURE_FAILURE",
107 18: "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",
108 14: "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
109 13: "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
110 15: "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD",
111 16: "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD",
112 24: "X509_V_ERR_INVALID_CA",
113 26: "X509_V_ERR_INVALID_PURPOSE",
114 17: "X509_V_ERR_OUT_OF_MEM",
115 25: "X509_V_ERR_PATH_LENGTH_EXCEEDED",
116 19: "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN",
117 6: "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
118 4: "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE",
119 5: "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE",
120 3: "X509_V_ERR_UNABLE_TO_GET_CRL",
121 2: "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
122 20: "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
123 21: "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE",
124 0: "X509_V_OK"}[int(num)]
126 def getRecords(hostname, rrtype='A', secure=True):
127 """Do a lookup of a name and a rrtype, returns a list of binary coded strings. Only queries for rr_class IN."""
129 ctx = unbound.ub_ctx()
130 ctx.add_ta_file('root.key')
131 ctx.set_option("dlv-anchor-file:", "dlv.isc.org.key")
132 # Use the local cache
133 if resolvconf and os.path.isfile(resolvconf):
134 ctx.resolvconf(resolvconf)
136 if type(rrtype) == str:
137 if 'RR_TYPE_' + rrtype in dir(unbound):
138 rrtype = getattr(unbound, 'RR_TYPE_' + rrtype)
140 raise Exception('Error: unknown RR TYPE: %s.' % rrtype)
141 elif type(rrtype) != int:
142 raise Exception('Error: rrtype in wrong format, neither int nor str.')
144 status, result = ctx.resolve(hostname, rrtype=rrtype)
145 if status == 0 and result.havedata:
146 if not result.secure:
148 # The data is insecure and a secure lookup was requested
149 raise InsecureLookupException('Error: query data not secure and secure data requested, unable to continue')
151 print >> sys.stderr, 'Warning: query data is not secure.'
152 # If we are here the data was either secure or insecure data is accepted
153 return result.data.raw
155 raise DNSLookupError('Unsuccesful lookup or no data returned for rrtype %s.' % rrtype)
157 def getHash(certificate, mtype):
158 """Hashes the certificate based on the mtype.
159 The certificate should be an M2Crypto.X509.X509 object (or the result of the get_pubkey() function on said object)
161 certificate = certificate.as_der()
163 return b2a_hex(certificate)
165 return sha256(certificate).hexdigest()
167 return sha512(certificate).hexdigest()
169 raise Exception('mtype should be 0,1,2')
171 def getTLSA(hostname, port=443, protocol='tcp', secure=True):
173 This function tries to do a secure lookup of the TLSA record.
174 At the moment it requests the TYPE52 record and parses it into a 'valid' TLSA record
175 It returns a list of TLSARecord objects
177 if hostname[-1] != '.':
180 if not protocol.lower() in ['tcp', 'udp', 'sctp']:
181 raise Exception('Error: unknown protocol: %s. Should be one of tcp, udp or sctp' % protocol)
184 records = getRecords('*._%s.%s' % (protocol.lower(), hostname), rrtype=52, secure=secure)
186 records = getRecords('_%s._%s.%s' % (port, protocol.lower(), hostname), rrtype=52, secure=secure)
187 except InsecureLookupException, e:
190 except DNSLookupError, e:
191 print 'Unable to resolve %s: %s' % (hostname, str(e))
194 for record in records:
195 hexdata = b2a_hex(record)
197 ret.append(TLSARecord('*._%s.%s' % (protocol.lower(), hostname), int(hexdata[0:2],16), int(hexdata[2:4],16), int(hexdata[4:6],16), hexdata[6:]))
199 ret.append(TLSARecord('_%s._%s.%s' % (port, protocol.lower(), hostname), int(hexdata[0:2],16), int(hexdata[2:4],16), int(hexdata[4:6],16), hexdata[6:]))
202 def loadCert(certificate):
203 """Returns an M2Crypto.X509.X509 object"""
204 if isinstance(certificate, X509.X509):
205 # nothing to be done :-)
208 # Maybe we were passed a path
209 return X509.load_cert(certificate)
211 # Can't load the cert
212 raise Exception('Unable to load certificate %s.' % certificate)
214 def verifyCertMatch(record, cert):
216 Verify the certificate with the record.
217 record should be a TLSARecord and cert should be a M2Crypto.X509.X509
219 if not isinstance(cert, X509.X509):
221 if not isinstance(record, TLSARecord):
224 if record.selector == 1:
225 certhash = getHash(cert.get_pubkey(), record.mtype)
227 certhash = getHash(cert, record.mtype)
232 if certhash == record.cert:
237 def verifyCertNameWithHostName(cert, hostname, with_msg=False):
238 """Verify the name on the certificate with a hostname, we need this because we get the cert based on IP address and thusly cannot rely on M2Crypto to verify this"""
239 if not isinstance(cert, X509.X509):
241 if not isinstance(hostname, str):
244 if hostname[-1] == '.':
245 hostname = hostname[0:-1]
247 # Ugly string comparison to see if the name on the ee-cert matches with the name provided on the commandline
249 altnames_on_cert = cert.get_ext('subjectAltName').get_value()
251 altnames_on_cert = ''
252 if hostname in (str(cert.get_subject()) + altnames_on_cert):
256 print 'WARNING: Name on the certificate (Subject: %s, SubjectAltName: %s) doesn\'t match requested hostname (%s).' % (str(cert.get_subject()), altnames_on_cert, hostname)
260 """When instanciated, this class contains all the fields of a TLSA record.
262 def __init__(self, name, usage, selector, mtype, cert):
263 """name is the name of the RR in the format: /^(_\d{1,5}|\*)\._(tcp|udp|sctp)\.([a-z0-9]*\.){2,}$/
264 usage, selector and mtype should be an integer
265 cert should be a hexidecimal string representing the certificate to be matched field
268 self.rrtype = 52 # TLSA per https://www.iana.org/assignments/dns-parameters
269 self.rrclass = 1 # IN
270 self.name = str(name)
271 self.usage = int(usage)
272 self.selector = int(selector)
273 self.mtype = int(mtype)
274 self.cert = str(cert)
276 raise Exception('Invalid value passed, unable to create a TLSARecord')
278 def getRecord(self, generic=False):
279 """Returns the RR string of this TLSARecord, either in rfc (default) or generic format"""
281 return '%s IN TYPE52 \# %s %s%s%s%s' % (self.name, (len(self.cert)/2)+3 , self._toHex(self.usage), self._toHex(self.selector), self._toHex(self.mtype), self.cert)
282 return '%s IN TLSA %s %s %s %s' % (self.name, self.usage, self.selector, self.mtype, self.cert)
284 def _toHex(self, val):
285 """Helper function to create hex strings from integers"""
288 def isValid(self, raiseException=False):
289 """Check whether all fields in the TLSA record are conforming to the spec and check if the port, protocol and name are good"""
292 if not 1 <= int(self.getPort()) <= 65535:
293 err.append('Port %s not within correct range (1 <= port <= 65535)' % self.getPort())
295 if self.getPort() != '*':
296 err.append('Port %s not a number' % self.getPort())
297 if not self.usage in [0,1,2,3]:
298 err.append('Usage: invalid (%s is not one of 0, 1, 2 or 3)' % self.usage)
299 if not self.selector in [0,1]:
300 err.append('Selector: invalid (%s is not one of 0 or 1)' % self.selector)
301 if not self.mtype in [0,1,2]:
302 err.append('Matching Type: invalid (%s is not one of 0, 1 or 2)' % self.mtype)
303 if not self.isNameValid():
304 err.append('Name (%s) is not in the correct format: _portnumber._transportprotocol.hostname.dom.' % self.name)
305 # A certificate length of 0 is accepted
306 if self.mtype in [1,2] and len(self.cert) != 0:
307 if not len(self.cert) == {1:64,2:128}[self.mtype]:
308 err.append('Certificate for Association: invalid (Hash length does not match hash-type in Matching Type(%s))' % {1:'SHA-256',2:'SHA-512'}[self.mtype])
310 if not raiseException:
313 msg = 'The TLSA record is invalid.'
315 msg += '\n\t%s' % error
316 raise RecordValidityException(msg)
320 def isNameValid(self):
321 """Check if the name if in the correct format"""
322 if not re.match('^(_\d{1,5}|\*)\._(tcp|udp|sctp)\.([-a-z0-9]*\.){2,}$', self.name):
326 def getProtocol(self):
327 """Returns the protocol based on the name"""
328 return re.split('\.', self.name)[1][1:]
331 """Returns the port based on the name"""
332 if re.split('\.', self.name)[0][0] == '*':
335 return re.split('\.', self.name)[0][1:]
338 """An object representing an A Record (IPv4 address)"""
339 def __init__(self, hostname, address):
341 self.hostname = hostname
342 self.address = address
349 IPv4Address(self.address)
355 """An object representing an AAAA Record (IPv6 address)"""
356 def __init__(self, hostname, address):
358 self.hostname = hostname
359 self.address = address
366 IPv6Address(self.address)
372 class RecordValidityException(Exception):
375 class InsecureLookupException(Exception):
378 class DNSLookupError(Exception):
381 if __name__ == '__main__':
384 parser = argparse.ArgumentParser(description='Create and verify DANE records.', epilog='This tool has a few limitations')
386 subparsers = parser.add_subparsers(title='Functions', help='Available functions, see %(prog)s function -h for function-specific help')
387 parser_verify = subparsers.add_parser('verify', help='Verify a TLSA record, exit 0 when all TLSA records are matched, exit 2 when a record does not match the received certificate, exit 1 on error.', epilog='Caveat: For TLSA validation, this program chases through the certificate chain offered by the server, not its local certificates.')
388 parser_verify.set_defaults(function='verify')
389 parser_create = subparsers.add_parser('create', help='Create a TLSA record')
390 parser_create.set_defaults(function='create')
392 #parser.add_argument('-4', dest='ipv4', action='store_true',help='use ipv4 networking only')
393 #parser.add_argument('-6', dest='ipv6', action='store_true',help='use ipv6 networking only')
394 parser.add_argument('--insecure', action='store_true', default=False, help='Allow use of non-dnssec secured answers')
395 parser.add_argument('--resolvconf', metavar='/PATH/TO/RESOLV.CONF', action='store', default='', help='Use a recursive resolver from resolv.conf')
396 parser.add_argument('-v', '--version', action='version', version='%(prog)s v0.2', help='show version and exit')
397 parser.add_argument('host', metavar="hostname")
399 parser_verify.add_argument('--port', '-p', action='store', default='443', help='The port, or \'*\' where running TLS is located (default: %(default)s).')
400 parser_verify.add_argument('--protocol', action='store', choices=['tcp','udp','sctp'], default='tcp', help='The protocol the TLS service is using (default: %(default)s).')
401 parser_verify.add_argument('--only-rr', '-o', action='store_true', help='Only verify that the TLSA resource record is correct (do not check certificate)')
402 parser_verify.add_argument('--ca-cert', metavar='/PATH/TO/CERTSTORE', action='store', default = '/etc/ssl/certs/', help='Path to a CA certificate or a directory containing the certificates (default: %(default)s)')
403 parser_verify.add_argument('--quiet', '-q', action='store_true', help='Only print the result of the validation')
405 parser_create.add_argument('--port', '-p', action='store', type=int, default=443, help='The port where running TLS is located (default: %(default)s).')
406 parser_create.add_argument('--protocol', action='store', choices=['tcp','udp','sctp'], default='tcp', help='The protocol the TLS service is using (default: %(default)s).')
407 parser_create.add_argument('--certificate', '-c', help='The certificate used for the host. If certificate is empty, the certificate will be downloaded from the server')
408 parser_create.add_argument('--output', '-o', action='store', default='generic', choices=['generic','rfc','both'], help='The type of output. Generic (RFC 3597, TYPE52), RFC (TLSA) or both (default: %(default)s).')
410 # Usage of the certificate
411 parser_create.add_argument('--usage', '-u', action='store', type=int, default=1, choices=[0,1,2,3], help='The Usage of the Certificate for Association. \'0\' for CA, \'1\' for End Entity, \'2\' for trust-anchor, \'3\' for ONLY End-Entity match (default: %(default)s).')
412 parser_create.add_argument('--selector', '-s', action='store', type=int, default=0, choices=[0,1], help='The Selector for the Certificate for Association. \'0\' for Full Certificate, \'1\' for SubjectPublicKeyInfo (default: %(default)s).')
413 parser_create.add_argument('--mtype', '-m', action='store', type=int, default=1, choices=[0,1,2], help='The Matching Type of the Certificate for Association. \'0\' for Exact match, \'1\' for SHA-256 hash, \'2\' for SHA-512 (default: %(default)s).')
415 args = parser.parse_args()
417 if args.host[-1] != '.':
422 if os.path.isfile(args.resolvconf):
423 resolvconf = args.resolvconf
425 print >> sys.stdout, '%s is not a file. Unable to use it as resolv.conf' % args.resolvconf
430 # not operations are fun!
431 secure = not args.insecure
433 if args.function == 'verify':
434 records = getTLSA(args.host, args.port, args.protocol, secure)
435 if len(records) == 0:
438 for record in records:
440 # First, check if the first three fields have correct values.
442 print 'Received the following record for name %s:' % record.name
443 print '\tUsage:\t\t\t\t%d (%s)' % (record.usage, {0:'CA Constraint', 1:'End-Entity Constraint + chain to CA', 2:'Trust Anchor', 3:'End-Entity'}.get(record.usage, 'INVALID'))
444 print '\tSelector:\t\t\t%d (%s)' % (record.selector, {0:'Certificate', 1:'SubjectPublicKeyInfo'}.get(record.selector, 'INVALID'))
445 print '\tMatching Type:\t\t\t%d (%s)' % (record.mtype, {0:'Full Certificate', 1:'SHA-256', 2:'SHA-512'}.get(record.mtype, 'INVALID'))
446 print '\tCertificate for Association:\t%s' % record.cert
449 record.isValid(raiseException=True)
450 except RecordValidityException, e:
451 print >> sys.stderr, 'Error: %s' % str(e)
455 print 'This record is valid (well-formed).'
458 # Go to the next record
461 # When we are here, The user also wants to verify the certificates with the record
462 if args.protocol != 'tcp':
463 print >> sys.stderr, 'Only SSL over TCP is supported (sorry)'
467 print 'Attempting to verify the record with the TLS service...'
468 addresses = getA(args.host, secure=secure) + getAAAA(args.host, secure=secure)
469 for address in addresses:
471 print 'Got the following IP: %s' % str(address)
472 # We do the certificate handling here, as M2Crypto keeps segfaulting when we do it in a method
474 if os.path.isfile(args.ca_cert):
475 if ctx.load_verify_locations(cafile=args.ca_cert) != 1: raise Exception('No CA cert')
476 elif os.path.exists(args.ca_cert):
477 if ctx.load_verify_locations(capath=args.ca_cert) != 1: raise Exception('No CA certs')
479 print >> sys.stderr, '%s is neither a file nor a directory, unable to continue' % args.ca_cert
481 # Don't error when the verification fails in the SSL handshake
482 ctx.set_verify(SSL.verify_none, depth=9)
483 if isinstance(address, AAAARecord):
484 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
485 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
488 connection = SSL.Connection(ctx, sock=sock)
490 connection.connect((str(address), int(args.port)))
491 except SSL.Checker.WrongHost, e:
492 # The name on the remote cert doesn't match the hostname because we connect on IP, not hostname (as we want secure lookup)
494 except socket.error, e:
495 print 'Cannot connect to %s: %s' % (address, str(e))
497 chain = connection.get_peer_cert_chain()
498 verify_result = connection.get_verify_result()
500 # Good, now let's verify
501 if not verifyCertNameWithHostName(cert=chain[0], hostname=str(args.host), with_msg=True):
502 # The name on the cert doesn't match the hostname... we don't verify the TLSA record
503 print 'Not checking the TLSA record.'
505 if record.usage == 1: # End-host cert
507 if verifyCertMatch(record, cert):
508 if verify_result == 0: # The cert chains to a valid CA cert according to the system-certificates
509 print 'SUCCESS (Usage 1): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate'
511 print 'FAIL (Usage 1): Certificate offered by the server matches the one mentioned in the TLSA record but the following error was raised during PKIX validation: %s' % getVerificationErrorReason(verify_result)
512 if pre_exit == 0: pre_exit = 2
513 if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
515 print 'FAIL: Certificate offered by the server does not match the TLSA record'
516 if pre_exit == 0: pre_exit = 2
518 elif record.usage == 0: # CA constraint
520 # Remove the first (= End-Entity cert) from the chain
523 if verifyCertMatch(record, cert):
528 if verify_result == 0:
529 print 'SUCCESS (Usage 0): A certificate in the certificate chain offered by the server matches the one mentioned in the TLSA record and is a CA certificate'
531 print 'FAIL (Usage 0): A certificate in the certificate chain offered by the server matches the one mentioned in the TLSA record and is a CA certificate, but the following error was raised during PKIX validation:' % getVerificationErrorReason(verify_result)
532 if pre_exit == 0: pre_exit = 2
534 print 'FAIL (Usage 0): A certificate in the certificate chain offered by the server matches the one mentioned in the TLSA record but is not a CA certificate'
535 if pre_exit == 0: pre_exit = 2
536 if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
538 print 'FAIL (Usage 0): No certificate in the certificate chain offered by the server matches the TLSA record'
539 if pre_exit == 0: pre_exit = 2
541 elif record.usage == 2: # Usage 2, use the cert in the record as trust anchor
542 #FIXME: doesnt comply to the spec
544 previous_issuer = None
547 if not str(previous_issuer) == str(cert.get_subject()): # The chain cannot be valid
548 print "FAIL: Certificates don't chain"
550 previous_issuer = cert.get_issuer()
551 if verifyCertMatch(record, cert):
555 print 'SUCCESS (usage 2): A certificate in the certificate chain (including the end-entity certificate) offered by the server matches the TLSA record'
556 if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
558 print 'FAIL (usage 2): No certificate in the certificate chain (including the end-entity certificate) offered by the server matches the TLSA record'
559 if pre_exit == 0: pre_exit = 2
561 elif record.usage == 3: # EE cert MUST match
562 if verifyCertMatch(record,chain[0]):
563 print 'SUCCESS (usage 3): The certificate offered by the server matches the TLSA record'
564 if not args.quiet: print 'The matched certificate has Subject: %s' % chain[0].get_subject()
566 print 'FAIL (usage 3): The certificate offered by the server does not match the TLSA record'
567 if pre_exit == 0: pre_exit = 2
569 # Cleanup, just in case
574 # END for address in addresses
575 # END for record in records
579 else: # we want to create
581 if not args.certificate:
582 if args.protocol != 'tcp':
583 print >> sys.stderr, 'Only SSL over TCP is supported (sorry)'
586 print 'No certificate specified on the commandline, attempting to retrieve it from the server %s' % (args.host)
587 connection_port = args.port
589 sys.stdout.write('The port specified on the commandline is *, please specify the port of the TLS service on %s (443): ' % args.host)
592 user_input = raw_input()
594 connection_port = 443
597 if 1 <= int(user_input) <= 65535:
598 connection_port = user_input
601 sys.stdout.write('Port %s not numerical or within correct range (1 <= port <= 65535), try again (hit enter for default 443): ' % user_input)
602 # Get the address records for the host
604 addresses = getA(args.host, secure=secure) + getAAAA(args.host, secure=secure)
605 except InsecureLookupException, e:
606 print >> sys.stderr, str(e)
609 for address in addresses:
610 print 'Attempting to get certificate from %s' % str(address)
611 # We do the certificate handling here, as M2Crypto keeps segfaulting when try to do stuff with the cert if we don't
613 ctx.set_verify(SSL.verify_none, depth=9)
614 if isinstance(address, AAAARecord):
615 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
616 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
619 connection = SSL.Connection(ctx, sock=sock)
621 connection.connect((str(address), int(connection_port)))
622 except SSL.Checker.WrongHost:
624 except socket.error, e:
625 print 'Cannot connect to %s: %s' % (address, str(e))
628 chain = connection.get_peer_cert_chain()
629 for chaincert in chain:
630 if int(args.usage) == 1 or int(args.usage) == 3:
631 # The first cert is the end-entity cert
632 print 'Got a certificate with Subject: %s' % chaincert.get_subject()
636 if (int(args.usage) == 0 and chaincert.check_ca()) or int(args.usage) == 2:
637 sys.stdout.write('Got a certificate with the following Subject:\n\t%s\nUse this as certificate to match? [y/N] ' % chaincert.get_subject())
640 user_input = raw_input()
641 if user_input in ['','n','N']:
643 elif user_input in ['y', 'Y']:
647 sys.stdout.write('Please answer Y or N')
651 if cert: # Print the requested records based on the retrieved certificates
652 if args.output == 'both':
653 print genTLSA(args.host, args.protocol, args.port, cert, 'draft', args.usage, args.selector, args.mtype)
654 print genTLSA(args.host, args.protocol, args.port, cert, 'rfc', args.usage, args.selector, args.mtype)
656 print genTLSA(args.host, args.protocol, args.port, cert, args.output, args.usage, args.selector, args.mtype)
658 # Clear the cert from memory (to stop M2Crypto from segfaulting)
659 # And cleanup the connection and context
665 else: # Pass the path to the certificate to the genTLSA function
666 if args.output == 'both':
667 print genTLSA(args.host, args.protocol, args.port, args.certificate, 'draft', args.usage, args.selector, args.mtype)
668 print genTLSA(args.host, args.protocol, args.port, args.certificate, 'rfc', args.usage, args.selector, args.mtype)
670 print genTLSA(args.host, args.protocol, args.port, args.certificate, args.output, args.usage, args.selector, args.mtype)