diff options
author | Rob Crittenden <rcritten@redhat.com> | 2010-06-24 11:40:02 -0400 |
---|---|---|
committer | Rob Crittenden <rcritten@redhat.com> | 2010-07-15 10:51:49 -0400 |
commit | 8d2d7429beb6bf66cb3c4fc35a7a3dbb165a432c (patch) | |
tree | c364bfb5b5926a165f1e6bc29e355131636afe45 /ipalib/x509.py | |
parent | 1e1985b17c3988056bef045fa84a9c7aaf0c4c65 (diff) | |
download | freeipa-8d2d7429beb6bf66cb3c4fc35a7a3dbb165a432c.tar.gz freeipa-8d2d7429beb6bf66cb3c4fc35a7a3dbb165a432c.tar.xz freeipa-8d2d7429beb6bf66cb3c4fc35a7a3dbb165a432c.zip |
Clean up crypto code, take advantage of new nss-python capabilities
This patch does the following:
- drops our in-tree x509v3 parser to use the python-nss one
- return more information on certificates
- make an API change, renaming cert-get to cert-show
- Drop a lot of duplicated code
Diffstat (limited to 'ipalib/x509.py')
-rw-r--r-- | ipalib/x509.py | 288 |
1 files changed, 52 insertions, 236 deletions
diff --git a/ipalib/x509.py b/ipalib/x509.py index 3c38a354e..bb765faad 100644 --- a/ipalib/x509.py +++ b/ipalib/x509.py @@ -1,199 +1,31 @@ -""" -Imported from pyasn1 project: - -Copyright (c) 2005-2009 Ilya Etingof <ilya@glas.net>, all rights reserved. - -THIS SOFTWARE IS NOT FAULT TOLERANT AND SHOULD NOT BE USED IN ANY SITUATION -ENDANGERING HUMAN LIFE OR PROPERTY. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - * The name of the authors may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - -""" -Enhancements released under IPA GPLv2 only license -""" - -# Read ASN.1/PEM X.509 certificates on stdin, parse each into plain text, -# then build substrate from it -import sys, string, base64 -from pyasn1.type import tag,namedtype,namedval,univ,constraint,char,useful -from pyasn1.codec.der import decoder, encoder -from pyasn1 import error - -# Would be autogenerated from ASN.1 source by a ASN.1 parser -# X.509 spec (rfc2459) +# Authors: +# Rob Crittenden <rcritten@redhat.com> +# +# Copyright (C) 2010 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import sys +import base64 +import nss.nss as nss +from ipapython import ipautil +from ipalib import api PEM = 0 DER = 1 -# Common OIDs found in a subject -oidtable = { "2.5.4.3": "CN", - "2.5.4.6": "C", - "2.5.4.7": "L", - "2.5.4.8": "ST", - "2.5.4.10": "O", - "2.5.4.11": "OU", - "1.2.840.113549.1.9.1": "E", - "0.9.2342.19200300.100.1.25": "DC", - } - -MAX = 64 # XXX ? - -class DirectoryString(univ.Choice): - componentType = namedtype.NamedTypes( - namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), - namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), - namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), - namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), - namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), - namedtype.NamedType('ia5String', char.IA5String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) # hm, this should not be here!? XXX - ) - -class AttributeValue(DirectoryString): pass - -class AttributeType(univ.ObjectIdentifier): pass - -class AttributeTypeAndValue(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('type', AttributeType()), - namedtype.NamedType('value', AttributeValue()) - ) - -class RelativeDistinguishedName(univ.SetOf): - componentType = AttributeTypeAndValue() - -class RDNSequence(univ.SequenceOf): - componentType = RelativeDistinguishedName() - -class Name(univ.Choice): - componentType = namedtype.NamedTypes( - namedtype.NamedType('', RDNSequence()) - ) - - def get_components(self): - components = self.getComponentByPosition(0) - complist = [] - for idx in range(len(components)): - attrandvalue = components[idx].getComponentByPosition(0) - oid = attrandvalue.getComponentByPosition(0) - # FIXME, should handle any string type - value = attrandvalue.getComponentByPosition(1).getComponentByType(char.PrintableString.tagSet) - if value is None: - value = attrandvalue.getComponentByPosition(1).getComponentByType(char.UTF8String.tagSet) - if value is None: - value = attrandvalue.getComponentByPosition(1).getComponentByType(char.IA5String.tagSet) - vout = value.prettyOut(value).decode('utf-8') - oidout = oid.prettyOut(oid).decode('utf-8') - c = ((oidtable.get(oidout, oidout), vout)) - complist.append(c) - - return tuple(complist) - -class AlgorithmIdentifier(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('algorithm', univ.ObjectIdentifier()), - namedtype.OptionalNamedType('parameters', univ.Null()) - # XXX syntax screwed? -# namedtype.OptionalNamedType('parameters', univ.ObjectIdentifier()) - ) - -class Extension(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('extnID', univ.ObjectIdentifier()), - namedtype.DefaultedNamedType('critical', univ.Boolean('False')), - namedtype.NamedType('extnValue', univ.OctetString()) - ) - -class Extensions(univ.SequenceOf): - componentType = Extension() - sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX) - -class SubjectPublicKeyInfo(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('algorithm', AlgorithmIdentifier()), - namedtype.NamedType('subjectPublicKey', univ.BitString()) - ) - -class UniqueIdentifier(univ.BitString): pass - -class Time(univ.Choice): - componentType = namedtype.NamedTypes( - namedtype.NamedType('utcTime', useful.UTCTime()), - namedtype.NamedType('generalTime', useful.GeneralizedTime()) - ) - -class Validity(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('notBefore', Time()), - namedtype.NamedType('notAfter', Time()) - ) - -class CertificateSerialNumber(univ.Integer): pass - -class Version(univ.Integer): - namedValues = namedval.NamedValues( - ('v1', 0), ('v2', 1), ('v3', 2) - ) - -class TBSCertificate(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.DefaultedNamedType('version', Version('v1', tagSet=Version.tagSet.tagExplicitly(tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))), - namedtype.NamedType('serialNumber', CertificateSerialNumber()), - namedtype.NamedType('signature', AlgorithmIdentifier()), - namedtype.NamedType('issuer', Name()), - namedtype.NamedType('validity', Validity()), - namedtype.NamedType('subject', Name()), - namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo()), - namedtype.OptionalNamedType('issuerUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), - namedtype.OptionalNamedType('subjectUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), - namedtype.OptionalNamedType('extensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))) - ) - -class Certificate(univ.Sequence): - componentType = namedtype.NamedTypes( - namedtype.NamedType('tbsCertificate', TBSCertificate()), - namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), - namedtype.NamedType('signatureValue', univ.BitString()) - ) - - def get_version(self): - info = self.getComponentByName('tbsCertificate') - version = info.getComponentByName('version') - return version._value - - def get_subject(self): - info = self.getComponentByName('tbsCertificate') - return info.getComponentByName('subject') - - def get_serial_number(self): - 'return the serial number as a Python long object' - info = self.getComponentByName('tbsCertificate') - return long(info.getComponentByName('serialNumber')) - -# end of ASN.1 data structures - def strip_header(pem): """ Remove the header and footer from a certificate. @@ -205,69 +37,53 @@ def strip_header(pem): return pem - -def load_certificate(data, type=PEM): +def load_certificate(data, datatype=PEM, dbdir=None): """ Given a base64-encoded certificate, with or without the header/footer, return a request object. + + Returns a nss.Certificate type """ - if (type == PEM): + if type(data) in (tuple, list): + data = data[0] + + if (datatype == PEM): data = strip_header(data) data = base64.b64decode(data) - return decoder.decode(data, asn1Spec=Certificate())[0] + if dbdir is None: + if api.env.in_tree: + dbdir = api.env.dot_ipa + os.sep + 'alias' + else: + dbdir = "/etc/httpd/alias" + + nss.nss_init(dbdir) + return nss.Certificate(buffer(data)) -def get_subject_components(certificate, type=PEM): +def get_subject(certificate, datatype=PEM): """ Load an X509.3 certificate and get the subject. - - Return a tuple of a certificate subject. - (('CN', u'www.example.com'), ('O', u'IPA')) """ - x509cert = load_certificate(certificate, type) - return x509cert.get_subject().get_components() + cert = load_certificate(certificate, datatype) + return cert.subject -def get_serial_number(certificate, type=PEM): +def get_serial_number(certificate, datatype=PEM): """ - Return the serial number of a certificate as a Python long object. + Return the decimal value of the serial number. """ - x509cert = load_certificate(certificate, type) - return x509cert.get_serial_number() + cert = load_certificate(certificate, datatype) + return cert.serial_number if __name__ == '__main__': - certType = Certificate() - - # Read PEM certs from stdin and print them out in plain text - - stSpam, stHam, stDump = 0, 1, 2 - state = stSpam - certCnt = 0 - for certLine in sys.stdin.readlines(): - certLine = string.strip(certLine) - if state == stSpam: - if state == stSpam: - if certLine == '-----BEGIN CERTIFICATE-----': - certLines = [] - state = stHam - continue - if state == stHam: - if certLine == '-----END CERTIFICATE-----': - state = stDump - else: - certLines.append(certLine) - if state == stDump: - substrate = '' - for certLine in certLines: - substrate = substrate + base64.b64decode(certLine) + nss.nss_init_nodb() - cert = decoder.decode(substrate, asn1Spec=certType)[0] - print cert.prettyPrint() + # Read PEM certs from stdin and print out its components - assert encoder.encode(cert) == substrate, 'cert recode fails' + certlines = sys.stdin.readlines() + cert = ''.join(certlines) - certCnt = certCnt + 1 - state = stSpam + cert = load_certificate(cert) - print '*** %s PEM cert(s) de/serialized' % certCnt + print cert |