diff --git a/README.md b/README.md index b75b11c..c069a6c 100644 --- a/README.md +++ b/README.md @@ -109,12 +109,12 @@ trusted CA chain (see [Ingress Configuration](#4-ingress-configuration)). ### 3. Configuration Options -| Variable | Description | Default | -| -------------------------------- | ------------------------ | ------------------------------- | -| `ALLOWED_CLIENT_SUBJECT_DN_PATH` | Path to the file listing allowed client certificate DNs. | `/config/allowed_client_dn.txt` | -| `SSL_CLIENT_SUBJECT_DN_HEADER` | HTTP header containing the client certificate DN. For **ingress-nginx**, this should not be changed. | `ssl-client-subject-dn` | -| `USE_WATCHDOG` | Enables file-change monitoring using [watchdog](https://pypi.org/project/watchdog/). Useful for non-Kubernetes environments. | `False` | -| `LOG_LEVEL` | Logging verbosity. Options: `DEBUG`, `INFO`, `WARNING`, `ERROR`. | `INFO` | +| Variable | Description | Default | +| -------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------| ------------------------------- | +| `ALLOWED_CLIENT_SUBJECT_DN_PATH` | Path to the file listing allowed client certificate DNs. DNs should be as close to RFC4514 as possible, and stored as UTF-8 in the file. | `/config/allowed_client_dn.txt` | +| `SSL_CLIENT_SUBJECT_DN_HEADER` | HTTP header containing the client certificate DN. For **ingress-nginx**, this should not be changed. | `ssl-client-subject-dn` | +| `USE_WATCHDOG` | Enables file-change monitoring using [watchdog](https://pypi.org/project/watchdog/). Useful for non-Kubernetes environments. | `False` | +| `LOG_LEVEL` | Logging verbosity. Options: `DEBUG`, `INFO`, `WARNING`, `ERROR`. | `INFO` | **File reload behavior:** diff --git a/doc/oids.txt b/doc/oids.txt new file mode 100644 index 0000000..7d238cc --- /dev/null +++ b/doc/oids.txt @@ -0,0 +1,2119 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Netscape security libraries. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1994-2000 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** +CVS_ID "@(#) $RCSfile: oids.txt,v $ $Revision: 1.3 $ $Date: 2005/02/02 22:28:24 $" + +# Fields +# OID -- the OID data itself, in dotted-number format +# TAG -- the unofficial but common name. e.g., when written like +# { joint-iso-ccitt(2) country(16) US(840) company(1) netscape(113730) } +# those words ("joint-iso-ccitt," "country," etc.) are the tags. +# EXPL -- a textual explanation, that should stand by itself +# NAME -- the name we use in our code. If no NAME is given, it won't be included +# +# Additionally, some sets of OIDs map to some other spaces.. +# additional fields capture that data: +# +# CKM -- the corresponding Cryptoki Mechanism, if any +# CKK -- the corresponding Cryptoki Key type, if any +# ATTR -- the UTF-8 attribute type encoding, if applicable +# it should be in the standard presentation style (e.g., lowercase), +# already-escaped a la RFC 2253 if necessary (which would only +# be necessary if some idiot defined an attribute with, say, +# an equals sign in it) +# CERT_EXTENSION -- SUPPORTED or UNSUPPORTED certificate extension, if applicable + +# Top of the OID tree -- see http://www.alvestrand.no/objectid/top.html +# +OID 0 +TAG ccitt # ITU-T used to be called CCITT +EXPL "ITU-T" + +# See X.208 Annex C for an explanation of the OIDs below ccitt/itu-t + +OID 0.0 +TAG recommendation +EXPL "ITU-T Recommendation" + +OID 0.1 +TAG question +EXPL "ITU-T Question" + +OID 0.2 +TAG administration +EXPL "ITU-T Administration" + +OID 0.3 +TAG network-operator +EXPL "ITU-T Network Operator" + +OID 0.4 +TAG identified-organization +EXPL "ITU-T Identified Organization" + +OID 0.9 # used in some RFCs (unofficial!) +TAG data +EXPL "RFC Data" + +OID 0.9.2342 +TAG pss +EXPL "PSS British Telecom X.25 Network" + +OID 0.9.2342.19200300 +TAG ucl +EXPL "RFC 1274 UCL Data networks" + +OID 0.9.2342.19200300.100 +TAG pilot +EXPL "RFC 1274 pilot" + +OID 0.9.2342.19200300.100.1 +TAG attributeType +EXPL "RFC 1274 Attribute Type" + +OID 0.9.2342.19200300.100.1.1 +TAG uid +EXPL "RFC 1274 User Id" +NAME NSS_OID_RFC1274_UID +ATTR "uid" + +OID 0.9.2342.19200300.100.1.3 +TAG mail +EXPL "RFC 1274 E-mail Addres" +NAME NSS_OID_RFC1274_EMAIL +ATTR "mail" # XXX fgmr + +OID 0.9.2342.19200300.100.1.25 +TAG dc +EXPL "RFC 2247 Domain Component" +NAME NSS_OID_RFC2247_DC +ATTR "dc" + +OID 0.9.2342.19200300.100.3 +TAG attributeSyntax +EXPL "RFC 1274 Attribute Syntax" + +OID 0.9.2342.19200300.100.3.4 +TAG iA5StringSyntax +EXPL "RFC 1274 IA5 String Attribute Syntax" + +OID 0.9.2342.19200300.100.3.5 +TAG caseIgnoreIA5StringSyntax +EXPL "RFC 1274 Case-Ignore IA5 String Attribute Syntax" + +OID 0.9.2342.19200300.100.4 +TAG objectClass +EXPL "RFC 1274 Object Class" + +OID 0.9.2342.19200300.100.10 +TAG groups +EXPL "RFC 1274 Groups" + +OID 0.9.2342.234219200300 +TAG ucl +EXPL "RFC 1327 ucl" + +OID 1 +TAG iso +EXPL "ISO" + +# See X.208 Annex B for an explanation of the OIDs below iso + +OID 1.0 +TAG standard +EXPL "ISO Standard" + +OID 1.1 +TAG registration-authority +EXPL "ISO Registration Authority" + +OID 1.2 +TAG member-body +EXPL "ISO Member Body" + +OID 1.2.36 +TAG australia +EXPL "Australia (ISO)" + +OID 1.2.158 +TAG taiwan +EXPL "Taiwan (ISO)" + +OID 1.2.372 +TAG ireland +EXPL "Ireland (ISO)" + +OID 1.2.578 +TAG norway +EXPL "Norway (ISO)" + +OID 1.2.752 +TAG sweden +EXPL "Sweden (ISO)" + +OID 1.2.826 +TAG great-britain +EXPL "Great Britain (ISO)" + +OID 1.2.840 +TAG us +EXPL "United States (ISO)" + +OID 1.2.840.1 +TAG organization +EXPL "US (ISO) organization" + +OID 1.2.840.10003 +TAG ansi-z30-50 +EXPL "ANSI Z39.50" + +OID 1.2.840.10008 +TAG dicom +EXPL "DICOM" + +OID 1.2.840.10017 +TAG ieee-1224 +EXPL "IEEE 1224" + +OID 1.2.840.10022 +TAG ieee-802-10 +EXPL "IEEE 802.10" + +OID 1.2.840.10036 +TAG ieee-802-11 +EXPL "IEEE 802.11" + +OID 1.2.840.10040 +TAG x9-57 +EXPL "ANSI X9.57" + +# RFC 2459: +# +# holdInstruction OBJECT IDENTIFIER ::= +# {iso(1) member-body(2) us(840) x9cm(10040) 2} +# +# Note that the appendices of RFC 2459 define the (wrong) value +# of 2.2.840.10040.2 for this oid. +OID 1.2.840.10040.2 +TAG holdInstruction +EXPL "ANSI X9.57 Hold Instruction" + +# RFC 2459: +# +# id-holdinstruction-none OBJECT IDENTIFIER ::= +# {holdInstruction 1} -- deprecated +OID 1.2.840.10040.2.1 +TAG id-holdinstruction-none +EXPL "ANSI X9.57 Hold Instruction: None" + +# RFC 2459: +# +# id-holdinstruction-callissuer OBJECT IDENTIFIER ::= +# {holdInstruction 2} +OID 1.2.840.10040.2.2 +TAG id-holdinstruction-callissuer +EXPL "ANSI X9.57 Hold Instruction: Call Issuer" + +# RFC 2459: +# +# id-holdinstruction-reject OBJECT IDENTIFIER ::= +# {holdInstruction 3} +OID 1.2.840.10040.2.3 +TAG id-holdinstruction-reject +EXPL "ANSI X9.57 Hold Instruction: Reject" + +OID 1.2.840.10040.4 +TAG x9algorithm +EXPL "ANSI X9.57 Algorithm" + +# RFC 2459: +# +# id-dsa OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 } +OID 1.2.840.10040.4.1 +TAG id-dsa +EXPL "ANSI X9.57 DSA Signature" +NAME NSS_OID_ANSIX9_DSA_SIGNATURE +CKM CKM_DSA +CKK CKK_DSA + +# RFC 2459: +# +# id-dsa-with-sha1 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 } +OID 1.2.840.10040.4.3 +TAG id-dsa-with-sha1 +EXPL "ANSI X9.57 Algorithm DSA Signature with SHA-1 Digest" +NAME NSS_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST +CKM CKM_DSA_SHA1 + +OID 1.2.840.10046 +TAG x942 +EXPL "ANSI X9.42" + +OID 1.2.840.10046.2 +TAG algorithm +EXPL "ANSI X9.42 Algorithm" + +# RFC 2459: +# +# dhpublicnumber OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 } +OID 1.2.840.10046.2.1 +TAG dhpublicnumber +EXPL "Diffie-Hellman Public Key Algorithm" +NAME NSS_OID_X942_DIFFIE_HELMAN_KEY +CKM CKM_DH_PKCS_DERIVE +CKK CKK_DH + +OID 1.2.840.113533 +TAG entrust +EXPL "Entrust Technologies" + +OID 1.2.840.113549 +TAG rsadsi +EXPL "RSA Data Security Inc." + +OID 1.2.840.113549.1 +# http://www.rsa.com/rsalabs/pubs/PKCS/ +TAG pkcs +EXPL "PKCS" + +# RFC 2459: +# +# pkcs-1 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } +OID 1.2.840.113549.1.1 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-1.asc +TAG pkcs-1 +EXPL "PKCS #1" + +# RFC 2459: +# +# rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } +OID 1.2.840.113549.1.1.1 +TAG rsaEncryption +EXPL "PKCS #1 RSA Encryption" +NAME NSS_OID_PKCS1_RSA_ENCRYPTION +CKM CKM_RSA_PKCS +CKK CKK_RSA + +# RFC 2459: +# +# md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } +OID 1.2.840.113549.1.1.2 +TAG md2WithRSAEncryption +EXPL "PKCS #1 MD2 With RSA Encryption" +NAME NSS_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION +CKM CKM_MD2_RSA_PKCS + +OID 1.2.840.113549.1.1.3 +TAG md4WithRSAEncryption +EXPL "PKCS #1 MD4 With RSA Encryption" +NAME NSS_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION +# No CKM! + +# RFC 2459: +# +# md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } +OID 1.2.840.113549.1.1.4 +TAG md5WithRSAEncryption +EXPL "PKCS #1 MD5 With RSA Encryption" +NAME NSS_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION +CKM CKM_MD5_RSA_PKCS + +# RFC 2459: +# +# sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } +OID 1.2.840.113549.1.1.5 +TAG sha1WithRSAEncryption +EXPL "PKCS #1 SHA-1 With RSA Encryption" +NAME NSS_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION +CKM CKM_SHA1_RSA_PKCS + +OID 1.2.840.113549.1.5 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-5.asc +TAG pkcs-5 +EXPL "PKCS #5" + +OID 1.2.840.113549.1.5.1 +TAG pbeWithMD2AndDES-CBC +EXPL "PKCS #5 Password Based Encryption With MD2 and DES-CBC" +NAME NSS_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC +CKM CKM_PBE_MD2_DES_CBC + +OID 1.2.840.113549.1.5.3 +TAG pbeWithMD5AndDES-CBC +EXPL "PKCS #5 Password Based Encryption With MD5 and DES-CBC" +NAME NSS_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC +CKM CKM_PBE_MD5_DES_CBC + +OID 1.2.840.113549.1.5.10 +TAG pbeWithSha1AndDES-CBC +EXPL "PKCS #5 Password Based Encryption With SHA-1 and DES-CBC" +NAME NSS_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC +CKM CKM_NETSCAPE_PBE_SHA1_DES_CBC + +OID 1.2.840.113549.1.7 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-7.asc +TAG pkcs-7 +EXPL "PKCS #7" +NAME NSS_OID_PKCS7 + +OID 1.2.840.113549.1.7.1 +TAG data +EXPL "PKCS #7 Data" +NAME NSS_OID_PKCS7_DATA + +OID 1.2.840.113549.1.7.2 +TAG signedData +EXPL "PKCS #7 Signed Data" +NAME NSS_OID_PKCS7_SIGNED_DATA + +OID 1.2.840.113549.1.7.3 +TAG envelopedData +EXPL "PKCS #7 Enveloped Data" +NAME NSS_OID_PKCS7_ENVELOPED_DATA + +OID 1.2.840.113549.1.7.4 +TAG signedAndEnvelopedData +EXPL "PKCS #7 Signed and Enveloped Data" +NAME NSS_OID_PKCS7_SIGNED_ENVELOPED_DATA + +OID 1.2.840.113549.1.7.5 +TAG digestedData +EXPL "PKCS #7 Digested Data" +NAME NSS_OID_PKCS7_DIGESTED_DATA + +OID 1.2.840.113549.1.7.6 +TAG encryptedData +EXPL "PKCS #7 Encrypted Data" +NAME NSS_OID_PKCS7_ENCRYPTED_DATA + +# RFC 2459: +# +# pkcs-9 OBJECT IDENTIFIER ::= +# { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } +OID 1.2.840.113549.1.9 +# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-9.asc +TAG pkcs-9 +EXPL "PKCS #9" + +# RFC 2459: +# +# emailAddress AttributeType ::= { pkcs-9 1 } +OID 1.2.840.113549.1.9.1 +TAG emailAddress +EXPL "PKCS #9 Email Address" +NAME NSS_OID_PKCS9_EMAIL_ADDRESS + +OID 1.2.840.113549.1.9.2 +TAG unstructuredName +EXPL "PKCS #9 Unstructured Name" +NAME NSS_OID_PKCS9_UNSTRUCTURED_NAME + +OID 1.2.840.113549.1.9.3 +TAG contentType +EXPL "PKCS #9 Content Type" +NAME NSS_OID_PKCS9_CONTENT_TYPE + +OID 1.2.840.113549.1.9.4 +TAG messageDigest +EXPL "PKCS #9 Message Digest" +NAME NSS_OID_PKCS9_MESSAGE_DIGEST + +OID 1.2.840.113549.1.9.5 +TAG signingTime +EXPL "PKCS #9 Signing Time" +NAME NSS_OID_PKCS9_SIGNING_TIME + +OID 1.2.840.113549.1.9.6 +TAG counterSignature +EXPL "PKCS #9 Counter Signature" +NAME NSS_OID_PKCS9_COUNTER_SIGNATURE + +OID 1.2.840.113549.1.9.7 +TAG challengePassword +EXPL "PKCS #9 Challenge Password" +NAME NSS_OID_PKCS9_CHALLENGE_PASSWORD + +OID 1.2.840.113549.1.9.8 +TAG unstructuredAddress +EXPL "PKCS #9 Unstructured Address" +NAME NSS_OID_PKCS9_UNSTRUCTURED_ADDRESS + +OID 1.2.840.113549.1.9.9 +TAG extendedCertificateAttributes +EXPL "PKCS #9 Extended Certificate Attributes" +NAME NSS_OID_PKCS9_EXTENDED_CERTIFICATE_ATTRIBUTES + +OID 1.2.840.113549.1.9.15 +TAG sMIMECapabilities +EXPL "PKCS #9 S/MIME Capabilities" +NAME NSS_OID_PKCS9_SMIME_CAPABILITIES + +OID 1.2.840.113549.1.9.20 +TAG friendlyName +EXPL "PKCS #9 Friendly Name" +NAME NSS_OID_PKCS9_FRIENDLY_NAME + +OID 1.2.840.113549.1.9.21 +TAG localKeyID +EXPL "PKCS #9 Local Key ID" +NAME NSS_OID_PKCS9_LOCAL_KEY_ID + +OID 1.2.840.113549.1.9.22 +TAG certTypes +EXPL "PKCS #9 Certificate Types" + +OID 1.2.840.113549.1.9.22.1 +TAG x509Certificate +EXPL "PKCS #9 Certificate Type = X.509" +NAME NSS_OID_PKCS9_X509_CERT + +OID 1.2.840.113549.1.9.22.2 +TAG sdsiCertificate +EXPL "PKCS #9 Certificate Type = SDSI" +NAME NSS_OID_PKCS9_SDSI_CERT + +OID 1.2.840.113549.1.9.23 +TAG crlTypes +EXPL "PKCS #9 Certificate Revocation List Types" + +OID 1.2.840.113549.1.9.23.1 +TAG x509Crl +EXPL "PKCS #9 CRL Type = X.509" +NAME NSS_OID_PKCS9_X509_CRL + +OID 1.2.840.113549.1.12 +# http://www.rsa.com/rsalabs/pubs/PKCS/html/pkcs-12.html +# NOTE -- PKCS #12 multiple contradictory +# documents exist. Somebody go figure out the canonical +# OID list, keeping in mind backwards-compatability.. +TAG pkcs-12 +EXPL "PKCS #12" +NAME NSS_OID_PKCS12 + +OID 1.2.840.113549.1.12.1 +TAG pkcs-12PbeIds +EXPL "PKCS #12 Password Based Encryption IDs" +NAME NSS_OID_PKCS12_PBE_IDS +# We called it SEC_OID_PKCS12_MODE_IDS + +OID 1.2.840.113549.1.12.1.1 +TAG pbeWithSHA1And128BitRC4 +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 128-bit RC4" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4 +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4 +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4 + +OID 1.2.840.113549.1.12.1.2 +TAG pbeWithSHA1And40BitRC4 +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 40-bit RC4" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4 +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4 +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4 + +OID 1.2.840.113549.1.12.1.3 +TAG pbeWithSHA1And3-KeyTripleDES-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 3-key Triple DES-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_3_KEY_TRIPLE_DES_CBC +CKM CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC + +OID 1.2.840.113549.1.12.1.4 +TAG pbeWithSHA1And2-KeyTripleDES-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 2-key Triple DES-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_2_KEY_TRIPLE_DES_CBC +CKM CKM_PBE_SHA1_DES2_EDE_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC + +OID 1.2.840.113549.1.12.1.5 +TAG pbeWithSHA1And128BitRC2-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 128-bit RC2-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.1.6 +TAG pbeWithSHA1And40BitRC2-CBC +EXPL "PKCS #12 Password Based Encryption With SHA-1 and 40-bit RC2-CBC" +NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.2 +TAG pkcs-12EspvkIds +EXPL "PKCS #12 ESPVK IDs" +# We called it SEC_OID_PKCS12_ESPVK_IDS + +OID 1.2.840.113549.1.12.2.1 +TAG pkcs8-key-shrouding +EXPL "PKCS #12 Key Shrouding" +# We called it SEC_OID_PKCS12_PKCS8_KEY_SHROUDING + +OID 1.2.840.113549.1.12.3 +TAG draft1Pkcs-12Bag-ids +EXPL "Draft 1.0 PKCS #12 Bag IDs" +# We called it SEC_OID_PKCS12_BAG_IDS + +OID 1.2.840.113549.1.12.3.1 +TAG keyBag +EXPL "Draft 1.0 PKCS #12 Key Bag" +# We called it SEC_OID_PKCS12_KEY_BAG_ID + +OID 1.2.840.113549.1.12.3.2 +TAG certAndCRLBagId +EXPL "Draft 1.0 PKCS #12 Cert and CRL Bag ID" +# We called it SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID + +OID 1.2.840.113549.1.12.3.3 +TAG secretBagId +EXPL "Draft 1.0 PKCS #12 Secret Bag ID" +# We called it SEC_OID_PKCS12_SECRET_BAG_ID + +OID 1.2.840.113549.1.12.3.4 +TAG safeContentsId +EXPL "Draft 1.0 PKCS #12 Safe Contents Bag ID" +# We called it SEC_OID_PKCS12_SAFE_CONTENTS_ID + +OID 1.2.840.113549.1.12.3.5 +TAG pkcs-8ShroudedKeyBagId +EXPL "Draft 1.0 PKCS #12 PKCS #8-shrouded Key Bag ID" +# We called it SEC_OID_PKCS12_PKCS8_SHROUDED_KEY_BAG_ID + +OID 1.2.840.113549.1.12.4 +TAG pkcs-12CertBagIds +EXPL "PKCS #12 Certificate Bag IDs" +# We called it SEC_OID_PKCS12_CERT_BAG_IDS + +OID 1.2.840.113549.1.12.4.1 +TAG x509CertCRLBagId +EXPL "PKCS #12 X.509 Certificate and CRL Bag" +# We called it SEC_OID_PKCS12_X509_CERT_CRL_BAG + +OID 1.2.840.113549.1.12.4.2 +TAG SDSICertBagID +EXPL "PKCS #12 SDSI Certificate Bag" +# We called it SEC_OID_PKCS12_SDSI_CERT_BAG + +OID 1.2.840.113549.1.12.5 +TAG pkcs-12Oids +EXPL "PKCS #12 OIDs (XXX)" +# We called it SEC_OID_PKCS12_OIDS + +OID 1.2.840.113549.1.12.5.1 +TAG pkcs-12PbeIds +EXPL "PKCS #12 OIDs PBE IDs (XXX)" +# We called it SEC_OID_PKCS12_PBE_IDS + +OID 1.2.840.113549.1.12.5.1.1 +TAG pbeWithSha1And128BitRC4 +EXPL "PKCS #12 OIDs PBE with SHA-1 and 128-bit RC4 (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4 +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4 + +OID 1.2.840.113549.1.12.5.1.2 +TAG pbeWithSha1And40BitRC4 +EXPL "PKCS #12 OIDs PBE with SHA-1 and 40-bit RC4 (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4 +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4 + +OID 1.2.840.113549.1.12.5.1.3 +TAG pbeWithSha1AndTripleDES-CBC +EXPL "PKCS #12 OIDs PBE with SHA-1 and Triple DES-CBC (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC + +OID 1.2.840.113549.1.12.5.1.4 +TAG pbeWithSha1And128BitRC2-CBC +EXPL "PKCS #12 OIDs PBE with SHA-1 and 128-bit RC2-CBC (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.5.1.5 +TAG pbeWithSha1And40BitRC2-CBC +EXPL "PKCS #12 OIDs PBE with SHA-1 and 40-bit RC2-CBC (XXX)" +CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC +# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC + +OID 1.2.840.113549.1.12.5.2 +TAG pkcs-12EnvelopingIds +EXPL "PKCS #12 OIDs Enveloping IDs (XXX)" +# We called it SEC_OID_PKCS12_ENVELOPING_IDS + +OID 1.2.840.113549.1.12.5.2.1 +TAG rsaEncryptionWith128BitRC4 +EXPL "PKCS #12 OIDs Enveloping RSA Encryption with 128-bit RC4" +# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_128_BIT_RC4 + +OID 1.2.840.113549.1.12.5.2.2 +TAG rsaEncryptionWith40BitRC4 +EXPL "PKCS #12 OIDs Enveloping RSA Encryption with 40-bit RC4" +# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_40_BIT_RC4 + +OID 1.2.840.113549.1.12.5.2.3 +TAG rsaEncryptionWithTripleDES +EXPL "PKCS #12 OIDs Enveloping RSA Encryption with Triple DES" +# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_TRIPLE_DES + +OID 1.2.840.113549.1.12.5.3 +TAG pkcs-12SignatureIds +EXPL "PKCS #12 OIDs Signature IDs (XXX)" +# We called it SEC_OID_PKCS12_SIGNATURE_IDS + +OID 1.2.840.113549.1.12.5.3.1 +TAG rsaSignatureWithSHA1Digest +EXPL "PKCS #12 OIDs RSA Signature with SHA-1 Digest" +# We called it SEC_OID_PKCS12_RSA_SIGNATURE_WITH_SHA1_DIGEST + +OID 1.2.840.113549.1.12.10 +TAG pkcs-12Version1 +EXPL "PKCS #12 Version 1" + +OID 1.2.840.113549.1.12.10.1 +TAG pkcs-12BagIds +EXPL "PKCS #12 Bag IDs" + +OID 1.2.840.113549.1.12.10.1.1 +TAG keyBag +EXPL "PKCS #12 Key Bag" +NAME NSS_OID_PKCS12_KEY_BAG +# We called it SEC_OID_PKCS12_V1_KEY_BAG_ID + +OID 1.2.840.113549.1.12.10.1.2 +TAG pkcs-8ShroudedKeyBag +EXPL "PKCS #12 PKCS #8-shrouded Key Bag" +NAME NSS_OID_PKCS12_PKCS8_SHROUDED_KEY_BAG +# We called it SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID + +OID 1.2.840.113549.1.12.10.1.3 +TAG certBag +EXPL "PKCS #12 Certificate Bag" +NAME NSS_OID_PKCS12_CERT_BAG +# We called it SEC_OID_PKCS12_V1_CERT_BAG_ID + +OID 1.2.840.113549.1.12.10.1.4 +TAG crlBag +EXPL "PKCS #12 CRL Bag" +NAME NSS_OID_PKCS12_CRL_BAG +# We called it SEC_OID_PKCS12_V1_CRL_BAG_ID + +OID 1.2.840.113549.1.12.10.1.5 +TAG secretBag +EXPL "PKCS #12 Secret Bag" +NAME NSS_OID_PKCS12_SECRET_BAG +# We called it SEC_OID_PKCS12_V1_SECRET_BAG_ID + +OID 1.2.840.113549.1.12.10.1.6 +TAG safeContentsBag +EXPL "PKCS #12 Safe Contents Bag" +NAME NSS_OID_PKCS12_SAFE_CONTENTS_BAG +# We called it SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID + +OID 1.2.840.113549.2 +TAG digest +EXPL "RSA digest algorithm" + +OID 1.2.840.113549.2.2 +TAG md2 +EXPL "MD2" +NAME NSS_OID_MD2 +CKM CKM_MD2 + +OID 1.2.840.113549.2.4 +TAG md4 +EXPL "MD4" +NAME NSS_OID_MD4 +# No CKM + +OID 1.2.840.113549.2.5 +TAG md5 +EXPL "MD5" +NAME NSS_OID_MD5 +CKM CKM_MD5 + +OID 1.2.840.113549.3 +TAG cipher +EXPL "RSA cipher algorithm" + +OID 1.2.840.113549.3.2 +TAG rc2cbc +EXPL "RC2-CBC" +NAME NSS_OID_RC2_CBC +CKM_RC2_CBC + +OID 1.2.840.113549.3.4 +TAG rc4 +EXPL "RC4" +NAME NSS_OID_RC4 +CKM CKM_RC4 + +OID 1.2.840.113549.3.7 +TAG desede3cbc +EXPL "DES-EDE3-CBC" +NAME NSS_OID_DES_EDE3_CBC +CKM CKM_DES3_CBC + +OID 1.2.840.113549.3.9 +TAG rc5cbcpad +EXPL "RC5-CBCPad" +NAME NSS_OID_RC5_CBC_PAD +CKM CKM_RC5_CBC + +OID 1.2.840.113556 +TAG microsoft +EXPL "Microsoft" + +OID 1.2.840.113560 +TAG columbia-university +EXPL "Columbia University" + +OID 1.2.840.113572 +TAG unisys +EXPL "Unisys" + +OID 1.2.840.113658 +TAG xapia +EXPL "XAPIA" + +OID 1.2.840.113699 +TAG wordperfect +EXPL "WordPerfect" + +OID 1.3 +TAG identified-organization +EXPL "ISO identified organizations" + +OID 1.3.6 +TAG us-dod +EXPL "United States Department of Defense" + +OID 1.3.6.1 +TAG internet # See RFC 1065 +EXPL "The Internet" + +OID 1.3.6.1.1 +TAG directory +EXPL "Internet: Directory" + +OID 1.3.6.1.2 +TAG management +EXPL "Internet: Management" + +OID 1.3.6.1.3 +TAG experimental +EXPL "Internet: Experimental" + +OID 1.3.6.1.4 +TAG private +EXPL "Internet: Private" + +OID 1.3.6.1.5 +TAG security +EXPL "Internet: Security" + +OID 1.3.6.1.5.5 + +# RFC 2459: +# +# id-pkix OBJECT IDENTIFIER ::= +# { iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) } +OID 1.3.6.1.5.5.7 +TAG id-pkix +EXPL "Public Key Infrastructure" + +# RFC 2459: +# +# PKIX1Explicit88 {iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit-88(1)} +OID 1.3.6.1.5.5.7.0.1 +TAG PKIX1Explicit88 +EXPL "RFC 2459 Explicitly Tagged Module, 1988 Syntax" + +# RFC 2459: +# +# PKIX1Implicit88 {iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit-88(2)} +OID 1.3.6.1.5.5.7.0.2 +TAG PKIXImplicit88 +EXPL "RFC 2459 Implicitly Tagged Module, 1988 Syntax" + +# RFC 2459: +# +# PKIX1Explicit93 {iso(1) identified-organization(3) dod(6) internet(1) +# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit-93(3)} +OID 1.3.6.1.5.5.7.0.3 +TAG PKIXExplicit93 +EXPL "RFC 2459 Explicitly Tagged Module, 1993 Syntax" + +# RFC 2459: +# +# id-pe OBJECT IDENTIFIER ::= { id-pkix 1 } +# -- arc for private certificate extensions +OID 1.3.6.1.5.5.7.1 +TAG id-pe +EXPL "PKIX Private Certificate Extensions" + +# RFC 2459: +# +# id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 } +OID 1.3.6.1.5.5.7.1.1 +TAG id-pe-authorityInfoAccess +EXPL "Certificate Authority Information Access" +NAME NSS_OID_X509_AUTH_INFO_ACCESS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } +# -- arc for policy qualifier types +OID 1.3.6.1.5.5.7.2 +TAG id-qt +EXPL "PKIX Policy Qualifier Types" + +# RFC 2459: +# +# id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } +# -- OID for CPS qualifier +OID 1.3.6.1.5.5.7.2.1 +TAG id-qt-cps +EXPL "PKIX CPS Pointer Qualifier" +NAME NSS_OID_PKIX_CPS_POINTER_QUALIFIER + +# RFC 2459: +# +# id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } +# -- OID for user notice qualifier +OID 1.3.6.1.5.5.7.2.2 +TAG id-qt-unotice +EXPL "PKIX User Notice Qualifier" +NAME NSS_OID_PKIX_USER_NOTICE_QUALIFIER + +# RFC 2459: +# +# id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } +# -- arc for extended key purpose OIDS +OID 1.3.6.1.5.5.7.3 +TAG id-kp +EXPL "PKIX Key Purpose" + +# RFC 2459: +# +# id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } +OID 1.3.6.1.5.5.7.3.1 +TAG id-kp-serverAuth +EXPL "TLS Web Server Authentication Certificate" +NAME NSS_OID_EXT_KEY_USAGE_SERVER_AUTH + +# RFC 2459: +# +# id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } +OID 1.3.6.1.5.5.7.3.2 +TAG id-kp-clientAuth +EXPL "TLS Web Client Authentication Certificate" +NAME NSS_OID_EXT_KEY_USAGE_CLIENT_AUTH + +# RFC 2459: +# +# id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } +OID 1.3.6.1.5.5.7.3.3 +TAG id-kp-codeSigning +EXPL "Code Signing Certificate" +NAME NSS_OID_EXT_KEY_USAGE_CODE_SIGN + +# RFC 2459: +# +# id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } +OID 1.3.6.1.5.5.7.3.4 +TAG id-kp-emailProtection +EXPL "E-Mail Protection Certificate" +NAME NSS_OID_EXT_KEY_USAGE_EMAIL_PROTECTION + +# RFC 2459: +# +# id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 } +OID 1.3.6.1.5.5.7.3.5 +TAG id-kp-ipsecEndSystem +EXPL "IPSEC End System Certificate" +NAME NSS_OID_EXT_KEY_USAGE_IPSEC_END_SYSTEM + +# RFC 2459: +# +# id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 } +OID 1.3.6.1.5.5.7.3.6 +TAG id-kp-ipsecTunnel +EXPL "IPSEC Tunnel Certificate" +NAME NSS_OID_EXT_KEY_USAGE_IPSEC_TUNNEL + +# RFC 2459: +# +# id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 } +OID 1.3.6.1.5.5.7.3.7 +TAG id-kp-ipsecUser +EXPL "IPSEC User Certificate" +NAME NSS_OID_EXT_KEY_USAGE_IPSEC_USER + +# RFC 2459: +# +# id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } +OID 1.3.6.1.5.5.7.3.8 +TAG id-kp-timeStamping +EXPL "Time Stamping Certificate" +NAME NSS_OID_EXT_KEY_USAGE_TIME_STAMP + +OID 1.3.6.1.5.5.7.3.9 +TAG ocsp-responder +EXPL "OCSP Responder Certificate" +NAME NSS_OID_OCSP_RESPONDER + +OID 1.3.6.1.5.5.7.7 +TAG pkix-id-pkix + +OID 1.3.6.1.5.5.7.7.5 +TAG pkix-id-pkip + +OID 1.3.6.1.5.5.7.7.5.1 +TAG pkix-id-regctrl +EXPL "CRMF Registration Control" + +OID 1.3.6.1.5.5.7.7.5.1.1 +TAG regtoken +EXPL "CRMF Registration Control, Registration Token" +NAME NSS_OID_PKIX_REGCTRL_REGTOKEN + +OID 1.3.6.1.5.5.7.7.5.1.2 +TAG authenticator +EXPL "CRMF Registration Control, Registration Authenticator" +NAME NSS_OID_PKIX_REGCTRL_AUTHENTICATOR + +OID 1.3.6.1.5.5.7.7.5.1.3 +TAG pkipubinfo +EXPL "CRMF Registration Control, PKI Publication Info" +NAME NSS_OID_PKIX_REGCTRL_PKIPUBINFO + +OID 1.3.6.1.5.5.7.7.5.1.4 +TAG pki-arch-options +EXPL "CRMF Registration Control, PKI Archive Options" +NAME NSS_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS + +OID 1.3.6.1.5.5.7.7.5.1.5 +TAG old-cert-id +EXPL "CRMF Registration Control, Old Certificate ID" +NAME NSS_OID_PKIX_REGCTRL_OLD_CERT_ID + +OID 1.3.6.1.5.5.7.7.5.1.6 +TAG protocol-encryption-key +EXPL "CRMF Registration Control, Protocol Encryption Key" +NAME NSS_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY + +OID 1.3.6.1.5.5.7.7.5.2 +TAG pkix-id-reginfo +EXPL "CRMF Registration Info" + +OID 1.3.6.1.5.5.7.7.5.2.1 +TAG utf8-pairs +EXPL "CRMF Registration Info, UTF8 Pairs" +NAME NSS_OID_PKIX_REGINFO_UTF8_PAIRS + +OID 1.3.6.1.5.5.7.7.5.2.2 +TAG cert-request +EXPL "CRMF Registration Info, Certificate Request" +NAME NSS_OID_PKIX_REGINFO_CERT_REQUEST + +# RFC 2549: +# +# id-ad OBJECT IDENTIFIER ::= { id-pkix 48 } +# -- arc for access descriptors +OID 1.3.6.1.5.5.7.48 +TAG id-ad +EXPL "PKIX Access Descriptors" + +# RFC 2549: +# +# id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 } +OID 1.3.6.1.5.5.7.48.1 +TAG id-ad-ocsp +EXPL "PKIX Online Certificate Status Protocol" +NAME NSS_OID_OID_PKIX_OCSP + +OID 1.3.6.1.5.5.7.48.1.1 +TAG basic-response +EXPL "OCSP Basic Response" +NAME NSS_OID_PKIX_OCSP_BASIC_RESPONSE + +OID 1.3.6.1.5.5.7.48.1.2 +TAG nonce-extension +EXPL "OCSP Nonce Extension" +NAME NSS_OID_PKIX_OCSP_NONCE + +OID 1.3.6.1.5.5.7.48.1.3 +TAG response +EXPL "OCSP Response Types Extension" +NAME NSS_OID_PKIX_OCSP_RESPONSE + +OID 1.3.6.1.5.5.7.48.1.4 +TAG crl +EXPL "OCSP CRL Reference Extension" +NAME NSS_OID_PKIX_OCSP_CRL + +OID 1.3.6.1.5.5.7.48.1.5 +TAG no-check +EXPL "OCSP No Check Extension" +NAME NSS_OID_X509_OCSP_NO_CHECK # X509_... ? + +OID 1.3.6.1.5.5.7.48.1.6 +TAG archive-cutoff +EXPL "OCSP Archive Cutoff Extension" +NAME NSS_OID_PKIX_OCSP_ARCHIVE_CUTOFF + +OID 1.3.6.1.5.5.7.48.1.7 +TAG service-locator +EXPL "OCSP Service Locator Extension" +NAME NSS_OID_PKIX_OCSP_SERVICE_LOCATOR + +# RFC 2549: +# +# id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 } +OID 1.3.6.1.5.5.7.48.2 +TAG id-ad-caIssuers +EXPL "Certificate Authority Issuers" + +OID 1.3.6.1.6 +TAG snmpv2 +EXPL "Internet: SNMPv2" + +OID 1.3.6.1.7 +TAG mail +EXPL "Internet: mail" + +OID 1.3.6.1.7.1 +TAG mime-mhs +EXPL "Internet: mail MIME mhs" + +OID 1.3.12 +TAG ecma +EXPL "European Computers Manufacturing Association" + +OID 1.3.14 +TAG oiw +EXPL "Open Systems Implementors Workshop" + +OID 1.3.14.3 secsig +TAG secsig +EXPL "Open Systems Implementors Workshop Security Special Interest Group" + +OID 1.3.14.3.1 +TAG oIWSECSIGAlgorithmObjectIdentifiers +EXPL "OIW SECSIG Algorithm OIDs" + +OID 1.3.14.3.2 +TAG algorithm +EXPL "OIW SECSIG Algorithm" + +OID 1.3.14.3.2.6 +TAG desecb +EXPL "DES-ECB" +NAME NSS_OID_DES_ECB +CKM CKM_DES_ECB + +OID 1.3.14.3.2.7 +TAG descbc +EXPL "DES-CBC" +NAME NSS_OID_DES_CBC +CKM CKM_DES_CBC + +OID 1.3.14.3.2.8 +TAG desofb +EXPL "DES-OFB" +NAME NSS_OID_DES_OFB +# No CKM.. + +OID 1.3.14.3.2.9 +TAG descfb +EXPL "DES-CFB" +NAME NSS_OID_DES_CFB +# No CKM.. + +OID 1.3.14.3.2.10 +TAG desmac +EXPL "DES-MAC" +NAME NSS_OID_DES_MAC +CKM CKM_DES_MAC + +OID 1.3.14.3.2.15 +TAG isoSHAWithRSASignature +EXPL "ISO SHA with RSA Signature" +NAME NSS_OID_ISO_SHA_WITH_RSA_SIGNATURE +# No CKM.. + +OID 1.3.14.3.2.17 +TAG desede +EXPL "DES-EDE" +NAME NSS_OID_DES_EDE +# No CKM.. + +OID 1.3.14.3.2.26 +TAG sha1 +EXPL "SHA-1" +NAME NSS_OID_SHA1 +CKM CKM_SHA_1 + +OID 1.3.14.3.2.27 +TAG bogusDSASignatureWithSHA1Digest +EXPL "Forgezza DSA Signature with SHA-1 Digest" +NAME NSS_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST +CKM CKM_DSA_SHA1 + +OID 1.3.14.3.3 +TAG authentication-mechanism +EXPL "OIW SECSIG Authentication Mechanisms" + +OID 1.3.14.3.4 +TAG security-attribute +EXPL "OIW SECSIG Security Attributes" + +OID 1.3.14.3.5 +TAG document-definition +EXPL "OIW SECSIG Document Definitions used in security" + +OID 1.3.14.7 +TAG directory-services-sig +EXPL "OIW directory services sig" + +OID 1.3.16 +TAG ewos +EXPL "European Workshop on Open Systems" + +OID 1.3.22 +TAG osf +EXPL "Open Software Foundation" + +OID 1.3.23 +TAG nordunet +EXPL "Nordunet" + +OID 1.3.26 +TAG nato-id-org +EXPL "NATO identified organisation" + +OID 1.3.36 +TAG teletrust +EXPL "Teletrust" + +OID 1.3.52 +TAG smpte +EXPL "Society of Motion Picture and Television Engineers" + +OID 1.3.69 +TAG sita +EXPL "Societe Internationale de Telecommunications Aeronautiques" + +OID 1.3.90 +TAG iana +EXPL "Internet Assigned Numbers Authority" + +OID 1.3.101 +TAG thawte +EXPL "Thawte" + +OID 2 +TAG joint-iso-ccitt +EXPL "Joint ISO/ITU-T assignment" + +OID 2.0 +TAG presentation +EXPL "Joint ISO/ITU-T Presentation" + +OID 2.1 +TAG asn-1 +EXPL "Abstract Syntax Notation One" + +OID 2.2 +TAG acse +EXPL "Association Control" + +OID 2.3 +TAG rtse +EXPL "Reliable Transfer" + +OID 2.4 +TAG rose +EXPL "Remote Operations" + +OID 2.5 +TAG x500 +EXPL "Directory" + +OID 2.5.1 +TAG modules +EXPL "X.500 modules" + +OID 2.5.2 +TAG service-environment +EXPL "X.500 service environment" + +OID 2.5.3 +TAG application-context +EXPL "X.500 application context" + +# RFC 2459: +# +# id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} +OID 2.5.4 +TAG id-at +EXPL "X.520 attribute types" + +# RFC 2459: +# +# id-at-commonName AttributeType ::= {id-at 3} +OID 2.5.4.3 +TAG id-at-commonName +EXPL "X.520 Common Name" +NAME NSS_OID_X520_COMMON_NAME +ATTR "cn" + +# RFC 2459: +# +# id-at-surname AttributeType ::= {id-at 4} +OID 2.5.4.4 +TAG id-at-surname +EXPL "X.520 Surname" +NAME NSS_OID_X520_SURNAME +ATTR "sn" + +# RFC 2459: +# +# id-at-countryName AttributeType ::= {id-at 6} +OID 2.5.4.6 +TAG id-at-countryName +EXPL "X.520 Country Name" +NAME NSS_OID_X520_COUNTRY_NAME +ATTR "c" + +# RFC 2459: +# +# id-at-localityName AttributeType ::= {id-at 7} +OID 2.5.4.7 +TAG id-at-localityName +EXPL "X.520 Locality Name" +NAME NSS_OID_X520_LOCALITY_NAME +ATTR "l" + +# RFC 2459: +# +# id-at-stateOrProvinceName AttributeType ::= {id-at 8} +OID 2.5.4.8 +TAG id-at-stateOrProvinceName +EXPL "X.520 State or Province Name" +NAME NSS_OID_X520_STATE_OR_PROVINCE_NAME +ATTR "s" + +# RFC 2459: +# +# id-at-organizationName AttributeType ::= {id-at 10} +OID 2.5.4.10 +TAG id-at-organizationName +EXPL "X.520 Organization Name" +NAME NSS_OID_X520_ORGANIZATION_NAME +ATTR "o" + +# RFC 2459: +# +# id-at-organizationalUnitName AttributeType ::= {id-at 11} +OID 2.5.4.11 +TAG id-at-organizationalUnitName +EXPL "X.520 Organizational Unit Name" +NAME NSS_OID_X520_ORGANIZATIONAL_UNIT_NAME +ATTR "ou" + +# RFC 2459: +# +# id-at-title AttributeType ::= {id-at 12} +OID 2.5.4.12 +TAG id-at-title +EXPL "X.520 Title" +NAME NSS_OID_X520_TITLE +ATTR "title" + +# RFC 2459: +# +# id-at-name AttributeType ::= {id-at 41} +OID 2.5.4.41 +TAG id-at-name +EXPL "X.520 Name" +NAME NSS_OID_X520_NAME +ATTR "name" + +# RFC 2459: +# +# id-at-givenName AttributeType ::= {id-at 42} +OID 2.5.4.42 +TAG id-at-givenName +EXPL "X.520 Given Name" +NAME NSS_OID_X520_GIVEN_NAME +ATTR "givenName" + +# RFC 2459: +# +# id-at-initials AttributeType ::= {id-at 43} +OID 2.5.4.43 +TAG id-at-initials +EXPL "X.520 Initials" +NAME NSS_OID_X520_INITIALS +ATTR "initials" + +# RFC 2459: +# +# id-at-generationQualifier AttributeType ::= {id-at 44} +OID 2.5.4.44 +TAG id-at-generationQualifier +EXPL "X.520 Generation Qualifier" +NAME NSS_OID_X520_GENERATION_QUALIFIER +ATTR "generationQualifier" + +# RFC 2459: +# +# id-at-dnQualifier AttributeType ::= {id-at 46} +OID 2.5.4.46 +TAG id-at-dnQualifier +EXPL "X.520 DN Qualifier" +NAME NSS_OID_X520_DN_QUALIFIER +ATTR "dnQualifier" + +OID 2.5.5 +TAG attribute-syntax +EXPL "X.500 attribute syntaxes" + +OID 2.5.6 +TAG object-classes +EXPL "X.500 standard object classes" + +OID 2.5.7 +TAG attribute-set +EXPL "X.500 attribute sets" + +OID 2.5.8 +TAG algorithms +EXPL "X.500-defined algorithms" + +OID 2.5.8.1 +TAG encryption +EXPL "X.500-defined encryption algorithms" + +OID 2.5.8.1.1 +TAG rsa +EXPL "RSA Encryption Algorithm" +NAME NSS_OID_X500_RSA_ENCRYPTION +CKM CKM_RSA_X_509 + +OID 2.5.9 +TAG abstract-syntax +EXPL "X.500 abstract syntaxes" + +OID 2.5.12 +TAG operational-attribute +EXPL "DSA Operational Attributes" + +OID 2.5.13 +TAG matching-rule +EXPL "Matching Rule" + +OID 2.5.14 +TAG knowledge-matching-rule +EXPL "X.500 knowledge Matching Rules" + +OID 2.5.15 +TAG name-form +EXPL "X.500 name forms" + +OID 2.5.16 +TAG group +EXPL "X.500 groups" + +OID 2.5.17 +TAG subentry +EXPL "X.500 subentry" + +OID 2.5.18 +TAG operational-attribute-type +EXPL "X.500 operational attribute type" + +OID 2.5.19 +TAG operational-binding +EXPL "X.500 operational binding" + +OID 2.5.20 +TAG schema-object-class +EXPL "X.500 schema Object class" + +OID 2.5.21 +TAG schema-operational-attribute +EXPL "X.500 schema operational attributes" + +OID 2.5.23 +TAG administrative-role +EXPL "X.500 administrative roles" + +OID 2.5.24 +TAG access-control-attribute +EXPL "X.500 access control attribute" + +OID 2.5.25 +TAG ros +EXPL "X.500 ros object" + +OID 2.5.26 +TAG contract +EXPL "X.500 contract" + +OID 2.5.27 +TAG package +EXPL "X.500 package" + +OID 2.5.28 +TAG access-control-schema +EXPL "X.500 access control schema" + +# RFC 2459: +# +# id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} +OID 2.5.29 +TAG id-ce +EXPL "X.500 Certificate Extension" + +OID 2.5.29.5 +TAG subject-directory-attributes +EXPL "Certificate Subject Directory Attributes" +NAME NSS_OID_X509_SUBJECT_DIRECTORY_ATTR +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } +OID 2.5.29.9 +TAG id-ce-subjectDirectoryAttributes +EXPL "Certificate Subject Directory Attributes" +NAME NSS_OID_X509_SUBJECT_DIRECTORY_ATTRIBUTES +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } +OID 2.5.29.14 +TAG id-ce-subjectKeyIdentifier +EXPL "Certificate Subject Key ID" +NAME NSS_OID_X509_SUBJECT_KEY_ID +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } +OID 2.5.29.15 +TAG id-ce-keyUsage +EXPL "Certificate Key Usage" +NAME NSS_OID_X509_KEY_USAGE +CERT_EXTENSION SUPPORTED +# We called it PKCS12_KEY_USAGE + +# RFC 2459: +# +# id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 } +OID 2.5.29.16 +TAG id-ce-privateKeyUsagePeriod +EXPL "Certificate Private Key Usage Period" +NAME NSS_OID_X509_PRIVATE_KEY_USAGE_PERIOD +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } +OID 2.5.29.17 +TAG id-ce-subjectAltName +EXPL "Certificate Subject Alternate Name" +NAME NSS_OID_X509_SUBJECT_ALT_NAME +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } +OID 2.5.29.18 +TAG id-ce-issuerAltName +EXPL "Certificate Issuer Alternate Name" +NAME NSS_OID_X509_ISSUER_ALT_NAME +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } +OID 2.5.29.19 +TAG id-ce-basicConstraints +EXPL "Certificate Basic Constraints" +NAME NSS_OID_X509_BASIC_CONSTRAINTS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } +OID 2.5.29.20 +TAG id-ce-cRLNumber +EXPL "CRL Number" +NAME NSS_OID_X509_CRL_NUMBER +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 } +OID 2.5.29.21 +TAG id-ce-cRLReasons +EXPL "CRL Reason Code" +NAME NSS_OID_X509_REASON_CODE +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 } +OID 2.5.29.23 +TAG id-ce-holdInstructionCode +EXPL "Hold Instruction Code" +NAME NSS_OID_X509_HOLD_INSTRUCTION_CODE +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 } +OID 2.5.29.24 +TAG id-ce-invalidityDate +EXPL "Invalid Date" +NAME NSS_OID_X509_INVALID_DATE +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } +OID 2.5.29.27 +TAG id-ce-deltaCRLIndicator +EXPL "Delta CRL Indicator" +NAME NSS_OID_X509_DELTA_CRL_INDICATOR +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } +OID 2.5.29.28 +TAG id-ce-issuingDistributionPoint +EXPL "Issuing Distribution Point" +NAME NSS_OID_X509_ISSUING_DISTRIBUTION_POINT +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } +OID 2.5.29.29 +TAG id-ce-certificateIssuer +EXPL "Certificate Issuer" +NAME NSS_OID_X509_CERTIFICATE_ISSUER +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } +OID 2.5.29.30 +TAG id-ce-nameConstraints +EXPL "Certificate Name Constraints" +NAME NSS_OID_X509_NAME_CONSTRAINTS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31} +OID 2.5.29.31 +TAG id-ce-cRLDistributionPoints +EXPL "CRL Distribution Points" +NAME NSS_OID_X509_CRL_DIST_POINTS +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } +OID 2.5.29.32 +TAG id-ce-certificatePolicies +EXPL "Certificate Policies" +NAME NSS_OID_X509_CERTIFICATE_POLICIES +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } +OID 2.5.29.33 +TAG id-ce-policyMappings +EXPL "Certificate Policy Mappings" +NAME NSS_OID_X509_POLICY_MAPPINGS +CERT_EXTENSION UNSUPPORTED + +OID 2.5.29.34 +TAG policy-constraints +EXPL "Certificate Policy Constraints (old)" +CERT_EXTENSION UNSUPPORTED + +# RFC 2459: +# +# id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } +OID 2.5.29.35 +TAG id-ce-authorityKeyIdentifier +EXPL "Certificate Authority Key Identifier" +NAME NSS_OID_X509_AUTH_KEY_ID +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } +OID 2.5.29.36 +TAG id-ce-policyConstraints +EXPL "Certificate Policy Constraints" +NAME NSS_OID_X509_POLICY_CONSTRAINTS +CERT_EXTENSION SUPPORTED + +# RFC 2459: +# +# id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37} +OID 2.5.29.37 +TAG id-ce-extKeyUsage +EXPL "Extended Key Usage" +NAME NSS_OID_X509_EXT_KEY_USAGE +CERT_EXTENSION SUPPORTED + +OID 2.5.30 +TAG id-mgt +EXPL "X.500 Management Object" + +OID 2.6 +TAG x400 +EXPL "X.400 MHS" + +OID 2.7 +TAG ccr +EXPL "Committment, Concurrency and Recovery" + +OID 2.8 +TAG oda +EXPL "Office Document Architecture" + +OID 2.9 +TAG osi-management +EXPL "OSI management" + +OID 2.10 +TAG tp +EXPL "Transaction Processing" + +OID 2.11 +TAG dor +EXPL "Distinguished Object Reference" + +OID 2.12 +TAG rdt +EXPL "Referenced Data Transfer" + +OID 2.13 +TAG nlm +EXPL "Network Layer Management" + +OID 2.14 +TAG tlm +EXPL "Transport Layer Management" + +OID 2.15 +TAG llm +EXPL "Link Layer Management" + +OID 2.16 +TAG country +EXPL "Country Assignments" + +OID 2.16.124 +TAG canada +EXPL "Canada" + +OID 2.16.158 +TAG taiwan +EXPL "Taiwan" + +OID 2.16.578 +TAG norway +EXPL "Norway" + +OID 2.16.756 +TAG switzerland +EXPL "Switzerland" + +OID 2.16.840 +TAG us +EXPL "United States" + +OID 2.16.840.1 +TAG us-company +EXPL "United States Company" + +OID 2.16.840.1.101 +TAG us-government +EXPL "United States Government (1.101)" + +OID 2.16.840.1.101.2 +TAG us-dod +EXPL "United States Department of Defense" + +OID 2.16.840.1.101.2.1 +TAG id-infosec +EXPL "US DOD Infosec" + +OID 2.16.840.1.101.2.1.0 +TAG id-modules +EXPL "US DOD Infosec modules" + +OID 2.16.840.1.101.2.1.1 +TAG id-algorithms +EXPL "US DOD Infosec algorithms (MISSI)" + +OID 2.16.840.1.101.2.1.1.2 +TAG old-dss +EXPL "MISSI DSS Algorithm (Old)" +NAME NSS_OID_MISSI_DSS_OLD + +# This is labeled as "### mwelch temporary" +# Is it official?? XXX fgmr +OID 2.16.840.1.101.2.1.1.4 +TAG skipjack-cbc-64 +EXPL "Skipjack CBC64" +NAME NSS_OID_FORTEZZA_SKIPJACK +CKM CKM_SKIPJACK_CBC64 + +OID 2.16.840.1.101.2.1.1.10 +TAG kea +EXPL "MISSI KEA Algorithm" +NAME NSS_OID_MISSI_KEA + +OID 2.16.840.1.101.2.1.1.12 +TAG old-kea-dss +EXPL "MISSI KEA and DSS Algorithm (Old)" +NAME NSS_OID_MISSI_KEA_DSS_OLD + +OID 2.16.840.1.101.2.1.1.19 +TAG dss +EXPL "MISSI DSS Algorithm" +NAME NSS_OID_MISSI_DSS + +OID 2.16.840.1.101.2.1.1.20 +TAG kea-dss +EXPL "MISSI KEA and DSS Algorithm" +NAME NSS_OID_MISSI_KEA_DSS + +OID 2.16.840.1.101.2.1.1.22 +TAG alt-kea +EXPL "MISSI Alternate KEA Algorithm" +NAME NSS_OID_MISSI_ALT_KEY + +OID 2.16.840.1.101.2.1.2 +TAG id-formats +EXPL "US DOD Infosec formats" + +OID 2.16.840.1.101.2.1.3 +TAG id-policy +EXPL "US DOD Infosec policy" + +OID 2.16.840.1.101.2.1.4 +TAG id-object-classes +EXPL "US DOD Infosec object classes" + +OID 2.16.840.1.101.2.1.5 +TAG id-attributes +EXPL "US DOD Infosec attributes" + +OID 2.16.840.1.101.2.1.6 +TAG id-attribute-syntax +EXPL "US DOD Infosec attribute syntax" + +OID 2.16.840.1.113730 +# The Netscape OID space +TAG netscape +EXPL "Netscape Communications Corp." + +OID 2.16.840.1.113730.1 +TAG cert-ext +EXPL "Netscape Cert Extensions" + +OID 2.16.840.1.113730.1.1 +TAG cert-type +EXPL "Certificate Type" +NAME NSS_OID_NS_CERT_EXT_CERT_TYPE +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.2 +TAG base-url +EXPL "Certificate Extension Base URL" +NAME NSS_OID_NS_CERT_EXT_BASE_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.3 +TAG revocation-url +EXPL "Certificate Revocation URL" +NAME NSS_OID_NS_CERT_EXT_REVOCATION_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.4 +TAG ca-revocation-url +EXPL "Certificate Authority Revocation URL" +NAME NSS_OID_NS_CERT_EXT_CA_REVOCATION_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.5 +TAG ca-crl-download-url +EXPL "Certificate Authority CRL Download URL" +NAME NSS_OID_NS_CERT_EXT_CA_CRL_URL +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.6 +TAG ca-cert-url +EXPL "Certificate Authority Certificate Download URL" +NAME NSS_OID_NS_CERT_EXT_CA_CERT_URL +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.7 +TAG renewal-url +EXPL "Certificate Renewal URL" +NAME NSS_OID_NS_CERT_EXT_CERT_RENEWAL_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.8 +TAG ca-policy-url +EXPL "Certificate Authority Policy URL" +NAME NSS_OID_NS_CERT_EXT_CA_POLICY_URL +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.9 +TAG homepage-url +EXPL "Certificate Homepage URL" +NAME NSS_OID_NS_CERT_EXT_HOMEPAGE_URL +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.10 +TAG entity-logo +EXPL "Certificate Entity Logo" +NAME NSS_OID_NS_CERT_EXT_ENTITY_LOGO +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.11 +TAG user-picture +EXPL "Certificate User Picture" +NAME NSS_OID_NS_CERT_EXT_USER_PICTURE +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.1.12 +TAG ssl-server-name +EXPL "Certificate SSL Server Name" +NAME NSS_OID_NS_CERT_EXT_SSL_SERVER_NAME +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.13 +TAG comment +EXPL "Certificate Comment" +NAME NSS_OID_NS_CERT_EXT_COMMENT +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.1.14 +TAG thayes +EXPL "" +NAME NSS_OID_NS_CERT_EXT_THAYES +CERT_EXTENSION SUPPORTED + +OID 2.16.840.1.113730.2 +TAG data-type +EXPL "Netscape Data Types" + +OID 2.16.840.1.113730.2.1 +TAG gif +EXPL "image/gif" +NAME NSS_OID_NS_TYPE_GIF + +OID 2.16.840.1.113730.2.2 +TAG jpeg +EXPL "image/jpeg" +NAME NSS_OID_NS_TYPE_JPEG + +OID 2.16.840.1.113730.2.3 +TAG url +EXPL "URL" +NAME NSS_OID_NS_TYPE_URL + +OID 2.16.840.1.113730.2.4 +TAG html +EXPL "text/html" +NAME NSS_OID_NS_TYPE_HTML + +OID 2.16.840.1.113730.2.5 +TAG cert-sequence +EXPL "Certificate Sequence" +NAME NSS_OID_NS_TYPE_CERT_SEQUENCE + +OID 2.16.840.1.113730.3 +# The Netscape Directory OID space +TAG directory +EXPL "Netscape Directory" + +OID 2.16.840.1.113730.4 +TAG policy +EXPL "Netscape Policy Type OIDs" + +OID 2.16.840.1.113730.4.1 +TAG export-approved +EXPL "Strong Crypto Export Approved" +NAME NSS_OID_NS_KEY_USAGE_GOVT_APPROVED +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.1.113730.5 +TAG cert-server +EXPL "Netscape Certificate Server" + +OID 2.16.840.1.113730.5.1 + +OID 2.16.840.1.113730.5.1.1 +TAG recovery-request +EXPL "Netscape Cert Server Recovery Request" +NAME NSS_OID_NETSCAPE_RECOVERY_REQUEST + +OID 2.16.840.1.113730.6 +TAG algs +EXPL "Netscape algorithm OIDs" + +OID 2.16.840.1.113730.6.1 +TAG smime-kea +EXPL "Netscape S/MIME KEA" +NAME NSS_OID_NETSCAPE_SMIME_KEA + +OID 2.16.840.1.113730.7 +TAG name-components +EXPL "Netscape Name Components" + +OID 2.16.840.1.113730.7.1 +TAG nickname +EXPL "Netscape Nickname" +NAME NSS_OID_NETSCAPE_NICKNAME + +OID 2.16.840.1.113733 +TAG verisign +EXPL "Verisign" + +OID 2.16.840.1.113733.1 + +OID 2.16.840.1.113733.1.7 + +OID 2.16.840.1.113733.1.7.1 + +OID 2.16.840.1.113733.1.7.1.1 +TAG verisign-user-notices +EXPL "Verisign User Notices" +NAME NSS_OID_VERISIGN_USER_NOTICES + +OID 2.16.840.101 +TAG us-government +EXPL "US Government (101)" + +OID 2.16.840.102 +TAG us-government2 +EXPL "US Government (102)" + +OID 2.16.840.11370 +TAG old-netscape +EXPL "Netscape Communications Corp. (Old)" + +OID 2.16.840.11370.1 +TAG ns-cert-ext +EXPL "Netscape Cert Extensions (Old NS)" + +OID 2.16.840.11370.1.1 +TAG netscape-ok +EXPL "Netscape says this cert is ok (Old NS)" +NAME NSS_OID_NS_CERT_EXT_NETSCAPE_OK +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.11370.1.2 +TAG issuer-logo +EXPL "Certificate Issuer Logo (Old NS)" +NAME NSS_OID_NS_CERT_EXT_ISSUER_LOGO +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.11370.1.3 +TAG subject-logo +EXPL "Certificate Subject Logo (Old NS)" +NAME NSS_OID_NS_CERT_EXT_SUBJECT_LOGO +CERT_EXTENSION UNSUPPORTED + +OID 2.16.840.11370.2 +TAG ns-file-type +EXPL "Netscape File Type" + +OID 2.16.840.11370.3 +TAG ns-image-type +EXPL "Netscape Image Type" + +OID 2.17 +TAG registration-procedures +EXPL "Registration procedures" + +OID 2.18 +TAG physical-layer-management +EXPL "Physical layer Management" + +OID 2.19 +TAG mheg +EXPL "MHEG" + +OID 2.20 +TAG guls +EXPL "Generic Upper Layer Security" + +OID 2.21 +TAG tls +EXPL "Transport Layer Security Protocol" + +OID 2.22 +TAG nls +EXPL "Network Layer Security Protocol" + +OID 2.23 +TAG organization +EXPL "International organizations" diff --git a/more-tests-cert/HOWTO.txt b/more-tests-cert/HOWTO.txt new file mode 100644 index 0000000..d06478a --- /dev/null +++ b/more-tests-cert/HOWTO.txt @@ -0,0 +1 @@ +Running these tests somehow requires PYTHONPATH to be set to the parent dir. diff --git a/more-tests-cert/__init__.py b/more-tests-cert/__init__.py new file mode 100644 index 0000000..80591da --- /dev/null +++ b/more-tests-cert/__init__.py @@ -0,0 +1,12 @@ +# Copyright 2026 SURF. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/more-tests-cert/conftest.py b/more-tests-cert/conftest.py new file mode 100644 index 0000000..27a46ed --- /dev/null +++ b/more-tests-cert/conftest.py @@ -0,0 +1,52 @@ +# Copyright 2026 SURF. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from collections.abc import Generator +from pathlib import Path + +from flask import Flask +from flask.testing import FlaskClient +from pytest import MonkeyPatch, fixture + + +@fixture +def allowed_client_dn(tmp_path: Path) -> Path: + """Create a temporary file for testing client DNs.""" + path = tmp_path / "settings.json" + # Arno: someone did not use openssl x509 -nameopt rfc2253 + content = ("CN=Good CA,O=Test Certificates 2011,C=US\n") # fmt: skip + path.write_text(content, encoding="utf-8") + return path + + +@fixture +def application(allowed_client_dn: Path, monkeypatch: MonkeyPatch) -> Generator[Flask, None, None]: + """Create and configure a new app instance for each test.""" + monkeypatch.setenv("allowed_client_subject_dn_path", str(allowed_client_dn)) + # These tests are for Cert-based auth + monkeypatch.setenv("tls_client_subject_authn_header", str("X-Forwarded-Tls-Client-Cert")) + + from nsi_auth import app + + app.config.update( + { + "TESTING": True, # Propagates exceptions to the test suite + # "allowed_client_subject_dn_path": allowed_client_dn + } + ) + yield app + + +@fixture +def client(application: Flask) -> FlaskClient: + """A test client for the application instance.""" + return application.test_client() diff --git a/more-tests-cert/functional/test_application.py b/more-tests-cert/functional/test_application.py new file mode 100644 index 0000000..be0af84 --- /dev/null +++ b/more-tests-cert/functional/test_application.py @@ -0,0 +1,75 @@ +# Copyright 2026 SURF. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from flask.testing import FlaskClient + +# From: https://raw.githubusercontent.com/pyca/cryptography/refs/heads/main/docs/x509/reference.rst +trust_anchor_example_cert_pem_bytes = b""" +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJVUzEf +MB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEVMBMGA1UEAxMMVHJ1c3Qg +QW5jaG9yMB4XDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAwMFowQDELMAkGA1UE +BhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExEDAOBgNVBAMT +B0dvb2QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQWJpHYo37 +Xfb7oJSPe+WvfTlzIG21WQ7MyMbGtK/m8mejCzR6c+f/pJhEH/OcDSMsXq8h5kXa +BGqWK+vSwD/Pzp5OYGptXmGPcthDtAwlrafkGOS4GqIJ8+k9XGKs+vQUXJKsOk47 +RuzD6PZupq4s16xaLVqYbUC26UcY08GpnoLNHJZS/EmXw1ZZ3d4YZjNlpIpWFNHn +UGmdiGKXUPX/9H0fVjIAaQwjnGAbpgyCumWgzIwPpX+ElFOUr3z7BoVnFKhIXze+ +VmQGSWxZxvWDUN90Ul0tLEpLgk3OVxUB4VUGuf15OJOpgo1xibINPmWt14Vda2N9 +yrNKloJGZNqLAgMBAAGjfDB6MB8GA1UdIwQYMBaAFOR9X9FclYYILAWuvnW2ZafZ +XahmMB0GA1UdDgQWBBRYAYQkG7wrUpRKPaUQchRR9a86yTAOBgNVHQ8BAf8EBAMC +AQYwFwYDVR0gBBAwDjAMBgpghkgBZQMCATABMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBADWHlxbmdTXNwBL/llwhQqwnazK7CC2WsXBBqgNPWj7m +tvQ+aLG8/50Qc2Sun7o2VnwF9D18UUe8Gj3uPUYH+oSI1vDdyKcjmMbKRU4rk0eo +3UHNDXwqIVc9CQS9smyV+x1HCwL4TTrq+LXLKx/qVij0Yqk+UJfAtrg2jnYKXsCu +FMBQQnWCGrwa1g1TphRp/RmYHnMynYFmZrXtzFz+U9XEA7C+gPq4kqDI/iVfIT1s +6lBtdB50lrDVwl2oYfAvW/6sC2se2QleZidUmrziVNP4oEeXINokU6T6p//HM1FG +QYw2jOvpKcKtWCSAnegEbgsGYzATKjmPJPJ0npHFqzM= +-----END CERTIFICATE----- +""".strip() + +# HTTP/Traefik cannot carry newlines and compresses +trust_anchor_example_cert_pem_str = trust_anchor_example_cert_pem_bytes.decode("iso-8859-1") +trust_anchor_example_cert_pem_str = trust_anchor_example_cert_pem_str.replace("-----BEGIN CERTIFICATE-----", "") +trust_anchor_example_cert_pem_str = trust_anchor_example_cert_pem_str.replace("-----END CERTIFICATE-----", "") +trust_anchor_example_cert_pem_str = trust_anchor_example_cert_pem_str.replace("\n", "") + +def test_root_not_found(client: FlaskClient) -> None: + """Verify that the root endpoint returns 404.""" + response = client.get("/") + assert response.status_code == 404 + + +def test_validate_without_cert_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 403 without Cert header.""" + response = client.get("/validate") + assert response.status_code == 403 + + +def test_validate_with_valid_cert_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 200 with correct Cert header.""" + headers = { + "X-Forwarded-Tls-Client-Cert": trust_anchor_example_cert_pem_str, + } + response = client.get("/validate", headers=headers) + assert response.status_code == 200 + + +def test_validate_with_invalid_cert_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 403 with incorrect DN header.""" + bad_pem_str = trust_anchor_example_cert_pem_str.replace("M","Z") + headers = { + "X-Forwarded-Tls-Client-Cert": bad_pem_str, + } + response = client.get("/validate", headers=headers) + assert response.status_code == 403 + diff --git a/more-tests/HOWTO.txt b/more-tests/HOWTO.txt new file mode 100644 index 0000000..d06478a --- /dev/null +++ b/more-tests/HOWTO.txt @@ -0,0 +1 @@ +Running these tests somehow requires PYTHONPATH to be set to the parent dir. diff --git a/more-tests/__init__.py b/more-tests/__init__.py new file mode 100644 index 0000000..80591da --- /dev/null +++ b/more-tests/__init__.py @@ -0,0 +1,12 @@ +# Copyright 2026 SURF. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/more-tests/conftest.py b/more-tests/conftest.py new file mode 100644 index 0000000..714b5b5 --- /dev/null +++ b/more-tests/conftest.py @@ -0,0 +1,52 @@ +# Copyright 2026 SURF. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from collections.abc import Generator +from pathlib import Path + +from flask import Flask +from flask.testing import FlaskClient +from pytest import MonkeyPatch, fixture + + +@fixture +def allowed_client_dn(tmp_path: Path) -> Path: + """Create a temporary file for testing client DNs.""" + path = tmp_path / "settings.json" + # Arno: someone did not use openssl x509 -nameopt rfc2253 + content = ("C=ZZ,O=Company Y,OU=Dept X,CN=CertA\n" + "CN=CertB,OU=Dept X,O=Company Y,C=ZZ\n") # fmt: skip + path.write_text(content, encoding="utf-8") + return path + + +@fixture +def application(allowed_client_dn: Path, monkeypatch: MonkeyPatch) -> Generator[Flask, None, None]: + """Create and configure a new app instance for each test.""" + monkeypatch.setenv("allowed_client_subject_dn_path", str(allowed_client_dn)) + # These tests are for NGINX Header-based auth + monkeypatch.setenv("tls_client_subject_authn_header", str("ssl-client-subject-dn")) + from nsi_auth import app + + app.config.update( + { + "TESTING": True, # Propagates exceptions to the test suite + # "allowed_client_subject_dn_path": allowed_client_dn + } + ) + yield app + + +@fixture +def client(application: Flask) -> FlaskClient: + """A test client for the application instance.""" + return application.test_client() diff --git a/more-tests/functional/test_application.py b/more-tests/functional/test_application.py new file mode 100644 index 0000000..886df3e --- /dev/null +++ b/more-tests/functional/test_application.py @@ -0,0 +1,55 @@ +# Copyright 2026 SURF. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from flask.testing import FlaskClient + + +def test_root_not_found(client: FlaskClient) -> None: + """Verify that the root endpoint returns 404.""" + response = client.get("/") + assert response.status_code == 404 + + +def test_validate_without_dn_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 403 without DN header.""" + response = client.get("/validate") + assert response.status_code == 403 + + +def test_validate_with_valid_dn_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 200 with correct DN header.""" + # According to https://datatracker.ietf.org/doc/html/rfc4517#section-3.3.4 + # Country is "the two-character codes from ISO 3166". ZZ is a user-assigned element :-) + # https://www.iso.org/obp/ui/#iso:pub:PUB500001:en + headers = { + "ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=ZZ", + } + response = client.get("/validate", headers=headers) + assert response.status_code == 200 + + +def test_validate_with_invalid_dn_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 403 with incorrect DN header.""" + headers = { + "ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=ZZZZZZZZZZ", + } + response = client.get("/validate", headers=headers) + assert response.status_code == 403 + +def test_validate_wrong_order_dn_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 403 with incorrect DN header. + header value must be in RFC2253 notation.""" + headers = { + "ssl-client-subject-dn": "C=ZZ,O=Company Y,OU=Dept X,CN=CertA", + } + response = client.get("/validate", headers=headers) + assert response.status_code == 403 diff --git a/nsi_auth.py b/nsi_auth.py index 423b910..159833c 100644 --- a/nsi_auth.py +++ b/nsi_auth.py @@ -10,37 +10,61 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# TODO: +# - Support other HTTP proxies than NGINX + Traefik, most can send full cert. +# - Test karl + CANARIE wildcard +# """Verify DN from HTTP header against list of allowed DN's.""" - -import base64 -import re import threading +import traceback from logging.config import dictConfig from typing import Callable from urllib.parse import unquote, unquote_plus - from cryptography import x509 -from cryptography.x509.oid import NameOID from flask import Flask, request from pydantic import BaseModel, FilePath from pydantic_settings import BaseSettings from watchdog.events import FileModifiedEvent, FileSystemEvent, FileSystemEventHandler from watchdog.observers import Observer -# OID not included in cryptography.x509.oid.NameOID -OID_ORGANIZATION_IDENTIFIER = x509.ObjectIdentifier("2.5.4.97") +import rfc4514_cmp + +# Client TLS certificate Subject DistinguishedName as HTTPS Header: +# ----------------------------------------------------------------- +# Kubernetes ingress NGINX's annotation: https://github.com/kubernetes/ingress-nginx/blob/main/docs/user-guide/nginx-configuration/annotations.md +# defined as 'The subject information of the client certificate. Example: "CN=My Client"' +# If we ASSUME this is the $ssl_client_s_dn variable from ngx_http_ssl_module then this is +# defined as (https://nginx.org/en/docs/http/ngx_http_ssl_module.html): +# '$ssl_client_s_dn' returns the “subject DN” string of the client certificate for an +# established SSL connection according to RFC 2253 (1.11.6);' +# So RFC2253 format. Note that itself is obsoleted by RFC4514, so NGINX has work to do. +# +K8S_NGINX_TLS_CLIENT_SUBJECT_DN_HEADER = "ssl-client-subject-dn" + +# Full Client TLS certificate as HTTPS Header: +# -------------------------------------------- +# For Traefik: +# * https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/passtlsclientcert/ +# * ``that contains the pem.'' +# * https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/passtlsclientcert/#pem +# * ``The delimiters and \n will be removed. +# * If there are more than one certificate, they are separated by a ",".'' +# * More elaborate: +# * https://doc.traefik.io/traefik/v2.1/middlewares/passtlsclientcert/ +# * ``In the example, it is the part between -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- delimiters :'' +# +# * k8s ingress Traefik uses this header, see https://doc.traefik.io/traefik/v1.7/configuration/backends/kubernetes/#general-annotations +# ARNOTODO: find out if Traefik will send multiple certs if a chain is presented, see e.g. +# Internet2's example PEM cert (or is that some form of key chain / key store) + +K8S_TRAEFIK_TLS_CLIENT_CERT_HEADER = "X-Forwarded-Tls-Client-Cert" -_OID_SHORT_NAMES = { - NameOID.COUNTRY_NAME: "C", - NameOID.STATE_OR_PROVINCE_NAME: "ST", - NameOID.LOCALITY_NAME: "L", - NameOID.ORGANIZATION_NAME: "O", - NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", - NameOID.COMMON_NAME: "CN", - NameOID.SERIAL_NUMBER: "serialNumber", - NameOID.EMAIL_ADDRESS: "emailAddress", - OID_ORGANIZATION_IDENTIFIER: "organizationIdentifier", -} +# Traefik can also send the subject info from the cert: +# * https://doc.traefik.io/traefik/reference/routing-configuration/http/middlewares/passtlsclientcert/ +# * ``X-Forwarded-Tls-Client-Cert-Info header value is a string that has been escaped in order to be a valid URL query.'' +# +K8S_TRAEFIK_TLS_CLIENT_SUBJECT_DN_HEADER = "X-Forwarded-Tls-Client-Cert-Info" # @@ -49,16 +73,26 @@ class Settings(BaseSettings): """Application settings.""" + # ASSUME: DNs in this file are not necessarily in a X.509 DN normal form, so we'll parse + # flexibly. File MUST be in UTF-8 encoding, following RFC4514. allowed_client_subject_dn_path: FilePath = FilePath("/config/allowed_client_dn.txt") - ssl_client_subject_dn_header: str = "ssl-client-subject-dn" + + # This setting determines behaviour, one of K8S*_HEADER, see above. + # If a cert header, then we check using full cert, otherwise passed subject DN. + tls_client_subject_authn_header: str = K8S_NGINX_TLS_CLIENT_SUBJECT_DN_HEADER + use_watchdog: bool = False log_level: str = "INFO" -class State(BaseModel): +# State cannot inherit from BaseModel, as x509.name.Name does not play nice with PyDantic +# Alternative is a DataClass, or no parent. Latter chosen. +class State: """Application state.""" - - allowed_client_subject_dn: list[str] = [] + # Note: if we inherit from BaseModel we get "Unable to generate pydantic-core + # schema for ." + # So we do not inherit ;o) + allowed_client_subject_dn_names: list[x509.name.Name] = [] def init_app() -> Flask: @@ -92,109 +126,79 @@ def init_app() -> Flask: state = State() app = init_app() - -def _escape_dn_value(value: str) -> str: - """Escape special characters in a DN attribute value per RFC 4514.""" - value = value.replace("\\", "\\\\") - for ch in (",", "+", '"', "<", ">", ";"): - value = value.replace(ch, f"\\{ch}") - if value.startswith("#"): - value = "\\" + value - if value.startswith(" "): - value = "\\ " + value[1:] - if value.endswith(" "): - value = value[:-1] + "\\ " - return value - - -def extract_dn_from_pem_header(header_value: str) -> str | None: - """Extract DN from Traefik's X-Forwarded-Tls-Client-Cert header (URL-encoded PEM). - - Parses the full certificate to access all subject fields including - organizationIdentifier (OID 2.5.4.97) and emailAddress (OID 1.2.840.113549.1.9.1). - Returns a normalized DN string in DER field order, or None on parse failure. - """ - try: - # Traefik strips newlines from the PEM before URL-encoding (to prevent header injection), - # so load_pem_x509_certificate would fail on the re-assembled string. Instead, extract - # the base64 between the PEM markers and load as DER. - # Use unquote (not unquote_plus) to preserve '+' characters valid in base64. - pem_str = unquote(header_value) - b64 = re.sub(r"-----[^-]+-----", "", pem_str).replace(" ", "") - cert = x509.load_der_x509_certificate(base64.b64decode(b64)) - except Exception as e: - app.logger.warning(f"failed to parse PEM from X-Forwarded-Tls-Client-Cert: {e!s}") - return None - - parts = [ - f"{_OID_SHORT_NAMES.get(attr.oid, attr.oid.dotted_string)}={_escape_dn_value(attr.value)}" - for attr in cert.subject - ] - return ",".join(parts) - - -def extract_dn_from_traefik_header(header_value: str) -> str | None: - """Extract DN from Traefik's X-Forwarded-Tls-Client-Cert-Info header. - - Traefik format: Subject="CN=...,O=...,C=..." - Returns the DN string without the Subject="" wrapper, or None if not found. - """ - # Match Subject="..." pattern and extract the DN - # Traefik URL-encodes the header value, so decode it first - match = re.search(r'Subject="([^"]+)"', unquote_plus(header_value)) - if match: - return match.group(1) - return None - - -def get_client_dn() -> tuple[str | None, str]: +# Arno: pydantic cannot handle x509.Name +def get_client_dn(): ### -> tuple[str | None, str]: """Extract client DN from request headers. Priority order: 1. X-Forwarded-Tls-Client-Cert (Traefik PEM - all subject fields) 2. X-Forwarded-Tls-Client-Cert-Info (Traefik Info - limited fields) - 3. ssl-client-subject-dn or configured header (nginx fallback) + 3. ssl-client-subject-dn (nginx fallback) Returns: - Tuple of (dn, source) where source indicates which header was used. + Tuple of (x509.Name, source) where source indicates which header was used. """ - pem_header = request.headers.get("X-Forwarded-Tls-Client-Cert") - if pem_header: - dn = extract_dn_from_pem_header(pem_header) - if dn: - return dn, "traefik-pem" - - traefik_header = request.headers.get("X-Forwarded-Tls-Client-Cert-Info") - if traefik_header: - dn = extract_dn_from_traefik_header(traefik_header) - if dn: - return dn, "traefik" - - nginx_header = request.headers.get(settings.ssl_client_subject_dn_header) - if nginx_header: - return nginx_header, "nginx" - - return None, "none" + try: + # https://werkzeug.palletsprojects.com/en/stable/datastructures/#werkzeug.datastructures.Headers + # .get() returns str + + # Risk: if ingress does DN header, and malicous actor sends cert header, and the + # proxy does not strip it, we would trust the cert header. So check only the + # header from settings. + # + authn_header_val = request.headers.get(settings.tls_client_subject_authn_header) + if not authn_header_val: + return None, "missing" + + if settings.tls_client_subject_authn_header == K8S_TRAEFIK_TLS_CLIENT_CERT_HEADER: + # If Traefik this is in PEM with some changes, see above + if ',' in authn_header_val: + app.logger.warning( + f"multiple certificates in {K8S_TRAEFIK_TLS_CLIENT_CERT_HEADER} header on HTTP request, unsupported") + return None, "traefik-pem-multiple-certificates" + + dn = rfc4514_cmp.subject_dn_from_traefik_cert_pem(authn_header_val) + if dn: + return dn, "traefik-pem" + + elif settings.tls_client_subject_authn_header == K8S_TRAEFIK_TLS_CLIENT_SUBJECT_DN_HEADER: + dn = rfc4514_cmp.subject_dn_from_traefik_cert_info(authn_header_val) + if dn: + return dn, "traefik-info" + + elif settings.tls_client_subject_authn_header == K8S_NGINX_TLS_CLIENT_SUBJECT_DN_HEADER: + dn = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name(authn_header_val) + if dn: + return dn, "nginx" + else: + # Default no name, higher layer logs and sends 403. + return None, "none" + + except ValueError as e: + return None, str(e) @app.route("/validate", methods=["GET"]) def validate() -> tuple[str, int]: """Verify the DN from the packet header against the list of allowed DN.""" - dn, source = get_client_dn() + request_rfc4514_name, source = get_client_dn() - if not dn: + if request_rfc4514_name is None: app.logger.warning( - f"no client DN found in headers (tried X-Forwarded-Tls-Client-Cert, " - f"X-Forwarded-Tls-Client-Cert-Info, {settings.ssl_client_subject_dn_header})" + f"Missing authorization header or incorrect value: {settings.tls_client_subject_authn_header}: {source}" ) return "Forbidden", 403 - if dn not in state.allowed_client_subject_dn: - app.logger.info(f"deny {dn} (from {source} header)") - return "Forbidden", 403 + # *** Main authentication line *** + # x509.Name object equals method does comparison + for allowed_dn_name in state.allowed_client_subject_dn_names: - app.logger.info(f"allow {dn} (from {source} header)") - return "OK", 200 + if request_rfc4514_name == allowed_dn_name: + app.logger.info(f"allow {request_rfc4514_name} (from {source} header)") + return "OK", 200 + + app.logger.info(f"deny {request_rfc4514_name} (from {source} header)") + return "Forbidden", 403 # @@ -254,22 +258,32 @@ def watch() -> None: event = threading.Event() threading.Thread(target=watch, daemon=True).start() - # # Load DN from file. # def load_allowed_client_dn(filepath: FilePath) -> None: """Load list of allowed client DN from file.""" + new_allowed_client_subject_dn_names = [] try: with filepath.open("r") as f: - new_allowed_client_subject_dn = [line.strip() for line in f if line.strip()] + lines = [line.strip() for line in f if line.strip()] except Exception as e: app.logger.error(f"cannot load allowed client DN from {filepath}: {e!s}") else: - if state.allowed_client_subject_dn != new_allowed_client_subject_dn: - state.allowed_client_subject_dn = new_allowed_client_subject_dn - app.logger.info(f"load {len(new_allowed_client_subject_dn)} DN from {filepath}") + # Convert DNs from "free" file format into RFC4514 format for comparison against k8s ingress + # NGINX's "ssl-client-subject-dn" annotation header (also converted to RFC4514 format) + for line in lines: + try: + rfc4514_name = rfc4514_cmp.dn_tagvalue_string_to_rfc4514_name(line) + except ValueError: + app.logger.warning(f"Not a Distinguished Name {line} header in {filepath}") + else: + new_allowed_client_subject_dn_names.append(rfc4514_name) + # Detect change in persistent state vs run-time + if state.allowed_client_subject_dn_names != new_allowed_client_subject_dn_names: + state.allowed_client_subject_dn_names = new_allowed_client_subject_dn_names + app.logger.info(f"load {len(new_allowed_client_subject_dn_names)} DN from {filepath}") if settings.use_watchdog: watchdog_file(settings.allowed_client_subject_dn_path, load_allowed_client_dn) diff --git a/pyproject.toml b/pyproject.toml index d8a7ef9..a5c5eab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ dependencies = [ "gunicorn", "pydantic-settings", "watchdog", + "cryptography", ] readme = "README.md" diff --git a/rfc4514_cmp.py b/rfc4514_cmp.py new file mode 100644 index 0000000..5bf8b73 --- /dev/null +++ b/rfc4514_cmp.py @@ -0,0 +1,305 @@ +# +# Official definition of distinguishedNameMatch +# https://datatracker.ietf.org/doc/html/rfc4517#section-4.2.15 +# +import re +from cryptography import x509 +from cryptography.x509.oid import ObjectIdentifier, NameOID +from cryptography.x509 import load_pem_x509_certificate + +# OID not included in cryptography.x509.oid.NameOID +OID_ORGANIZATION_IDENTIFIER = ObjectIdentifier("2.5.4.97") + +_OID_SHORT_NAMES = { + NameOID.COUNTRY_NAME: "C", + NameOID.STATE_OR_PROVINCE_NAME: "ST", + NameOID.LOCALITY_NAME: "L", + NameOID.ORGANIZATION_NAME: "O", + NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", + NameOID.COMMON_NAME: "CN", + NameOID.SERIAL_NUMBER: "serialNumber", + NameOID.EMAIL_ADDRESS: "emailAddress", + OID_ORGANIZATION_IDENTIFIER: "organizationIdentifier", +} + +# Omissions cryptography's OIDs +# Previous code tried to fix this by using a Mozilla compiled list of OIDs, oids.txt in: +# https://ftp.mozilla.org/pub/security/nss/releases/NSS_3_12_3_1_RTM/src/nss-3.12.3.1.tar.gz +# but that contained duplicate tags for different OIDs. So we just stick to +# what cryptography knows, with some known ommissions, and be less flexible to +# what the administrator put in the allowed_client_subject_dn_path file. +# +# Example: openssl x509 -nameopt RFC2253 -text output: +# "CN=University Corporation For Advanced Internet Development,emailAddress=knewell@internet2.edu,organizationIdentifier=NTRUS\\+MI-801069584,O=University Corporation For Advanced Internet Development,ST=Michigan,C=US" +# According to https://datatracker.ietf.org/doc/html/rfc4514#page-7 'emailAddress' is not a MUST, +# nor is 'organizationIdentifier'. cryptography package doesn't do MAY +# +names2oid = {} +names2oid["sn"] = ObjectIdentifier("2.5.4.4") # surname +names2oid["gn"] = ObjectIdentifier("2.5.4.42") # givenname +names2oid["organizationIdentifier"] = OID_ORGANIZATION_IDENTIFIER +names2oid["emailAddress"] = ObjectIdentifier("1.2.840.113549.1.9.1") + + +def dn_rfc2253_string_to_rfc4514_name(rfc2253_string:str): + """ + Convert a string with a DistinguishedName in RFC2253 format, such as generated by: + openssl x509 -nameopt RFC2253 -in bla.pem -text + into a cryptography.x509.Name object. + + Args: + rfc2253_string (str): RFC2253 formatted DN. + According to openssl CN comes first + + Returns: + cryptography.x509.Name: Parsed version of DN. + Raises: + ValueError: If rfc2253_string is not a valid RFC2253 DN. + """ + # Output is big-to-small sorted + return x509.Name.from_rfc4514_string(rfc2253_string,names2oid) + + +def dn_tagvalue_string_to_rfc4514_name(tagvalue_string:str): + """ + Convert a string in "tag1=value1,tag2=value2" format, that should represent + a valid DistinguishedName into a cryptography.x509.Name object, if it can be parsed + by the cryptography code. tags (symbolic names, not oids) are taken from the set + of valid DN tags as defined by cryptography, with e.g. "organizationIdentifier" added, + see above. + + Args: + tagvalue_string (str): comma-separated tag-value DN + + Returns: + cryptography.x509.Name: Parsed version of DN. + Raises: + ValueError: If tagvalue_string is not a valid DN. + """ + # Handle case when administrator copy-pastes subject DN from openssl x509 -text call + # but without -nameopt rfc2253. This means extra spaces and no escaped special chars. + clean_tagvalue_string = confer_parse_tag_pairs(tagvalue_string) + + rfc454_name = dn_rfc2253_string_to_rfc4514_name(clean_tagvalue_string) + oidlist = [] + # Copy RDNS + rdns = list(rfc454_name) + for rdn in rdns: + oidlist.append(rdn.oid) + + # cryptography does not fix order, so if input was big-to-small this is not fixed automatically + # note that big-to-small is the internal representation. + firstoid = oidlist[0] + lastoid = oidlist[-1] + bigtosmall = True + # Heuristic to determine order. These two fields MUST be "recognized" + # https://datatracker.ietf.org/doc/html/rfc4514#section-3 + if firstoid == NameOID.COMMON_NAME: + bigtosmall = False + elif lastoid == NameOID.COUNTRY_NAME: + bigtosmall = False + if not bigtosmall: + rdns.reverse() + + n = x509.Name(rdns) + return n + + +# Arno talking to confer.to: +def confer_parse_tag_pairs(input_string): + r""" + Parse a string of tag=value pairs with various separators + (commas, spaces, or both) and normalize to just commas. + Values are properly escaped according to RFC 4514 in the output. + Order is preserved. + + RFC 4514 escaping rules for values: + - Must escape: , + " # < > ; = \ + - Leading/trailing spaces must be escaped + - Embedded spaces do NOT need escaping + + Input may have mixed or no escaping; output will be consistently + escaped per RFC 4514. + + Args: + input_string: String containing tag=value pairs + (e.g., "tag1=val+ue1, tag2=val\\,ue2") + + Returns: + String with tag=value pairs separated only by commas, + values properly escaped per RFC 4514, order preserved + """ + import re + + def unescape_value(value): + """ + Unescape RFC 4514 escaped sequences in a value. + Converts \\X back to X for escaped characters. + """ + result = [] + i = 0 + while i < len(value): + if value[i] == '\\' and i + 1 < len(value): + # Escaped character - take the next char literally + result.append(value[i + 1]) + i += 2 + else: + result.append(value[i]) + i += 1 + return ''.join(result) + + def escape_rfc4514(value): + """ + Escape a value according to RFC 4514 section 2.4. + + Characters that must be escaped with backslash: + - Special chars: , + " # < > ; = \ + - Leading space (if first char) + - Trailing space (if last char) + """ + if not value: + return value + + # Characters that always need escaping + special_chars = { + ',': '\\,', + '+': '\\+', + '"': '\\"', + '#': '\\#', + '<': '\\<', + '>': '\\>', + ';': '\\;', + '=': '\\=', + '\\': '\\\\', + } + + result = [] + for i, char in enumerate(value): + if char in special_chars: + result.append(special_chars[char]) + elif char == ' ': + # Space: only escape if leading or trailing + if i == 0 or i == len(value) - 1: + result.append('\\ ') + else: + result.append(char) + else: + result.append(char) + + return ''.join(result) + + input_string = input_string.strip() + + if not input_string: + return "" + + # Pattern to match tag=value pairs + # Tag: one or more chars that aren't whitespace, comma, or equals + # Value: escaped chars (\\.) OR any char except equals, comma, backslash + pattern = r'([^\s,=]+)=((?:\\.|[^=,\\])*)(?=\s*,?\s*[^\s,=]+=|$)' + + matches = re.findall(pattern, input_string, re.DOTALL) + + if not matches: + return input_string + + # Unescape input values, then re-escape per RFC 4514 + result = ','.join( + f'{tag}={escape_rfc4514(unescape_value(value))}' + for tag, value in matches + ) + + return result +#EOAI + + +def subject_dn_from_cert_pem(cert_pem_bytes:str): + """ + Parse Subject DN from a PEM-encoded certificate. + Args: + cert_pem_str (bytes): PEM framed as per cryptography's rules + See https://cryptography.io/en/latest/faq/#why-can-t-i-import-my-pem-file + + Returns: + cryptography.x509.Name: Parsed version of Subject DN. + Raises: + ValueError: If cert_pem_str is not a valid PEM-encoded x.509 certificate. + """ + cert_obj = load_pem_x509_certificate(cert_pem_bytes) + subject_name = x509.Name(cert_obj.subject) + return subject_name + + +def subject_dn_from_traefik_cert_pem(traefik_cert_str): + """ Convert Traefik minimized, HTTP Header compatible PEM to what + cryptography groks + See https://cryptography.io/en/latest/faq/#why-can-t-i-import-my-pem-file + """ + delim_pem_cert_str = '-----BEGIN CERTIFICATE-----\n' + n = 64 + base64lines = [traefik_cert_str[i:i + n] for i in range(0, len(traefik_cert_str), n)] + for base64line in base64lines: + delim_pem_cert_str += base64line+'\n' + delim_pem_cert_str += '-----END CERTIFICATE-----\n' + + # https://www.rfc-editor.org/rfc/rfc9110.html#name-fields says values must be considered + # opaque bytes, but Python cannot do that, so use the old HTTP/1.1 standard header encoding. + delim_pem_cert_bytes = bytes(delim_pem_cert_str, "iso-8859-1") + s = subject_dn_from_cert_pem(delim_pem_cert_bytes) + return s + + +def _escape_dn_value(value: str) -> str: + """Escape special characters in a DN attribute value per RFC 4514.""" + value = value.replace("\\", "\\\\") + for ch in (",", "+", '"', "<", ">", ";"): + value = value.replace(ch, f"\\{ch}") + if value.startswith("#"): + value = "\\" + value + if value.startswith(" "): + value = "\\ " + value[1:] + if value.endswith(" "): + value = value[:-1] + "\\ " + return value + +# Arno: Currently unused, pem_str does not contain ---BEGIN--- delimiters, according +# to Traefik docs. +def subject_dn_from_traefik_cert_karl(header_value: str) -> str | None: + """Extract DN from Traefik's X-Forwarded-Tls-Client-Cert header (URL-encoded PEM). + + Parses the full certificate to access all subject fields including + organizationIdentifier (OID 2.5.4.97) and emailAddress (OID 1.2.840.113549.1.9.1). + Returns a normalized DN string in DER field order, or None on parse failure. + """ + try: + # Traefik strips newlines from the PEM before URL-encoding (to prevent header injection), + # so load_pem_x509_certificate would fail on the re-assembled string. Instead, extract + # the base64 between the PEM markers and load as DER. + # Use unquote (not unquote_plus) to preserve '+' characters valid in base64. + pem_str = unquote(header_value) + b64 = re.sub(r"-----[^-]+-----", "", pem_str).replace(" ", "") + cert = x509.load_der_x509_certificate(base64.b64decode(b64)) + except Exception as e: + app.logger.warning(f"failed to parse PEM from X-Forwarded-Tls-Client-Cert: {e!s}") + return None + + parts = [ + f"{rfc4514_cmp._OID_SHORT_NAMES.get(attr.oid, attr.oid.dotted_string)}={_escape_dn_value(attr.value)}" + for attr in cert.subject + ] + return ",".join(parts) + + +def subject_dn_from_traefik_cert_info(header_value: str) -> str | None: + """Extract DN from Traefik's X-Forwarded-Tls-Client-Cert-Info header. + + Traefik format: Subject="CN=...,O=...,C=..." + Returns the DN string without the Subject="" wrapper, or None if not found. + """ + # Match Subject="..." pattern and extract the DN + # Traefik URL-encodes the header value, so decode it first + match = re.search(r'Subject="([^"]+)"', unquote_plus(header_value)) + if match: + return match.group(1) + return None + diff --git a/tests/conftest.py b/tests/conftest.py index c23ce90..be63b75 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,8 +22,8 @@ def allowed_client_dn(tmp_path: Path) -> Path: """Create a temporary file for testing client DNs.""" path = tmp_path / "settings.json" - content = ("CN=CertA,OU=Dept X,O=Company Y,C=Z\n" - "CN=CertB,OU=Dept X,O=Company Y,C=Z\n") # fmt: skip + content = ("CN=CertA,OU=Dept X,O=Company Y,C=ZZ\n" + "CN=CertB,OU=Dept X,O=Company Y,C=ZZ\n") # fmt: skip path.write_text(content, encoding="utf-8") return path @@ -32,6 +32,8 @@ def allowed_client_dn(tmp_path: Path) -> Path: def application(allowed_client_dn: Path, monkeypatch: MonkeyPatch) -> Generator[Flask, None, None]: """Create and configure a new app instance for each test.""" monkeypatch.setenv("allowed_client_subject_dn_path", str(allowed_client_dn)) + # These tests are for NGINX Header-based auth + monkeypatch.setenv("tls_client_subject_authn_header", str("ssl-client-subject-dn")) from nsi_auth import app app.config.update( diff --git a/tests/functional/test_application.py b/tests/functional/test_application.py index b7aaa7c..5a04467 100644 --- a/tests/functional/test_application.py +++ b/tests/functional/test_application.py @@ -29,8 +29,11 @@ def test_validate_without_dn_header(client: FlaskClient) -> None: def test_validate_with_valid_dn_header(client: FlaskClient) -> None: """Verify that the /validate endpoint returns 200 with correct DN header.""" + # According to https://datatracker.ietf.org/doc/html/rfc4517#section-3.3.4 + # Country is "the two-character codes from ISO 3166". ZZ is a user-assigned element :-) + # https://www.iso.org/obp/ui/#iso:pub:PUB500001:en headers = { - "ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=Z", + "ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=ZZ", } response = client.get("/validate", headers=headers) assert response.status_code == 200 @@ -40,7 +43,7 @@ def test_validate_with_valid_dn_header(client: FlaskClient) -> None: def test_validate_with_second_valid_dn_header(client: FlaskClient) -> None: """Verify that the second allowed DN also returns 200.""" headers = { - "ssl-client-subject-dn": "CN=CertB,OU=Dept X,O=Company Y,C=Z", + "ssl-client-subject-dn": "CN=CertB,OU=Dept X,O=Company Y,C=ZZ", } response = client.get("/validate", headers=headers) assert response.status_code == 200 @@ -54,6 +57,15 @@ def test_validate_with_invalid_dn_header(client: FlaskClient) -> None: } response = client.get("/validate", headers=headers) assert response.status_code == 403 + +def test_validate_wrong_order_dn_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 403 with incorrect DN header. + header value must be in RFC2253 notation.""" + headers = { + "ssl-client-subject-dn": "C=ZZ,O=Company Y,OU=Dept X,CN=CertA", + } + response = client.get("/validate", headers=headers) + assert response.status_code == 403 assert response.data == b"Forbidden" @@ -71,3 +83,15 @@ def test_validate_rejects_non_get_methods(client: FlaskClient, method: str) -> N """Verify that the /validate endpoint only accepts GET requests.""" response = getattr(client, method)("/validate") assert response.status_code == 405 + + +def test_validate_with_unauthorized_escaped_dn_header(client: FlaskClient) -> None: + """Verify that the /validate endpoint returns 403 with header with a correct but unauthorized DN containing escapes.""" + # According to https://datatracker.ietf.org/doc/html/rfc4517#section-3.3.4 + # Country is "the two-character codes from ISO 3166". ZZ is a user-assigned element :-) + # https://www.iso.org/obp/ui/#iso:pub:PUB500001:en + headers = { + "ssl-client-subject-dn": "CN=CertA,OU=Dept\\,X,O=Company Y,C=ZZ", + } + response = client.get("/validate", headers=headers) + assert response.status_code == 403 diff --git a/tests/unit/test_load_allowed_client_dn.py b/tests/unit/test_load_allowed_client_dn.py index d181d99..121f96b 100644 --- a/tests/unit/test_load_allowed_client_dn.py +++ b/tests/unit/test_load_allowed_client_dn.py @@ -15,44 +15,52 @@ from flask import Flask from flask.testing import FlaskClient +import rfc4514_cmp + def test_load_dn_from_file(client: FlaskClient, allowed_client_dn: Path) -> None: """Verify that DNs are loaded from the configured file.""" from nsi_auth import state + + a_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=CertA,OU=Dept X,O=Company Y,C=ZZ") + b_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=CertB,OU=Dept X,O=Company Y,C=ZZ") - assert len(state.allowed_client_subject_dn) == 2 - assert "CN=CertA,OU=Dept X,O=Company Y,C=Z" in state.allowed_client_subject_dn - assert "CN=CertB,OU=Dept X,O=Company Y,C=Z" in state.allowed_client_subject_dn + assert len(state.allowed_client_subject_dn_names) == 2 + assert a_name in state.allowed_client_subject_dn_names + assert b_name in state.allowed_client_subject_dn_names def test_load_dn_ignores_blank_lines(application: Flask, allowed_client_dn: Path) -> None: """Verify that blank lines in the DN file are ignored.""" from nsi_auth import load_allowed_client_dn, state - allowed_client_dn.write_text("\n\nCN=CertA,OU=Dept X,O=Company Y,C=Z\n\n\n", encoding="utf-8") + allowed_client_dn.write_text("\n\nCN=CertA,OU=Dept X,O=Company Y,C=ZZ\n\n\n", encoding="utf-8") load_allowed_client_dn(allowed_client_dn) - assert state.allowed_client_subject_dn == ["CN=CertA,OU=Dept X,O=Company Y,C=Z"] + a_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=CertA,OU=Dept X,O=Company Y,C=ZZ") + assert state.allowed_client_subject_dn_names == [a_name] def test_load_dn_ignores_whitespace_only_lines(application: Flask, allowed_client_dn: Path) -> None: """Verify that whitespace-only lines in the DN file are ignored.""" from nsi_auth import load_allowed_client_dn, state - allowed_client_dn.write_text(" \n\t\nCN=CertA,OU=Dept X,O=Company Y,C=Z\n", encoding="utf-8") + allowed_client_dn.write_text(" \n\t\nCN=CertA,OU=Dept X,O=Company Y,C=ZZ\n", encoding="utf-8") load_allowed_client_dn(allowed_client_dn) - assert state.allowed_client_subject_dn == ["CN=CertA,OU=Dept X,O=Company Y,C=Z"] + a_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=CertA,OU=Dept X,O=Company Y,C=ZZ") + assert state.allowed_client_subject_dn_names == [a_name] def test_load_dn_strips_whitespace(application: Flask, allowed_client_dn: Path) -> None: """Verify that leading/trailing whitespace is stripped from DNs.""" from nsi_auth import load_allowed_client_dn, state - allowed_client_dn.write_text(" CN=CertA,OU=Dept X,O=Company Y,C=Z \n", encoding="utf-8") + allowed_client_dn.write_text(" CN=CertA,OU=Dept X,O=Company Y,C=ZZ \n", encoding="utf-8") load_allowed_client_dn(allowed_client_dn) - assert state.allowed_client_subject_dn == ["CN=CertA,OU=Dept X,O=Company Y,C=Z"] + a_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=CertA,OU=Dept X,O=Company Y,C=ZZ") + assert state.allowed_client_subject_dn_names == [a_name] def test_load_dn_empty_file(application: Flask, allowed_client_dn: Path) -> None: @@ -62,17 +70,20 @@ def test_load_dn_empty_file(application: Flask, allowed_client_dn: Path) -> None allowed_client_dn.write_text("", encoding="utf-8") load_allowed_client_dn(allowed_client_dn) - assert state.allowed_client_subject_dn == [] + assert state.allowed_client_subject_dn_names == [] def test_load_dn_nonexistent_file_keeps_previous_state(application: Flask, tmp_path: Path) -> None: """Verify that loading from a nonexistent file preserves the previous state.""" from nsi_auth import load_allowed_client_dn, state - state.allowed_client_subject_dn = ["CN=Existing"] + e_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=Existing") + + state.allowed_client_subject_dn_names = [e_name] load_allowed_client_dn(tmp_path / "nonexistent.txt") - assert state.allowed_client_subject_dn == ["CN=Existing"] + assert state.allowed_client_subject_dn_names == [e_name] + def test_load_dn_no_update_when_unchanged(application: Flask, allowed_client_dn: Path) -> None: @@ -80,10 +91,10 @@ def test_load_dn_no_update_when_unchanged(application: Flask, allowed_client_dn: from nsi_auth import load_allowed_client_dn, state load_allowed_client_dn(allowed_client_dn) - original_list = state.allowed_client_subject_dn + original_list = state.allowed_client_subject_dn_names load_allowed_client_dn(allowed_client_dn) - assert state.allowed_client_subject_dn is original_list + assert state.allowed_client_subject_dn_names is original_list def test_load_dn_updates_on_file_change(application: Flask, allowed_client_dn: Path) -> None: @@ -91,12 +102,13 @@ def test_load_dn_updates_on_file_change(application: Flask, allowed_client_dn: P from nsi_auth import load_allowed_client_dn, state load_allowed_client_dn(allowed_client_dn) - assert len(state.allowed_client_subject_dn) == 2 + assert len(state.allowed_client_subject_dn_names) == 2 allowed_client_dn.write_text("CN=NewCert,O=NewOrg,C=NL\n", encoding="utf-8") load_allowed_client_dn(allowed_client_dn) - assert state.allowed_client_subject_dn == ["CN=NewCert,O=NewOrg,C=NL"] + n_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=NewCert,O=NewOrg,C=NL") + assert state.allowed_client_subject_dn_names == [n_name] def test_validate_after_dn_reload(client: FlaskClient, allowed_client_dn: Path) -> None: @@ -104,14 +116,56 @@ def test_validate_after_dn_reload(client: FlaskClient, allowed_client_dn: Path) from nsi_auth import load_allowed_client_dn load_allowed_client_dn(allowed_client_dn) - response = client.get("/validate", headers={"ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=Z"}) + response = client.get("/validate", headers={"ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=ZZ"}) assert response.status_code == 200 allowed_client_dn.write_text("CN=NewCert,O=NewOrg,C=NL\n", encoding="utf-8") load_allowed_client_dn(allowed_client_dn) - response = client.get("/validate", headers={"ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=Z"}) + response = client.get("/validate", headers={"ssl-client-subject-dn": "CN=CertA,OU=Dept X,O=Company Y,C=ZZ"}) assert response.status_code == 403 response = client.get("/validate", headers={"ssl-client-subject-dn": "CN=NewCert,O=NewOrg,C=NL"}) assert response.status_code == 200 + + +def test_load_wildcard_dn_from_file(client: FlaskClient, allowed_client_dn: Path) -> None: + """Verify that wildcard DNs are loaded from the configured file.""" + from nsi_auth import state, load_allowed_client_dn + + allowed_client_dn.write_text("CN=*,OU=Dept X,O=Company Y,C=ZZ\n", encoding="utf-8") + load_allowed_client_dn(allowed_client_dn) + a_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name("CN=*,OU=Dept X,O=Company Y,C=ZZ") + + assert len(state.allowed_client_subject_dn_names) == 1 + assert a_name in state.allowed_client_subject_dn_names + + +def test_load_escaped_dn_from_file(client: FlaskClient, allowed_client_dn: Path) -> None: + """Verify that DN with escapes are loaded from the configured file.""" + from nsi_auth import state, load_allowed_client_dn + + data = "CN=University Corporation For Advanced Internet Development,emailAddress=johndoe@internet2.edu,organizationIdentifier=NTRUS\\+MI-801069584,O=University Corporation For Advanced Internet Development,ST=Michigan,C=US" + allowed_client_dn.write_text(data+"\n", encoding="utf-8") + load_allowed_client_dn(allowed_client_dn) + a_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name(data) + + assert len(state.allowed_client_subject_dn_names) == 1 + assert a_name in state.allowed_client_subject_dn_names + +def test_load_reversed_dn_from_file(client: FlaskClient, allowed_client_dn: Path) -> None: + """Verify that DN in wrong order are loaded from the configured file.""" + from nsi_auth import state, load_allowed_client_dn + + data = 'CN=University Corporation For Advanced Internet Development,emailAddress=johndoe@internet2.edu,organizationIdentifier=NTRUS\\+MI-801069584,O=University Corporation For Advanced Internet Development,ST=Michigan,C=US' + # TODO: cryptography doesn't grok spaces after the commas. + # These spaces are there when the admin uses openssl x509 -in bla.pem -txt without -nameopt rfc2253, so will be a common mistake + # Without -nameopt, openssl also does not escape special characters. + revdata = "C=US, ST=Michigan, O=University Corporation For Advanced Internet Development, organizationIdentifier=NTRUS+MI-801069584, emailAddress=johndoe@internet2.edu, CN=University Corporation For Advanced Internet Development" + allowed_client_dn.write_text(revdata+"\n", encoding="utf-8") + load_allowed_client_dn(allowed_client_dn) + a_name = rfc4514_cmp.dn_rfc2253_string_to_rfc4514_name(data) + + assert len(state.allowed_client_subject_dn_names) == 1 + assert a_name in state.allowed_client_subject_dn_names +