Question

Ok, I am trying to verify data from PKCS7 envelop using Python.

I have the working code in Java: http://nyal.developpez.com/tutoriel/java/bouncycastle/#L4.2

What I want is first get the certificate from the envelop.

I am able to open it with that command:

openssl pkcs7 -in pkcs7 -print_certs -text

Then I want to verify that the data is ok.

I tried this:

import base64
from M2Crypto import SMIME, X509, BIO

raw_sig = """base64 PKCS7 envelop"""
msg = "challenge message to verify"

sm_obj = SMIME.SMIME()
x509 = X509.load_cert('/etc/ssl/certs/ca-certificates.crt') # public key cert used by the remote
# client when signing the message
sk = X509.X509_Stack()
sk.push(x509)
sm_obj.set_x509_stack(sk)

st = X509.X509_Store()
st.load_info('/etc/ssl/certs/ca-certificates.crt') # Public cert for the CA which signed
# the above certificate
sm_obj.set_x509_store(st)

# re-wrap signature so that it fits base64 standards
cooked_sig = '\n'.join(raw_sig[pos:pos+76] for pos in xrange(0, len(raw_sig), 76))

# now, wrap the signature in a PKCS7 block
sig = """
-----BEGIN PKCS7-----
%s
-----END PKCS7-----
""" % cooked_sig

# print sig

# and load it into an SMIME p7 object through the BIO I/O buffer:
buf = BIO.MemoryBuffer(sig)
p7 = SMIME.load_pkcs7_bio(buf)

# do the same for the message text
data_bio = BIO.MemoryBuffer(msg)

cert = sm_obj.verify(p7, data_bio)

I think one of the /etc/ssl/certs/ca-certificates.crt should be the userCertificate.

After having the certificate I want to check that it is still valid (using validatity date) and verify it against the CRL and the CPS for revocation.

I hope you can help me.

Was it helpful?

Solution

So I was almost there:

import base64
from M2Crypto import SMIME, X509, BIO

raw_sig = """base64 PKCS7 envelop"""
msg = "challenge message to verify"

sm_obj = SMIME.SMIME()
x509 = X509.load_cert('ISSUER.crt') # public key cert used by the remote
                                             # client when signing the message
sk = X509.X509_Stack()
sk.push(x509)
sm_obj.set_x509_stack(sk)

st = X509.X509_Store()
st.load_info('ROOT.crt') # Public cert for the CA which signed
                                    # the above certificate
sm_obj.set_x509_store(st)

# re-wrap signature so that it fits base64 standards
cooked_sig = '\n'.join(raw_sig[pos:pos+76] for pos in xrange(0, len(raw_sig), 76))

# now, wrap the signature in a PKCS7 block
sig = """
-----BEGIN PKCS7-----
%s
-----END PKCS7-----
""" % cooked_sig

# print sig

# and load it into an SMIME p7 object through the BIO I/O buffer:
buf = BIO.MemoryBuffer(sig)
p7 = SMIME.load_pkcs7_bio(buf)

signers = p7.get0_signers(sk)
certificat = signers[0]

Then you might also be interested in CRL and OCSP validation:

from os.path import basename
import re
from tempfile import NamedTemporaryFile

try:
    from subprocess import check_output, CalledProcessError, STDOUT
except ImportError:  # check_output new in 2.7, so use a backport for <=2.6
    from subprocess32 import check_output, CalledProcessError, STDOUT

class OpenSSLError(Exception):
    pass

def info_extension_cert(cert):
    """
    This function take a certificate and return the extensions in dict.

    @type cert : M2Crypto.X509
    @param cert : Certificate
    """
    certificateExtensions = {}

    for index in range(cert.get_ext_count()):
        ext = cert.get_ext_at(index)
        certificateExtensions[ext.get_name()] = ext.get_value()
    return certificateExtensions


