1<?php 2/* 3 * Copyright 2008-2010 GuardTime AS 4 * 5 * This file is part of the GuardTime PHP SDK. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20/** 21 * @package asn1 22 * @subpackage cms 23 */ 24 25/** 26 * CMS SignedData implementation. 27 * 28 * <pre> 29 * SignedData ::= SEQUENCE { 30 * version CMSVersion, 31 * digestAlgorithms DigestAlgorithmIdentifiers, 32 * encapContentInfo EncapsulatedContentInfo, 33 * certificates [0] IMPLICIT CertificateSet OPTIONAL, 34 * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, 35 * signerInfos SignerInfos 36 * } 37 * </pre> 38 * 39 * @package asn1 40 * @subpackage cms 41 * 42 * @link http://tools.ietf.org/html/rfc3852#section-5 RFC 3852: Cryptographic Message Syntax 43 */ 44class CMSSignedData implements ASN1DEREncodable { 45 46 /** 47 * Object identifier for the Signed-Data content type. 48 */ 49 const OID = "1.2.840.113549.1.7.2"; 50 51 private $version; 52 private $digestAlgorithms; 53 private $encapsulatedContent; 54 private $certificates; 55 private $signerInfos; 56 57 /** 58 * Constructs a new instance of CMSSignedData. 59 * 60 */ 61 public function __construct() { 62 } 63 64 /** 65 * Decodes the given ASN1Sequence as CMSSignedData. 66 * 67 * @throws GTException 68 * @param ASN1Sequence $object CMSSignedData encoded as an ASN1Sequence. 69 * @return void 70 */ 71 public function decode($object) { 72 73 if (!$object instanceof ASN1Sequence) { 74 throw new GTException("Expecting an ASN1Sequence"); 75 } 76 77 $size = $object->getObjectCount(); 78 79 if ($size < 4 || $size > 6) { 80 throw new GTException("Invalid sequence size: {$size}"); 81 } 82 83 $version = $object->getObjectAt(0); 84 85 if (!$version instanceof ASN1Integer) { 86 throw new GTException("Expecting an ASN1Integer"); 87 } 88 89 $this->version = (int) $version->getValue(); 90 91 if ($this->version !== 3 && $this->version != 1) { 92 throw new GTException("Invalid version: {$this->version}"); 93 } 94 95 $set = $object->getObjectAt(1); 96 97 if (!$set instanceof ASN1Set) { 98 throw new GTException("Expecting an ASN1Set"); 99 } 100 101 $digestAlgorithms = array(); 102 103 foreach ($set->getObjects() as $item) { 104 105 $digestAlgorithm = new X509AlgorithmIdentifier(); 106 $digestAlgorithm->decode($item); 107 108 array_push($digestAlgorithms, $digestAlgorithm); 109 } 110 111 $this->digestAlgorithms = $digestAlgorithms; 112 113 $encapsulatedContent = new CMSEncapsulatedContentInfo(); 114 $encapsulatedContent->decode($object->getObjectAt(2)); 115 116 $this->encapsulatedContent = $encapsulatedContent; 117 118 for ($i = 3; $i < $object->getObjectCount(); $i++) { 119 120 $item = $object->getObjectAt($i); 121 122 if ($item instanceof ASN1Tag) { 123 124 switch ($item->getTagValue()) { 125 126 case 0: 127 // certificates 128 129 $this->certificates = array(); 130 131 $set = $item->getObjectAs(ASN1_TAG_SET); 132 133 if ($set->getObjectCount() == 0) { 134 throw new GTException("Certificates size: 0"); 135 } 136 137 foreach ($set->getObjects() as $certificate) { 138 139 if (!$certificate instanceof ASN1Sequence) { 140 throw new GTException("Certificate should be encoded as an ASN1Sequence"); 141 } 142 143 if ($certificate->getObjectCount() == 0) { 144 throw new GTException("Certificate content missing"); 145 } 146 147 array_push($this->certificates, $certificate); 148 149 } 150 151 break; 152 153 case 1: 154 // certificate revocation list 155 156 throw new GTException("Certificate revocation lists not implemented"); 157 158 break; 159 160 default: 161 throw new GTException("Unknown tag value: {$item->getTagValue()}"); 162 163 } 164 165 } else if ($item instanceof ASN1Set) { 166 167 $this->signerInfos = array(); 168 169 if ($item->getObjectCount() == 0) { 170 throw new GTException("SignerInfos size: 0"); 171 } 172 173 foreach ($item->getObjects() as $info) { 174 175 $signerInfo = new CMSSignerInfo(); 176 $signerInfo->decode($info); 177 178 array_push($this->signerInfos, $signerInfo); 179 } 180 181 } else { 182 throw new GTException("Unkonwn child of CMSSignedData: " . get_class($item)); 183 } 184 185 } 186 187 } 188 189 /** 190 * Encodes this CMSSignedData using DER. 191 * 192 * @return array byte array that contains the DER encoding of this CMSSignedData 193 */ 194 public function encodeDER() { 195 196 $sequence = new ASN1Sequence(); 197 $sequence->add(new ASN1Integer((int) $this->version)); 198 199 $digestAlgorithms = new ASN1Set(); 200 201 foreach ($this->digestAlgorithms as $digestAlgorithm) { 202 $digestAlgorithms->add($digestAlgorithm); 203 } 204 205 $sequence->add($digestAlgorithms); 206 $sequence->add($this->encapsulatedContent); 207 208 if (!empty($this->certificates)) { 209 210 $certificates = new ASN1Set(); 211 212 foreach ($this->certificates as $certificate) { 213 $certificates->add($certificate); 214 } 215 216 $certificatesTag = new ASN1Tag(); 217 $certificatesTag->setExplicit(false); 218 $certificatesTag->setTagClass(ASN1_TAG_CONTEXT); 219 $certificatesTag->setTagType(ASN1_TAG_CONSTRUCTED); 220 $certificatesTag->setTagValue(0); 221 $certificatesTag->setObject($certificates); 222 223 $sequence->add($certificatesTag); 224 } 225 226 $signerInfos = new ASN1Set(); 227 228 foreach ($this->signerInfos as $signerInfo) { 229 $signerInfos->add($signerInfo); 230 } 231 232 $sequence->add($signerInfos); 233 234 return $sequence->encodeDER(); 235 } 236 237 /** 238 * Gets the digest algorithms. 239 * 240 * @return array array of X509AlgorithmIdentifer 241 */ 242 public function getDigestAlgorithms() { 243 return $this->digestAlgorithms; 244 } 245 246 /** 247 * Gets the encapsulated content. 248 * 249 * @return CMSEncapsulatedContentInfo the encapsulated content 250 */ 251 public function getEncapsulatedContent() { 252 return $this->encapsulatedContent; 253 } 254 255 /** 256 * Gets the certificates. 257 * 258 * @return array certificates as raw unparsed ASN1Sequence instances 259 */ 260 public function getCertificates() { 261 return $this->certificates; 262 } 263 264 /** 265 * Sets the certificates. 266 * 267 * @param array $certificates certufificates as raw unparsed ASN1Sequence instances 268 * @return void 269 */ 270 public function setCertificates($certificates) { 271 $this->certificates = $certificates; 272 } 273 274 /** 275 * Gets the signer infos. 276 * 277 * @return array of CMSSignerInfo 278 */ 279 public function getSignerInfos() { 280 return $this->signerInfos; 281 } 282 283 /** 284 * Convenience method for timestamps to get the single signer info. 285 * 286 * 287 * @return CMSSignerInfo the first signer info 288 */ 289 public function getSignerInfo() { 290 291 if (isset($this->signerInfos[0])) { 292 return $this->signerInfos[0]; 293 } 294 295 return null; 296 } 297 298 /** 299 * Gets the version. 300 * 301 * @return int version 302 */ 303 public function getVersion() { 304 return $this->version; 305 } 306} 307 308?> 309