X509Chain

X509Chain is a class for managing X509 chains with their Pkeys

Link to the RFC 3820: https://tools.ietf.org/html/rfc3820 In particular, limited proxy: https://tools.ietf.org/html/rfc3820#section-3.8

There are also details available about Per-User Sub-Proxies (PUSP) here: https://wiki.egi.eu/wiki/Usage_of_the_per_user_sub_proxy_in_EGI

class DIRAC.Core.Security.m2crypto.X509Chain.X509Chain(certList=False, keyObj=False)

Bases: object

An X509Chain is basically a list of X509Certificate object, as well as a PKey object, which is associated to the X509Certificate the lowest in the chain.

This is what you will want to use for user certificate (because they will turn into proxy….), and for proxy.

A priori, once we get rid of pyGSI, we could even meld the X509Certificate into this one, and use the X509Chain for host certificates. After all, a certificate is nothing but a chain of length 1…

There are normally 4 ways you would instanciate an X509Chain object:

  • You are loading a proxy from a file

  • Loading the chain from a file

  • You are getting information about your peer during an SSL connection

  • You are delegating

Typical usages of X509Chain are illustrated below

Loading a proxy from a file (this will load the chain and the key, assuming the key is in the same file):

proxy = X509Chain()
res = proxy.loadProxyFromFile(myFile)
if not res['OK']:
  return res

Generating a proxy from a Certificate:

cert = X509Chain()
# Load user cert
retVal = cert.loadChainFromFile('/home/chaen/.globus/userkey.pem')
if not retVal['OK']:
  return retVal
# Load the key from a different place, with a password
retVal = cert.loadKeyFromFile('/home/chaen/.globus/userkey.pem', password='MySecretKey')
if not retVal['OK']:
  return res

# Generate a limited proxy, valid one hour
retVal = cert.generateProxyToFile('/tmp/proxy.pem',
                               3600, # only 1 h
                               diracGroup = 'lhcb_user',
                               strength= 2048,
                               limited=True)

Getting information from a peer in an SSL Connection:

# conn is an M2Crypto.SSL.Connection instance
chain = X509Chain.generateX509ChainFromSSLConnection(conn)
creds = chain.getCredentials()

Delegating a proxy to a service:

# The server side generates a request
# Equivalent to ProxyManager.requestDelegationUpload

x509Req = X509Request()
x509Req.generateProxyRequest()

# This reqStr object is sent to the client
reqStr = x509Req.dumpRequest()['Value']

# This object contains both the public and private key
pkeyReq = x509Req.getPKey()

#######################################################

# The client side signs the request, with its proxy
# Assume the proxy chain was already loaded one way or the otjer

# The proxy will not contain a private key
res = proxyChain.generateChainFromRequestString(reqStr, lifetime=lifetime)

# This is sent back to the server
delegatedProxyString = res['Value']

######################################################
# Equivalent to ProxyManager.completeDelegationUpload

# Create the new chain
# the pkey was generated together with the Request
delegatedProxy = X509Chain(keyObj=pkeyReq)
delegatedProxy.loadChainFromString(delegatedProxyString)

# make sure the public key match between Request and the new Chain
# (Stupid, of course it will ! But it is done in the ProxyManager...)
res = x509Req.checkChain(delegatedProxy)
__init__(certList=False, keyObj=False)

C’tor

Parameters:
  • certList – list of X509Certificate to constitute the chain

  • keyObj – ~M2Crypto.EVP.PKey object. The public or public/private key associated to the last certificate of the chain

dumpAllToFile(filename=False)

Dump all to file.

Parameters:

filename – If not specified, a temporary one will be created

Returns:

S_OK(filename)/S_ERROR

dumpAllToString()

Dump the current chain as a PEM encoded string The order would be:

  • first certificate

  • private key (without passphrase)

  • other certificates

Returns:

S_OK(PEM encoded chain with private key)

dumpChainToString()

Dump only cert chain to string, without the PKey

Returns:

S_OK(pem chain)

dumpPKeyToString()

Dump only the key to string, not encoded

Returns:

S_OK(PEM encoded key)

