wbond / asn1crypto

Python ASN.1 library with a focus on performance and a pythonic API
MIT License
335 stars 140 forks source link

How to add support for custom OIDs in the x509 module #259

Open AndyCWB opened 1 year ago

AndyCWB commented 1 year ago

I'm working with a lot of MS issued certificates and one of the properties I need to expose is the certificate template. I'm trying to figure out how to modify the x509 code to add support for the extra value. I know the OID is 1.3.6.1.4.1.311.21.7, but I can't figure out what code changes are needed to add it as a property.

I've added a new property to the Certificate class (based on similar properties), and the OID to the NameType class, but I'm clearly missing something to get the field pulled out of the certificate data. Can someone point me in the right direction, or to a PR that does something similar?

joernheissler commented 1 year ago

It looks like 1.3.6.1.4.1.311.21.7 is an Extension: https://learn.microsoft.com/en-us/windows/win32/api/certenroll/nn-certenroll-ix509extensiontemplate You would have to write a class that extends core.Sequence and add it to x509.ExtensionId and x509.Extension. I'm not sure if every Extension should also be added as property to Certificate. E.g. I'm using custom (proprietery) extensions for some of my stuff. Might be better to restrict this to commonly used extensions and add a general accessor for others?

Also, I was thinking that it might be a good idea to define a function that registers a new Extension; something like:

class Extension(Sequence):
  ...

  @classmethod
  def register_extension(cls, name, oid, ext_cls):
    if name in cls._oid_specs:
      raise Exception("Duplicate extension name: " + name)
    if oid in ExtensionId._map:
      raise Exception("Duplicate extension oid: " + oid)
    cls._oid_specs[name] = ext_cls
    ExtensionId._map[oid] = name

class Certificate(Sequence):
  ...
  def get_extension(self, name):
    if not self._processed_extensions:
      self._set_extensions()  # Add code so the next line works
    return self._extensions[name]  # or return None instead of raising KeyError?

  def has_extension(self, name):
     ...
     return name in self._extensions

@wbond Do you think about this?