Overview

Namespaces

  • BSN
    • CryptUser

Classes

  • CryptJSONSource
  • CryptMySQLSource
  • CryptUser
  • SSLKey

Interfaces

  • CryptDataSource
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: /*
  3: * Copyright (C) 2013 Bryan Nielsen - All Rights Reserved
  4: *
  5: * Author: Bryan Nielsen (bnielsen1965@gmail.com)
  6: *
  7: *
  8: * This file is part of cryptUser.
  9: * cryptUser is free software: you can redistribute it and/or modify
 10: * it under the terms of the GNU General Public License as published by
 11: * the Free Software Foundation, either version 3 of the License, or
 12: * (at your option) any later version.
 13: * 
 14: * cryptUser is distributed in the hope that it will be useful,
 15: * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 17: * GNU General Public License for more details.
 18: * 
 19: * You should have received a copy of the GNU General Public License
 20: * along with cryptUser.  If not, see <http://www.gnu.org/licenses/>.
 21: */
 22: 
 23: namespace BSN\CryptUser;
 24: use Exception;
 25: 
 26: /**
 27:  * Provides encryption and decryption methods using openssl.
 28:  *
 29:  * @author Bryan Nielsen, bnielsen1965@gmail.com
 30:  * @copyright Copyright 2013, Bryan Nielsen
 31:  */
 32: class SSLKey {
 33:     
 34:     /**
 35:      * Class properties
 36:      */
 37:     private $passphrase; // pass phrase used to protect private key
 38:     private $privateKey; // passphrase protected private key in PEM format
 39:     private $certificate; // SSL certificate in PEM format
 40:     private $errors; // error messages
 41: 
 42:     
 43:     /**
 44:      * Object constructor
 45:      * @param string $phrase Passphrase to use with protected key.
 46:      * @param string $key A PEM encoded private key, NULL when creating a new key pair.
 47:      * @param string $cert A PEM encoded certificate, NULL when creating a new key pair.
 48:      * @param array $SSLParams Associative array of parameters used when creating a new 
 49:      * key pair. The array is a combination of the Distinquished Name and configargs 
 50:      * values. The array may include the following...
 51:      *  'ssl_key_bits' => '4096', // bit size of the encryption key
 52:      *  'private_key_type' => OPENSSL_KEYTYPE_RSA, // the openssl key type
 53:      *  'countryName' => 'US', // distinquished name country
 54:      *  'stateOrProvinceName' => 'Nevada', // distinquished name province
 55:      *  'localityName' => 'Las Vegas', // distinquished name locality
 56:      *  'organizationName' => 'SSLKey', // distinquished name organization
 57:      *  'commonName' => 'localhost', // distinquished name domain
 58:      *  'emailAddress' => 'admin@localhost' // distinquished name email
 59:      * @param array $CACert Associative array containing a certificate authority parameters
 60:      * used to sign a new key pair. If NULL then the new key pair will be self signed. 
 61:      * The array must include the following elements...
 62:      *  'certificate' => CA certificate in PEM format,
 63:      *  'privateKey' => CA private key in PEM format,
 64:      *  'passPhrase' => CA passphrase.
 65:      */
 66:     function __construct($phrase = "", $key = NULL, $cert = NULL, $SSLParams = NULL, $CACert = NULL) {
 67:         // initialize error array
 68:         $this->errors = array();
 69:         
 70:         // if a key and cert were not passed then this is a new user, generate ssl keys
 71:         if ($key == NULL || $cert == NULL) {
 72:             // create a distinguished name using SSL parameters
 73:             $dn = $this->makeDN($SSLParams);
 74:             
 75:             // create the configargs array
 76:             $configArgs = $this->makeConfigArgs($SSLParams);// array();
 77:         
 78:             // generate a new key pair
 79:             $keyPair = openssl_pkey_new($configArgs);
 80: 
 81:             // export the private key in PEM format and encrypt with new user password
 82:             if (openssl_pkey_export($keyPair, $key, $phrase)) {
 83:                 // generate a certificate signing request with the defined dn identity
 84:                 if (($csr = openssl_csr_new($dn, $keyPair))) {
 85:                     if (is_null($CACert)) {
 86:                         // self sign the csr to generate the master certificate
 87:                         $rawCert = openssl_csr_sign($csr, null, $keyPair, 0);
 88:                     } else {
 89:                         // certificate authority sisgned csr to generate a user certificate
 90:                         $rawCert = openssl_csr_sign($csr, $CACert['certificate'], array($CACert['privateKey'], $CACert['passPhrase']), 0);
 91:                     }
 92: 
 93:                     if ($rawCert) {
 94:                         // export certificate in PEM format
 95:                         if (!openssl_x509_export($rawCert, $cert))
 96:                             $this->errors[] = "Error exporting certificate in PEM format. ";
 97:                     }
 98:                     else $this->error[] = "Error signing certificate. ";
 99:                 }
100:                 else $this->errors[] = "Error generating certificate signing request. ";
101:             }
102:             else $this->errors[] = "Error exporting private key. ";
103:         }
104: 
105:         $this->privateKey = $key;
106:         $this->passphrase = $phrase;
107:         $this->certificate = $cert;
108:     }
109: 
110: 
111:     /**
112:      * Decrypt the sealed package using this SSL key pair.
113:      * @param string $package The package to decrypt.
114:      * @param mixed $envelope The envelope for the package.
115:      * @param string $phrase The passphrase to use as the pass phrase for decryption.
116:      * @return mixed The decrypted package or FALSE if there was an error.
117:      */
118:     public function decryptPackage($package, $envelope, $phrase = NULL) {
119:         $decrypted = NULL;
120:         
121:         // use the passphrase from this instance if not provided in arguments
122:         if ($phrase == NULL) $phrase = $this->passphrase;
123:         
124:         // try to get the private key from the certificate
125:         if (($key = openssl_get_privatekey($this->privateKey, $phrase)) === FALSE) {
126:             $this->errors[] = 'Failed to get private key!';
127:             return NULL;
128:         }
129:         
130:         // try to decrypt the package
131:         if ((openssl_open($package, $decrypted, $envelope, $key)) === FALSE) {
132:             $this->errors[] = 'Failed to decrypt package!';
133:             return NULL;
134:         }
135:         
136:         return $decrypted;
137:     }
138: 
139:     
140:     /**
141:      * Encrypt a package using this SSL key pair.
142:      * @param mixed $package The package to encrypt.
143:      * @return array An array containing the envelope and encrypted package or NULL if an error occurs.
144:      */
145:     public function encryptPackage($package) {
146:         $encrypted = NULL;
147:         $envelope = NULL;
148:         
149:         if (is_null($package)) {
150:             $this->errors[] = 'No package provided to encrypt!';
151:             return NULL;
152:         }
153:         
154:         // try to get the public key from the certificate
155:         if (($key = openssl_get_publickey($this->certificate)) === FALSE) {
156:             $this->errors[] = 'Failed to get public key from certificate!';
157:             return NULL;
158:         }
159:         
160:         // try to encrypt the package
161:         if ((openssl_seal($package, $encrypted, $envelope, array($key))) === FALSE) {
162:             $this->errors[] = 'Failed to encrypt package!';
163:             return NULL;
164:         }
165:         
166:         // return the encrypted package and the envelope in an array
167:         return array("package" => $encrypted, "envelope" => $envelope[0]);
168:     }
169: 
170:     
171:     /**
172:      * Parses the passed string looking for a phrase using PEM style
173:      * encoding. This is a custom PEM parameter for SSLKey.
174:      * @param string $str The string to parse looking for the pass phrase component.
175:      * @param boolean $includeWrapper (optional) Specify if the PEM wrapper should be returned with the phrase.
176:      * @return string The discovered pass phrase or NULL if not found.
177:      */
178:     public static function parsePhrase($str, $includeWrapper = FALSE) {
179:         if ($includeWrapper) {
180:             $regEx = '/(-----BEGIN PHRASE-----.*-----END PHRASE-----)/msU';
181:         }
182:         else {
183:             $regEx = '/-----BEGIN PHRASE-----(.*)-----END PHRASE-----/msU';
184:         }
185:         if (preg_match($regEx, $str, $ma, PREG_OFFSET_CAPTURE)) {
186:             return trim($ma[1][0]);
187:         }
188: 
189:         return NULL;
190:     }
191: 
192:     
193:     /**
194:      * Parses the passed string looking for a certificate. 
195:      * @param string $str The string to parse looking for the certificate component.
196:      * @return string The discovered certificate or NULL if not found.
197:      */
198:     public static function parseCertificate($str) {
199:         if (preg_match('/(-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----)/msU', $str, $ma, PREG_OFFSET_CAPTURE)) {
200:             return trim($ma[1][0]);
201:         }
202: 
203:         return NULL;
204:     }
205: 
206:     
207:     /**
208:      * Parses the passed string looking for a private key. 
209:      * @param string $str The string to parse looking for the private key component.
210:      * @return string The discovered private key or NULL if not found.
211:      */
212:     public static function parsePrivateKey($str) {
213:         if (preg_match('/(-----BEGIN ENCRYPTED PRIVATE KEY-----.*-----END ENCRYPTED PRIVATE KEY-----)/msU', $str, $ma, PREG_OFFSET_CAPTURE)) {
214:             return trim($ma[1][0]);
215:         }
216: 
217:         return NULL;
218:     }
219: 
220:     
221:     /**
222:      * Creates a random passphrase.
223:      * @param integer $len The length of the generated pass phrase.
224:      * @param boolean $alphnumeric Determines if the generated pass phrase includes only alphanumeric characters.
225:      * @return string The generated pass phrase.
226:      */
227:     public static function makePhrase($len = 64, $alphanumeric = FALSE) {
228:         // determine the character set to use
229:         if ($alphanumeric === TRUE) {
230:             $charlist = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
231:         }
232:         else
233:             $charlist = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#&*(),.{}[];:";
234:         $phrase = "";
235:         
236:         // loop to create random phrase
237:         do {
238:             $phrase .= substr($charlist, mt_rand(0, strlen($charlist) - 1), 1);
239:         } while (--$len > 0);
240: 
241:         return $phrase;
242:     }
243: 
244:     
245:     /**
246:      * Retrieve the private key from this instance.
247:      */
248:     public function getPrivateKey() {
249:         return $this->privateKey;
250:     }
251: 
252:     
253:     /**
254:      * Retrieve the certificate from this instance.
255:      */
256:     public function getCertificate() {
257:         return $this->certificate;
258:     }
259:     
260:     
261:     /**
262:      * Retrieve the PEM encoded key (privateKey + certificate)
263:      */
264:     public function getKey() {
265:         $key = $this->getPrivateKey();
266:         $key .- $this->getCertificate();
267:         return $key;
268:     }
269:     
270:     
271:     /**
272:      * Retrieve a custom PEM encoded key with all needed components for encryption and decryption.
273:      */
274:     public function getFullKey() {
275:         // assemble all key components into a single PEM string.
276:         $key = "-----BEGIN PHRASE-----\n" . $this->passphrase . "\n-----END PHRASE-----\n\n";
277:         $key .= $this->getKey();
278:         /*
279:         $key .= $this->getPrivateKey();
280:         $key .= $this->getCertificate();
281:          */
282:         return $key;
283:     }
284: 
285: 
286:     /**
287:      * Creates a distinquished name array for SSL certificate generation.
288:      * @param array $sslParams The parameters to use when generating the distinquished name.
289:      */
290:     public function makeDN($sslParams = NULL) {
291:         $newName = array();
292:         
293:         // fields to be constructed and their default values
294:         $parameterFields = array(
295:             'countryName' => 'XX',
296:             'stateOrProvinceName' => 'XXXX',
297:             'localityName' => 'XXXX',
298:             'organizationName' => 'SSLKey',
299:             'commonName' => 'localhost',
300:             'emailAddress' => 'admin@localhost'
301:         );
302:         
303:         // build Distinquished Name from parameters and defaults
304:         foreach ($parameterFields as $field => $parameter) {
305:             if (isset($sslParams[$field])) $newName[$field] = $sslParams[$field];
306:             else $newName[$field] = $parameter;
307:         }
308:         
309:         return $newName;
310:     }
311:     
312:     
313:     /**
314:      * Creates a configArgs array to be used when creating certificates
315:      * @param array $sslParams The parameters to use when generating the configArgs.
316:      */
317:     public function makeConfigArgs($sslParams = NULL) {
318:         $newConfig = array();
319:         
320:         // fields to be constructed and their default values
321:         $parameterFields = array(
322:             'private_key_bits' => 1024,
323:             'private_key_type' => OPENSSL_KEYTYPE_RSA
324:         );
325:         
326:         // build configArgs from parameters and defaults
327:         foreach ($parameterFields as $field => $value) {
328:             if (isset($SSLParams[$field])) $newConfig[$field] = $SSLParams[$field];
329:             else $newConfig[$field] = $value;
330:         }
331:         
332:         return $newConfig;
333:     }
334:     
335:     
336:     /**
337:      * Get the current errors.
338:      */
339:     public function getErrors() {
340:         return $this->errors;
341:     }
342: 
343: }
344: 
345: 
cryptUser API documentation generated by ApiGen 2.8.0