def testDerCodec(self): substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate assert asn1Object['acinfo']['version'] == 1 attributeMap = { rfc3281.id_at_role: rfc3281.RoleSyntax(), rfc3281.id_aca_authenticationInfo: rfc3281.SvceAuthInfo(), rfc3281.id_aca_accessIdentity: rfc3281.SvceAuthInfo(), rfc3281.id_aca_chargingIdentity: rfc3281.IetfAttrSyntax(), rfc3281.id_aca_group: rfc3281.IetfAttrSyntax(), } count = 0 for attr in asn1Object['acinfo']['attributes']: assert attr['type'] in attributeMap av, rest = der_decode(attr['values'][0], asn1Spec=attributeMap[attr['type']]) assert not rest assert av.prettyPrint() assert der_encode(av) == attr['values'][0] count += 1 assert count == 5
def testDerCodec(self): substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decoder(substrate, asn1Spec=self.asn1Spec) self.assertFalse(rest) self.assertTrue(asn1Object.prettyPrint()) self.assertEqual(substrate, der_encoder(asn1Object)) self.assertEqual(1, asn1Object['acinfo']['version']) attributeMap = { rfc3281.id_at_role: rfc3281.RoleSyntax(), rfc3281.id_aca_authenticationInfo: rfc3281.SvceAuthInfo(), rfc3281.id_aca_accessIdentity: rfc3281.SvceAuthInfo(), rfc3281.id_aca_chargingIdentity: rfc3281.IetfAttrSyntax(), rfc3281.id_aca_group: rfc3281.IetfAttrSyntax(), } count = 0 for attr in asn1Object['acinfo']['attributes']: self.assertIn(attr['type'], attributeMap) av, rest = der_decoder(attr['values'][0], asn1Spec=attributeMap[attr['type']]) self.assertFalse(rest) self.assertTrue(av.prettyPrint()) self.assertEqual(attr['values'][0], der_encoder(av)) count += 1 self.assertEqual(5, count)
def decodeVOMSExtension(m2cert): """Decode the content of the VOMS extension :param m2cert: M2Crypto X509 object, a certificate :returns: A dictionnary containing the following fields: * notBefore: datetime.datetime * notAfter: datetime.datetime * attribute: (string). Comma separated list of VOMS tags presented as bellow "<tagName> = <tagValue> (<tagQualifier>)" Typically, the nickname will look like 'nickname = chaen (lhcb)', * fqan: List of VOMS "position" (['/lhcb/Role=production/Capability=NULL', '/lhcb/Role=NULL/Capability=NULL']) * vo: name of the VO, * subject: subject DN to which the attributes were granted, * issuer: typically the DN of the VOMS server (e.g '/DC=ch/DC=cern/OU=computers/CN=lcg-voms2.cern.ch') """ vomsExtensionDict = {} vomsExtensionOctetString = retrieveExtension(m2cert, VOMS_EXTENSION_OID) # Decode it as a ACSequenceOfSequence, which is what it is... vomsExtensionSeqOfSeq, _rest = der_decode(vomsExtensionOctetString, asn1Spec=_ACSequenceOfSequence()) # In principle, according to GFD 182, there could be more than one VO VOMS AC per proxy. # The standard specifies that we have to accept at least the first one, which is what # I will do... vomsCertAttribute = vomsExtensionSeqOfSeq[0][0] ###### # TODO in principle, we should check the signature of the Attribute... # _signatureAlgorith = vomsCertAttribute['signatureAlgorithm'] # _signatureValue = vomsCertAttribute['signatureValue'] ###### certAttrInfo = vomsCertAttribute["acinfo"] # pyasn1 does things correctly by setting a timezone info in the datetime # However, we do not in DIRAC, and so we can't compare the dates. # We have to remove the timezone info from the datetime objects notBefore = certAttrInfo["attrCertValidityPeriod"][ "notBeforeTime"].asDateTime vomsExtensionDict["notBefore"] = notBefore.replace(tzinfo=None) notAfter = certAttrInfo["attrCertValidityPeriod"][ "notAfterTime"].asDateTime vomsExtensionDict["notAfter"] = notAfter.replace(tzinfo=None) # ######### Retrieving the issuer ########## # Get the issuer. A bit tricky, because we have to reconstruct the full DN ourselves # The GFD 182 and RFC 3281 give enough restriction such that we can afford some direct # [0] access issuer = "" # rdnName is a rfc3280.RelativeDistinguishedName object for rdnName in certAttrInfo["issuer"]["v2Form"]["issuerName"][0][ "directoryName"]["rdnSequence"]: # rdnNameAttr rfc3280.AttributeTypeAndValue' rdnNameAttr = rdnName[0] attrOid = ".".join([str(e) for e in rdnNameAttr["type"].asTuple()]) # Now finally convert the last part into a asn1char.*String attrValStr = _decodeASN1String(rdnNameAttr["value"]) attrVal = attrValStr.asOctets().decode() # issuer += "%s%s" % (DN_MAPPING[attrOid], attrVal) vomsExtensionDict["issuer"] = issuer # ### Issuer retrieved ##### # ## Retrieving the Subject #### # We have to do the same for the subject than for the issuer subject = "" # rdnName is a rfc3280.RelativeDistinguishedName object for rdnName in certAttrInfo["holder"]["baseCertificateID"]["issuer"][0][ "directoryName"]["rdnSequence"]: # rdnNameAttr rfc3280.AttributeTypeAndValue' rdnNameAttr = rdnName[0] attrOid = ".".join([str(e) for e in rdnNameAttr["type"].asTuple()]) # # Because there are non printable characters in the values (new line, etc) # # we have to get ride of them. The best way is to get them as number, and make sure it is a # # a printable char (between 32 and 126) # # attrVal = ''.join([chr(c) for c in rdnNameAttr['value'].asNumbers() if 32 <= c <= 126 ]) # Now finally convert the last part into a asn1char.*String attrValStr = _decodeASN1String(rdnNameAttr["value"]) attrVal = attrValStr.asOctets().decode() subject += "%s%s" % (DN_MAPPING[attrOid], attrVal) vomsExtensionDict["subject"] = subject # ### Retrieving the FQAN #### # According to GFD182, there may be more attributes that just the FQAN, even though it # does not seem to be the case in practice. So we make sure to have the good one fqanOIDObj = univ.ObjectIdentifier(VOMS_FQANS_OID) # There shall be only one, hense the [0] # This is an rfc3280.Attribute object fqanAttrObj = [ attrObj for attrObj in certAttrInfo["attributes"] if attrObj["type"] == fqanOIDObj ][0] # According to GFD182 3.4.1, we decode the value as a IetfAttrSyntax. # Since multiple values are not allowed, just take the first item # fqanObj, _rest = der_decode(fqanAttrObj["values"][0], asn1Spec=rfc3281.IetfAttrSyntax()) # We retrieve the VO and the VOMS server voName, _, _ = fqanObj["policyAuthority"][0][ "uniformResourceIdentifier"].asOctets().decode().split(":") vomsExtensionDict["vo"] = voName # Now retrieve the position of the holder (group, role) fqanList = [] for fqanPositionObj in fqanObj["values"]: fqanList.append(fqanPositionObj["octets"].asOctets().decode()) vomsExtensionDict["fqan"] = fqanList # ############ End of the FQAN ################ # Now the Tags, called attributes in the dict... tagDescriptions = [] vomsTagsOIDObj = univ.ObjectIdentifier(VOMS_TAGS_EXT_OID) # First find the tag containers tagExtensionObj = [ extObj for extObj in certAttrInfo["extensions"] if extObj["extnID"] == vomsTagsOIDObj ] # If we found tags if tagExtensionObj: # Multiple is forbiden, so only one tag container tagExtensionObj = tagExtensionObj[0] tagContainersObj, _rest = der_decode(tagExtensionObj["extnValue"], asn1Spec=_TagContainers()) # TODO in principle, we should check that this value # and the one of the policyAuthority of the fqan are the same # _tagPolicyAuthority = tagContainersObj[0][0]['policyAuthority'][0]['uniformResourceIdentifier'] \ # .asOctets().decode() ###### for tagContainer in tagContainersObj: for tagList in tagContainer: # Note: it is here that I should check the policyAuthority tagList = tagList["tags"] for tag in tagList: # This gives a string like # nickname = chaen (lhcb) tagDescriptions.append("%s = %s (%s)" % ( tag["name"].asOctets().decode(), tag["value"].asOctets().decode(), tag["qualifier"].asOctets().decode(), )) vomsExtensionDict["attribute"] = ",".join(tagDescriptions) # #### Tags are done ################ return vomsExtensionDict