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 x509 23 */ 24 25/** 26 * X.509 Certificate implementation. 27 * 28 * This class treats X.509 certificates as BLOBs and doesn't 29 * actually fully implement ASN.1 decoding/encoding of X509Certificates. 30 * 31 * Wrapper methods are provided for PHP's openssl_xxx functions. 32 * 33 * @package asn1 34 * @subpackage x509 35 */ 36class X509Certificate { 37 38 private $bytes; 39 40 private $cert; 41 private $data; 42 private $pkey; 43 44 /** 45 * Constructs a new instance of X509Certificate. 46 * 47 * @throws GTException 48 * @param array|ASN1DEREncodable $data 49 * @return void 50 */ 51 public function __construct($data) { 52 53 if (is_array($data)) { 54 $this->bytes = $data; 55 56 } else if ($data instanceof ASN1DEREncodable) { 57 $this->bytes = $data->encodeDER(); 58 59 } else { 60 throw new GTException("paramater data must be an array of bytes or an instance of ASN1DEREncodable"); 61 62 } 63 } 64 65 /** 66 * Destructs this X509Certificate. 67 * 68 * This method frees any allocated OpenSSL resources. 69 * 70 */ 71 public function __destruct() { 72 73 if ($this->pkey !== null && $this->pkey !== false) { 74 openssl_pkey_free($this->pkey); 75 } 76 77 if ($this->cert !== null && $this->cert !== false) { 78 openssl_x509_free($this->cert); 79 } 80 81 } 82 83 /** 84 * PEM encodes this certificate. 85 * 86 * @return string pem encoded certificate 87 */ 88 public function encodePEM() { 89 90 $pem = ""; 91 $pem .= "-----BEGIN CERTIFICATE-----\r\n"; 92 93 $body = GTBase64::encode($this->bytes); 94 $body = wordwrap($body, 64, "\r\n", true); 95 96 if ($body{strlen($body) - 1} != "\n") { 97 $body .= "\r\n"; 98 } 99 100 $pem .= $body; 101 $pem .= "-----END CERTIFICATE-----\r\n"; 102 103 return $pem; 104 105 } 106 107 /** 108 * Verifies the given signature using this certificate's public key. 109 * 110 * @param array $data byte array containing the data bytes that were signed 111 * @param array $sign byte array containing the signature bytes 112 * @param string $algorithm the hash algorithm to use 113 * @return bool true if the signature is valid 114 */ 115 public function verifySignature($data, $sign, $algorithm = 'sha256') { 116 117 if (empty($data)) { 118 throw new GTException("Parameter data is required"); 119 } 120 121 if (empty($sign)) { 122 throw new GTException("Parameter sign is required"); 123 } 124 125 if ($this->cert === null || $this->cert === false) { 126 $this->cert = openssl_x509_read($this->encodePEM()); 127 } 128 129 if ($this->pkey == null || $this->pkey === false) { 130 $this->pkey = openssl_pkey_get_public($this->cert); 131 } 132 133 return X509Certificate::verifyPublicKeySignature($this->pkey, $data, $sign, $algorithm); 134 135 } 136 137 /** 138 * Gets the certificate parameters. 139 * 140 * @return array containing the certificate parameters 141 * @see openssl_x509_parse 142 */ 143 public function getParameters() { 144 145 if ($this->cert === null || $this->cert === false) { 146 $this->cert = openssl_x509_read($this->encodePEM()); 147 } 148 149 if ($this->data === null) { 150 $this->data = openssl_x509_parse($this->cert); 151 } 152 153 return $this->data; 154 155 } 156 157 /** 158 * Gets this certificate's public key. 159 * 160 * @return resource OpenSSL public key resource 161 * @see openssl_pkey_get_public 162 * 163 */ 164 public function getPublicKey() { 165 166 if ($this->cert === null || $this->cert === false) { 167 $this->cert = openssl_x509_read($this->encodePEM()); 168 } 169 170 if ($this->pkey === null || $this->pkey === false) { 171 $this->pkey = openssl_pkey_get_public($this->cert); 172 } 173 174 return $this->pkey; 175 } 176 177 /** 178 * Checks if this certificate is valid for given purpose. 179 * 180 * @throws GTException 181 * @param int $purpose purpose 182 * @param array $chain certificate chain 183 * @param array $cainfo root certificates 184 * @return bool true if this certificate is valid 185 * 186 * @see openssl_x509_checkpurpose 187 */ 188 public function isValid($purpose, $chain = array(), $cainfo = array()) { 189 190 if ($this->cert === null || $this->cert === false) { 191 $this->cert = openssl_x509_parse($this->encodePEM()); 192 } 193 194 // $untrustedFile = tempnam(sys_get_temp_dir(), "guardtime_ca"); 195 $untrustedFile = tempnam(DOKU_INC.'/data/tmp/', "guardtime_ca"); 196 197 198 foreach ($chain as $cert) { 199 GTUtil::write($untrustedFile, GTUtil::toByteArray($cert->encodePEM())); 200 } 201 202 if ($cainfo == null) { 203 $cainfo = array(); 204 205 } else if (is_string($cainfo)) { 206 207 if (!is_file($cainfo)) { 208 throw new GTException("Specified cainfo file does not exist: {$cainfo}"); 209 } 210 211 $cainfo = array($cainfo); 212 213 } else { 214 throw new GTException("Invalid cainfo specified: " . $cainfo); 215 216 } 217 218 $result = openssl_x509_checkpurpose($this->cert, $purpose, $cainfo, $untrustedFile) === true; 219 220 unlink($untrustedFile); 221 222 return $result; 223 224 } 225 226 /** 227 * Gets the public key hash for given public key. 228 * 229 * @static 230 * @throws GTException 231 * @param resource $pkey OpenSSL public key resource 232 * @return GTDataHash public key hash 233 */ 234 public static function getPublicKeyHash($pkey) { 235 236 if (!is_resource($pkey)) { 237 throw new GTException("Parameter pkey must be a valid resource of type OpenSSL key"); 238 } 239 240 $params = openssl_pkey_get_details($pkey); 241 242 $string = $params["key"]; 243 $string = str_replace("-----BEGIN PUBLIC KEY-----", "", $string); 244 $string = str_replace("-----END PUBLIC KEY-----", "", $string); 245 $string = str_replace("\r", "", $string); 246 $string = str_replace("\n", "", $string); 247 248 $bytes = GTBase64::decode($string); 249 250 $hash = new GTDataHash(GTHashAlgorithm::getByName('SHA256')); 251 $hash->update($bytes); 252 $hash->close(); 253 254 return $hash; 255 } 256 257 258 /** 259 * Verifies public key signature. 260 * 261 * @static 262 * @throws GTException 263 * @param resource $pkey public key to use for verification 264 * @param array $data array of data bytes 265 * @param array $sign array of signature bytes 266 * @param string $algorithm the hash algorithm to use 267 * @return bool true if signature is valid 268 */ 269 public static function verifyPublicKeySignature($pkey, $data, $sign, $algorithm = 'sha256') { 270 271 if (empty($data)) { 272 throw new GTException("Parameter data is required"); 273 } 274 275 if (empty($sign)) { 276 throw new GTException("Parameter sign is required"); 277 } 278 279 if (!is_resource($pkey)) { 280 throw new GTException("Parameter pkey must be a valid resource of type OpenSSL key"); 281 } 282 283 $data = GTUtil::fromByteArray($data); 284 $sign = GTUtil::fromByteArray($sign); 285 286 return openssl_verify($data, $sign, $pkey, $algorithm) === 1; 287 288 } 289} 290 291?> 292