def get_cert_url_ocsp(cert):
    """
    Get the OCSP url of a certificate

    @type cert : M2Crypto.X509
    @parm cert : Certificat

    @rtype : string
    @return : The OSCP url
    """

    infos = [x.strip() for x in info_extension_cert(cert)["authorityInfoAccess"].split('\n')]
    ocsp_url = None
    for info in infos:
        if re.match(r"^OCSP - URI:", info):
            ocsp_url = info.replace("OCSP - URI:","")
            break
    return ocsp_url.strip()


def is_revoked(cert, cert_parent):
    """
    Check if the certificate has been revoked.

    @type cert : M2Crypto.X509
    @param cert : The certificate

    @type cert_parent : string
    @param cert_parent : Issuer certificate file path

    @rtype : boolean
    @return : True if revoked or False
    """
    ocsp_url = get_cert_url_ocsp(cert)
    if re.match(r"^http", ocsp_url) is None:
        return False

    data = {'cert_parent': cert_parent,
            'ocsp_url': ocsp_url,
            'serial': cert.get_serial_number()}

    cmd = "openssl ocsp -issuer %(cert_parent)s -CAfile %(cert_parent)s -url %(ocsp_url)s -serial %(serial)s" % data
    print cmd
    try:
        output = check_output(cmd, shell=True, stderr=STDOUT).lower()
    except CalledProcessError, e:
        msg = u"[OpenSSL] Error while checking ocsp %s: %s. Output: %r" % (
                    cmd, e, e.output)
        raise OpenSSLError(msg)
    return not ('response verify ok' in output and '%s: good' % data['serial'] in output)


def is_revoked_crl(cert, cert_parent_with_crl):
    """
    Check if the certificate as been revoked with the crl.

    @type cert : M2Crypto.X509
    @param cert : The certificate

    @type cert_parent : string
    @param cert_parent : Issuer certificate file path

    @rtype : boolean
    @return : True if revoked or False
    """
    tmp_file = NamedTemporaryFile(prefix='cert')
    cert.save(tmp_file.name)
    data = {'cert': tmp_file.name,
            'cert_parent_with_crl': cert_parent_with_crl}
    cmd = "openssl verify -crl_check -CAfile %(cert_parent_with_crl)s %(cert)s" % data
    print cmd
    try:
        output = check_output(cmd, shell=True, stderr=STDOUT).lower()
    except CalledProcessError, e:
        msg = u"[OpenSSL] Error while checking ocsp %s: %s. Output: %r" % (
                    cmd, e, e.output)
        raise OpenSSLError(msg)
    print output
    return '%s: ok' % data['cert'] not in output


def get_cert_url_crl(cert):
    """
    Return the crl url from the certificate

    @type cert : M2Crypto.X509
    @parm cert : Certificate

    @rtype : string
    @return : CRL url
    """

    infos = [x.strip() for x in info_extension_cert(cert)["crlDistributionPoints"].split('\n')]
    crl_url = None
    for info in infos:
        print info
        if re.match(r"^URI:", info):
            crl_url = info.replace("URI:","")
            break
    return crl_url.strip()

cert_parent is the file with ROOT.crt and ISSUER.crt concatenates together. cert_parent_crl is the file with ROOT.crt, ISSUER.crt and CRL concatenates together.

To concat the CRL with other certificate I use:

rm FILE.crl
wget http://URL/FILE.crl
cat ROOT_ISSUER.crt > ROOT_ISSUER_CRL.crt
echo "-----BEGIN X509 CRL-----" >> ROOT_ISSUER_CRL.crt
openssl enc -base64 -in FILE.crl >> ROOT_ISSUER_CRL.crt
echo "-----END X509 CRL-----" >> ROOT_ISSUER_CRL.crt

OTHER TIPS

Try also https://github.com/erny/pyx509. This needs pyasn1 & pyasn1-modules. And it's python only:

./pkcs7_parse <pkcs7 signature in DER format>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top