* SignedData ::= SEQUENCE { * version CMSVersion, * digestAlgorithms DigestAlgorithmIdentifiers, * encapContentInfo EncapsulatedContentInfo, * certificates [0] IMPLICIT CertificateSet OPTIONAL, * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, * signerInfos SignerInfos * } * * * @package asn1 * @subpackage cms * * @link http://tools.ietf.org/html/rfc3852#section-5 RFC 3852: Cryptographic Message Syntax */ class CMSSignedData implements ASN1DEREncodable { /** * Object identifier for the Signed-Data content type. */ const OID = "1.2.840.113549.1.7.2"; private $version; private $digestAlgorithms; private $encapsulatedContent; private $certificates; private $signerInfos; /** * Constructs a new instance of CMSSignedData. * */ public function __construct() { } /** * Decodes the given ASN1Sequence as CMSSignedData. * * @throws GTException * @param ASN1Sequence $object CMSSignedData encoded as an ASN1Sequence. * @return void */ public function decode($object) { if (!$object instanceof ASN1Sequence) { throw new GTException("Expecting an ASN1Sequence"); } $size = $object->getObjectCount(); if ($size < 4 || $size > 6) { throw new GTException("Invalid sequence size: {$size}"); } $version = $object->getObjectAt(0); if (!$version instanceof ASN1Integer) { throw new GTException("Expecting an ASN1Integer"); } $this->version = (int) $version->getValue(); if ($this->version !== 3 && $this->version != 1) { throw new GTException("Invalid version: {$this->version}"); } $set = $object->getObjectAt(1); if (!$set instanceof ASN1Set) { throw new GTException("Expecting an ASN1Set"); } $digestAlgorithms = array(); foreach ($set->getObjects() as $item) { $digestAlgorithm = new X509AlgorithmIdentifier(); $digestAlgorithm->decode($item); array_push($digestAlgorithms, $digestAlgorithm); } $this->digestAlgorithms = $digestAlgorithms; $encapsulatedContent = new CMSEncapsulatedContentInfo(); $encapsulatedContent->decode($object->getObjectAt(2)); $this->encapsulatedContent = $encapsulatedContent; for ($i = 3; $i < $object->getObjectCount(); $i++) { $item = $object->getObjectAt($i); if ($item instanceof ASN1Tag) { switch ($item->getTagValue()) { case 0: // certificates $this->certificates = array(); $set = $item->getObjectAs(ASN1_TAG_SET); if ($set->getObjectCount() == 0) { throw new GTException("Certificates size: 0"); } foreach ($set->getObjects() as $certificate) { if (!$certificate instanceof ASN1Sequence) { throw new GTException("Certificate should be encoded as an ASN1Sequence"); } if ($certificate->getObjectCount() == 0) { throw new GTException("Certificate content missing"); } array_push($this->certificates, $certificate); } break; case 1: // certificate revocation list throw new GTException("Certificate revocation lists not implemented"); break; default: throw new GTException("Unknown tag value: {$item->getTagValue()}"); } } else if ($item instanceof ASN1Set) { $this->signerInfos = array(); if ($item->getObjectCount() == 0) { throw new GTException("SignerInfos size: 0"); } foreach ($item->getObjects() as $info) { $signerInfo = new CMSSignerInfo(); $signerInfo->decode($info); array_push($this->signerInfos, $signerInfo); } } else { throw new GTException("Unkonwn child of CMSSignedData: " . get_class($item)); } } } /** * Encodes this CMSSignedData using DER. * * @return array byte array that contains the DER encoding of this CMSSignedData */ public function encodeDER() { $sequence = new ASN1Sequence(); $sequence->add(new ASN1Integer((int) $this->version)); $digestAlgorithms = new ASN1Set(); foreach ($this->digestAlgorithms as $digestAlgorithm) { $digestAlgorithms->add($digestAlgorithm); } $sequence->add($digestAlgorithms); $sequence->add($this->encapsulatedContent); if (!empty($this->certificates)) { $certificates = new ASN1Set(); foreach ($this->certificates as $certificate) { $certificates->add($certificate); } $certificatesTag = new ASN1Tag(); $certificatesTag->setExplicit(false); $certificatesTag->setTagClass(ASN1_TAG_CONTEXT); $certificatesTag->setTagType(ASN1_TAG_CONSTRUCTED); $certificatesTag->setTagValue(0); $certificatesTag->setObject($certificates); $sequence->add($certificatesTag); } $signerInfos = new ASN1Set(); foreach ($this->signerInfos as $signerInfo) { $signerInfos->add($signerInfo); } $sequence->add($signerInfos); return $sequence->encodeDER(); } /** * Gets the digest algorithms. * * @return array array of X509AlgorithmIdentifer */ public function getDigestAlgorithms() { return $this->digestAlgorithms; } /** * Gets the encapsulated content. * * @return CMSEncapsulatedContentInfo the encapsulated content */ public function getEncapsulatedContent() { return $this->encapsulatedContent; } /** * Gets the certificates. * * @return array certificates as raw unparsed ASN1Sequence instances */ public function getCertificates() { return $this->certificates; } /** * Sets the certificates. * * @param array $certificates certufificates as raw unparsed ASN1Sequence instances * @return void */ public function setCertificates($certificates) { $this->certificates = $certificates; } /** * Gets the signer infos. * * @return array of CMSSignerInfo */ public function getSignerInfos() { return $this->signerInfos; } /** * Convenience method for timestamps to get the single signer info. * * * @return CMSSignerInfo the first signer info */ public function getSignerInfo() { if (isset($this->signerInfos[0])) { return $this->signerInfos[0]; } return null; } /** * Gets the version. * * @return int version */ public function getVersion() { return $this->version; } } ?>