summaryrefslogtreecommitdiffstats
path: root/ipalib/x509.py
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2010-06-24 11:40:02 -0400
committerRob Crittenden <rcritten@redhat.com>2010-07-15 10:51:49 -0400
commit8d2d7429beb6bf66cb3c4fc35a7a3dbb165a432c (patch)
treec364bfb5b5926a165f1e6bc29e355131636afe45 /ipalib/x509.py
parent1e1985b17c3988056bef045fa84a9c7aaf0c4c65 (diff)
downloadfreeipa-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.py288
1 files changed, 52 insertions, 236 deletions
diff --git a/ipalib/x509.py b/ipalib/x509.py
index 3c38a354..bb765faa 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