generateChainFromRequestString(pemData, lifetime=86400, requireLimited=False, diracGroup=False)

Generate a x509 chain from a request.

Parameters:
  • pemData – PEM encoded request

  • lifetime – lifetime of the delegated proxy in seconds (default 1 day)

  • requireLimited – if True, requires a limited proxy

  • diracGroup – DIRAC group to put in the proxy

  • rfc – placeholder for compatibility, ignored

Returns:

S_OK( X509 chain pem encoded string ) / S_ERROR. The new chain will have been signed with the public key included in the request

generateProxyRequest(bitStrength=2048, limited=False)

Generate a proxy request. See DIRAC.Core.Security.m2crypto.X509Certificate.X509Certificate.generateProxyRequest()

Return S_OK( X509Request ) / S_ERROR

generateProxyToFile(filePath, lifetime, diracGroup=False, strength=2048, limited=False)

Generate a proxy and put it into a file

Parameters:
  • filePath – file to write

  • lifetime – expected lifetime in seconds of proxy

  • diracGroup – diracGroup to add to the certificate

  • strength – length in bits of the pair

  • limited – Create a limited proxy

  • rfc – placeholder and ignored

generateProxyToString(lifetime, diracGroup=False, strength=2048, limited=False, proxyKey=False)

Generate a proxy and get it as a string.

Check here: https://github.com/eventbrite/m2crypto/blob/master/demo/x509/ca.py#L45

Parameters:
  • lifetime (int) – expected lifetime in seconds of proxy

  • diracGroup (str) – diracGroup to add to the certificate

  • strength (int) – length in bits of the pair if proxyKey not given (default 2048)

  • limited (bool) – Create a limited proxy (default False)

  • proxyKey – M2Crypto.EVP.PKey instance with private and public key. If not given, generate one

  • rfc – placeholder for backward compatibility and ignored

Returns:

S_OK(PEM encoded string), S_ERROR. The PEM string contains all the certificates in the chain and the private key associated to the last X509Certificate just generated.

static generateX509ChainFromSSLConnection(sslConnection)

Returns an instance of X509Chain from the SSL connection

Parameters:

sslConnection – ~M2Crypto.SSl.Connection instance

Returns:

a X509Chain instance

getCertInChain(certPos=0)

Get then a certificate in the chain

Warning:

Contrary to the pygsi version, this is not a copy!

Parameters:

certPos – position of the certificate in the chain. Default: 0

Returns:

S_OK(X509Certificate)/S_ERROR

getCertList()

Get the cert list

Deprecated: Only here for compatibility reason

getCredentials(ignoreDefault=False, withRegistryInfo=True)

Returns a summary of the credentials contained in the current chain

Params ignoreDefault:

(default False) If True and if no DIRAC group is found in the proxy, lookup the CS

Params withRegistryInfo:

(default True) if set to True, will enhance the returned dict with info from the registry

Returns:

S_OK with the credential dict. Some parameters of the dict are always there, other depends on the nature of the Chain

Always present:
  • subject: str. The last DN in the chain

  • issuer: str. The issuer of the last cert in the chain

  • secondsLeft: validity of the chain in seconds (see getRemainingSecs())

  • isProxy: boolean (see isProxy())

  • isLimitedProxy: boolean (see isLimitedProxy())

  • validDN: boolean if the DN is known to DIRAC

  • validGroup: False (see further definition)

  • DN: either the DN of the host, or the DN of the user corresponding to the proxy

Only for proxy:
  • identity: If it is a normal proxy, it is the DN of the certificate.

    If it is a PUSP, it contains the identity as in isPUSP()

  • username: DIRAC username associated to the DN (needs withRegistryInfo)

    (see DIRAC.ConfigurationSystem.Client.Helpers.Registry.getUsernameForDN())

  • group: DIRAC group, depending on ignoreDefault param(see getDIRACGroup())

  • validGroup: True if the group found is in the list of groups the user belongs to

  • groupProperty: (only if validGroup) get the properties of the group

For Host certificate (needs withRegistryInfo):
If it is a user certificate (needs withRegistryInfo):
  • username: like for proxy

  • validDN: like proxy

