1<?php 2 3/** 4 * DSA Private Key 5 * 6 * @category Crypt 7 * @package DSA 8 * @author Jim Wigginton <terrafrost@php.net> 9 * @copyright 2015 Jim Wigginton 10 * @license http://www.opensource.org/licenses/mit-license.html MIT License 11 * @link http://phpseclib.sourceforge.net 12 */ 13 14namespace phpseclib3\Crypt\DSA; 15 16use phpseclib3\Crypt\Common; 17use phpseclib3\Crypt\DSA; 18use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature; 19use phpseclib3\Math\BigInteger; 20 21/** 22 * DSA Private Key 23 * 24 * @package DSA 25 * @author Jim Wigginton <terrafrost@php.net> 26 * @access public 27 */ 28class PrivateKey extends DSA implements Common\PrivateKey 29{ 30 use Common\Traits\PasswordProtected; 31 32 /** 33 * DSA secret exponent x 34 * 35 * @var \phpseclib3\Math\BigInteger 36 * @access private 37 */ 38 protected $x; 39 40 /** 41 * Returns the public key 42 * 43 * If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key 44 * that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING. 45 * An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this 46 * parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g 47 * variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified 48 * by getting a DSA PKCS8 public key: 49 * 50 * "openssl dsa -in private.dsa -pubout -outform PEM" 51 * 52 * ie. just swap out rsa with dsa in the rsa command above. 53 * 54 * A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA 55 * the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature 56 * without the parameters and the PKCS1 DSA public key format does not include the parameters. 57 * 58 * @see self::getPrivateKey() 59 * @access public 60 * @return mixed 61 */ 62 public function getPublicKey() 63 { 64 $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); 65 66 if (!isset($this->y)) { 67 $this->y = $this->g->powMod($this->x, $this->p); 68 } 69 70 $key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y); 71 72 return DSA::loadFormat('PKCS8', $key) 73 ->withHash($this->hash->getHash()) 74 ->withSignatureFormat($this->shortFormat); 75 } 76 77 /** 78 * Create a signature 79 * 80 * @see self::verify() 81 * @access public 82 * @param string $message 83 * @return mixed 84 */ 85 public function sign($message) 86 { 87 $format = $this->sigFormat; 88 89 if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { 90 $signature = ''; 91 $result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash()); 92 93 if ($result) { 94 if ($this->shortFormat == 'ASN1') { 95 return $signature; 96 } 97 98 extract(ASN1Signature::load($signature)); 99 100 return $format::save($r, $s); 101 } 102 } 103 104 $h = $this->hash->hash($message); 105 $h = $this->bits2int($h); 106 107 while (true) { 108 $k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one)); 109 $r = $this->g->powMod($k, $this->p); 110 list(, $r) = $r->divide($this->q); 111 if ($r->equals(self::$zero)) { 112 continue; 113 } 114 $kinv = $k->modInverse($this->q); 115 $temp = $h->add($this->x->multiply($r)); 116 $temp = $kinv->multiply($temp); 117 list(, $s) = $temp->divide($this->q); 118 if (!$s->equals(self::$zero)) { 119 break; 120 } 121 } 122 123 // the following is an RFC6979 compliant implementation of deterministic DSA 124 // it's unused because it's mainly intended for use when a good CSPRNG isn't 125 // available. if phpseclib's CSPRNG isn't good then even key generation is 126 // suspect 127 /* 128 $h1 = $this->hash->hash($message); 129 $k = $this->computek($h1); 130 $r = $this->g->powMod($k, $this->p); 131 list(, $r) = $r->divide($this->q); 132 $kinv = $k->modInverse($this->q); 133 $h1 = $this->bits2int($h1); 134 $temp = $h1->add($this->x->multiply($r)); 135 $temp = $kinv->multiply($temp); 136 list(, $s) = $temp->divide($this->q); 137 */ 138 139 return $format::save($r, $s); 140 } 141 142 /** 143 * Returns the private key 144 * 145 * @param string $type 146 * @param array $options optional 147 * @return string 148 */ 149 public function toString($type, array $options = []) 150 { 151 $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); 152 153 if (!isset($this->y)) { 154 $this->y = $this->g->powMod($this->x, $this->p); 155 } 156 157 return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password, $options); 158 } 159} 160