1<?php 2 3/** 4 * PKCS#8 Formatted DSA Key Handler 5 * 6 * PHP version 5 7 * 8 * Processes keys with the following headers: 9 * 10 * -----BEGIN ENCRYPTED PRIVATE KEY----- 11 * -----BEGIN PRIVATE KEY----- 12 * -----BEGIN PUBLIC KEY----- 13 * 14 * Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8 15 * is specific to private keys it's basically creating a DER-encoded wrapper 16 * for keys. This just extends that same concept to public keys (much like ssh-keygen) 17 * 18 * @category Crypt 19 * @package DSA 20 * @author Jim Wigginton <terrafrost@php.net> 21 * @copyright 2015 Jim Wigginton 22 * @license http://www.opensource.org/licenses/mit-license.html MIT License 23 * @link http://phpseclib.sourceforge.net 24 */ 25 26namespace phpseclib3\Crypt\DSA\Formats\Keys; 27 28use phpseclib3\Common\Functions\Strings; 29use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor; 30use phpseclib3\File\ASN1; 31use phpseclib3\File\ASN1\Maps; 32use phpseclib3\Math\BigInteger; 33 34/** 35 * PKCS#8 Formatted DSA Key Handler 36 * 37 * @package DSA 38 * @author Jim Wigginton <terrafrost@php.net> 39 * @access public 40 */ 41abstract class PKCS8 extends Progenitor 42{ 43 /** 44 * OID Name 45 * 46 * @var string 47 * @access private 48 */ 49 const OID_NAME = 'id-dsa'; 50 51 /** 52 * OID Value 53 * 54 * @var string 55 * @access private 56 */ 57 const OID_VALUE = '1.2.840.10040.4.1'; 58 59 /** 60 * Child OIDs loaded 61 * 62 * @var bool 63 * @access private 64 */ 65 protected static $childOIDsLoaded = false; 66 67 /** 68 * Break a public or private key down into its constituent components 69 * 70 * @access public 71 * @param string $key 72 * @param string $password optional 73 * @return array 74 */ 75 public static function load($key, $password = '') 76 { 77 if (!Strings::is_stringable($key)) { 78 throw new \UnexpectedValueException('Key should be a string - not a ' . gettype($key)); 79 } 80 81 $isPublic = strpos($key, 'PUBLIC') !== false; 82 83 $key = parent::load($key, $password); 84 85 $type = isset($key['privateKey']) ? 'privateKey' : 'publicKey'; 86 87 switch (true) { 88 case !$isPublic && $type == 'publicKey': 89 throw new \UnexpectedValueException('Human readable string claims non-public key but DER encoded string claims public key'); 90 case $isPublic && $type == 'privateKey': 91 throw new \UnexpectedValueException('Human readable string claims public key but DER encoded string claims private key'); 92 } 93 94 $decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element); 95 if (empty($decoded)) { 96 throw new \RuntimeException('Unable to decode BER of parameters'); 97 } 98 $components = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP); 99 if (!is_array($components)) { 100 throw new \RuntimeException('Unable to perform ASN1 mapping on parameters'); 101 } 102 103 $decoded = ASN1::decodeBER($key[$type]); 104 if (empty($decoded)) { 105 throw new \RuntimeException('Unable to decode BER'); 106 } 107 108 $var = $type == 'privateKey' ? 'x' : 'y'; 109 $components[$var] = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP); 110 if (!$components[$var] instanceof BigInteger) { 111 throw new \RuntimeException('Unable to perform ASN1 mapping'); 112 } 113 114 if (isset($key['meta'])) { 115 $components['meta'] = $key['meta']; 116 } 117 118 return $components; 119 } 120 121 /** 122 * Convert a private key to the appropriate format. 123 * 124 * @access public 125 * @param \phpseclib3\Math\BigInteger $p 126 * @param \phpseclib3\Math\BigInteger $q 127 * @param \phpseclib3\Math\BigInteger $g 128 * @param \phpseclib3\Math\BigInteger $y 129 * @param \phpseclib3\Math\BigInteger $x 130 * @param string $password optional 131 * @param array $options optional 132 * @return string 133 */ 134 public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = []) 135 { 136 $params = [ 137 'p' => $p, 138 'q' => $q, 139 'g' => $g 140 ]; 141 $params = ASN1::encodeDER($params, Maps\DSAParams::MAP); 142 $params = new ASN1\Element($params); 143 $key = ASN1::encodeDER($x, Maps\DSAPublicKey::MAP); 144 return self::wrapPrivateKey($key, [], $params, $password, null, '', $options); 145 } 146 147 /** 148 * Convert a public key to the appropriate format 149 * 150 * @access public 151 * @param \phpseclib3\Math\BigInteger $p 152 * @param \phpseclib3\Math\BigInteger $q 153 * @param \phpseclib3\Math\BigInteger $g 154 * @param \phpseclib3\Math\BigInteger $y 155 * @param array $options optional 156 * @return string 157 */ 158 public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = []) 159 { 160 $params = [ 161 'p' => $p, 162 'q' => $q, 163 'g' => $g 164 ]; 165 $params = ASN1::encodeDER($params, Maps\DSAParams::MAP); 166 $params = new ASN1\Element($params); 167 $key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP); 168 return self::wrapPublicKey($key, $params); 169 } 170} 171