getDIRACGroup(ignoreDefault=False)

Retrieve the dirac group of the chain

Parameters:

ignoreDefault – (default False) if True, do not lookup the CS for a group if it is not in the proxy

Returns:

S_OK(dirac group)/S_ERROR

getIssuerCert()

Returns the issuer certificate of the last one if it is a proxy, otherwise the last one in the chain

Returns:

S_OK(X509Certificate)/S_ERROR

getNotAfterDate()

Get the smallest not after date

Returns:

S_OK(datetime.datetime)

getNumCertsInChain()

length of the certificate chain

Returns:

length of the certificate chain

getPKeyObj()

Get the pkey obj

returns:

~M2Crypto.EVP.PKey object

Deprecated: Only here for compatibility reason

getRemainingSecs()

Get remaining time (minimum of all cert in the chain)

Returns:

S_OK(time left in seconds)

getStrength()

Returns the strength in bit of the key of the first certificate in the chain

getVOMSData()

Returns the voms data.

Returns:

See getVOMSData() If no VOMS data is available, return DErrno.EVOMS

Warning:

In case the chain is not a proxy, this method will return False. Yes, it’s stupid, but it is for compatibility…

hasExpired()

Check whether any element of the chain has expired

Returns:

S_OK(boolean)

hash()

Get a hash of the chain In practice, this is only used to index the chain in a DictCache

Returns:

S_OK(string hash)

classmethod instanceFromFile(chainLocation)

Class method to generate a X509Chain from a file

param chainLocation:

path to the file

returns:

S_OK(X509Chain)

Deprecated: Use loadChainFromFile instead

isLimitedProxy()

Check whether this chain is a limited proxy

Returns:

S_OK(boolean)

isPUSP()

Checks whether the current chain is a PUSP

Returns:

S_OK(boolean). If True, the S_OK structure is enriched with: * Indentity: the DN * SubProxyUser: name of the user

isProxy()

Check whether this chain is a proxy

Returns:

S_OK(boolean)

isRFC()

Check whether this is an RFC proxy. It can only be true, providing it is a proxy

Returns:

S_OK(boolean)

isVOMS()

Check whether this proxy contains VOMS extensions. It is enough for one of the certificate of the chain to have VOMS extension

Returns:

S_OK(boolean)

isValidProxy(ignoreDefault=False)
Check whether this chain is a valid proxy, that is:
  • a proxy

  • still valid

  • with a valid group

Parameters:

ignoreDefault – (what a stupid name) if True, do not lookup the CS

Returns:

S_OK(True) if the proxy is valid, S_ERROR otherwise

loadChainFromFile(chainLocation)

Load a x509 chain from a pem file

Parameters:

chainLocation – path to the file

Returns:

S_OK/S_ERROR

loadChainFromString(data)

Load a x509 cert from a string containing the pem data

Parameters:

data – data representing the chain of certificate in the

Return : S_OK / S_ERROR

loadKeyFromFile(chainLocation, password=False)

Load a PKey from a pem file

Parameters:
  • chainLocation – path to the file

  • password – password to decode the file.

Returns:

S_OK / S_ERROR

loadKeyFromString(pemData, password=False)

Load a PKey from a string containing the pem data

Parameters:
  • pemData – pem data of the key, potentially encoded with the password

  • password – password to decode the file.

Returns:

S_OK / S_ERROR

loadProxyFromFile(chainLocation)

Load a Proxy from a pem file, that is both the Cert chain and the PKey

Parameters:

chainLocation – path to the proxy file

Returns:

S_OK / S_ERROR

loadProxyFromString(pemData)

Load a Proxy from a pem buffer, that is both the Cert chain and the PKey

Parameters:

pemData – PEM encoded cert chain and pkey

Returns:

S_OK / S_ERROR

setPKey(pkeyObj)

Set the chain Return : S_OK / S_ERROR

DIRAC.Core.Security.m2crypto.X509Chain.isPUSPdn(userDN)

Evaluate if the DN is of the PUSP type or not

Parameters:

userDN (str) – user DN string

Returns:

the subproxy user name or None