MKey.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. <?php
  2. namespace ACES\Common;
  3. use ACES\Common\Exception as Ex;
  4. use ACES\Common\Exception\MalformedException;
  5. use Common\Exception\SignException;
  6. class MKey
  7. {
  8. const IV_SIZE = 16;
  9. const RANDOM_SIZE = 4;
  10. const KEY_ID_LEN = 16;
  11. //1024 * 1024
  12. const MEGABYTE = 1048576;
  13. private $id;
  14. private $key;
  15. private $ver;
  16. private $service;
  17. private $keyUsage;
  18. private $keyType;
  19. private $skey;
  20. private $svkey; // for sign & verify
  21. private $isValid;
  22. private $effective;
  23. private $expired;
  24. private $key_digest;
  25. private $keyStatus;
  26. public function __construct(
  27. $service,
  28. $kid,
  29. $key,
  30. $kdigest,
  31. $kver,
  32. $effectiveTs,
  33. $expTs,
  34. $ktype,
  35. $kusage,
  36. $kstatus)
  37. {
  38. if(empty($kid) || empty($service))
  39. throw new MalformedException("ID and App fields cannot be null.");
  40. $this->service = $service;
  41. $this->id = $kid;
  42. $this->key = $key;
  43. if($kver < -1){
  44. throw new MalformedException("Invalid key version.");
  45. }
  46. $this->ver = $kver;
  47. $this->keyUsage = KEY_USAGE::fromValue($kusage);
  48. $this->keyStatus = KEY_STATUS::fromValue($kstatus);
  49. $this->keyType = KEY_TYPE::fromValue($ktype);
  50. $this->isValid = false;
  51. if(!empty($key)){
  52. $this->key = $key;
  53. $this->expired = $expTs;
  54. $this->effective = $effectiveTs;
  55. // TODO: due to cryptographic policy control, JDK only allow AES 128 bit
  56. $this->skey = substr($this->key, 0, 16);
  57. $this->svkey = $this->key;
  58. }
  59. $this->key_digest = $kdigest;
  60. $digest = base64_encode(hash(Constants::DEFAULT_CERTDIGEST_ALGO, $this->key, TRUE));
  61. if(strcmp($this->key_digest, $digest)==0){
  62. $this->isValid = TRUE;
  63. }
  64. }
  65. function __destruct()
  66. {
  67. // TODO - Insert your code here
  68. }
  69. public function encrypt($pt)
  70. {
  71. $ct = KeyEncryption::encrypt($this, $pt);
  72. $ct = pack("C",Constants::CIPHER_TYPE_WEAK) .pack("C",Constants::ALGO_TYPE_AES_CBC_128) . $this->id . $ct;
  73. return $ct;
  74. }
  75. public function strong_encrypt($pt){
  76. $de = new DataEncryption();
  77. $data_cipher = $de->encrypt($pt);
  78. $key_cipher = KeyEncryption::wrap($this, $de->exportKey());
  79. $ct = pack("C", Constants::CIPHER_TYPE_REGULAR) . pack("n", strlen($this->id)) .
  80. $this->id . pack("C", Constants::ALGO_TYPE_AES_CBC_128) . pack("n", strlen($key_cipher)) .
  81. $key_cipher . pack("C", Constants::ALGO_TYPE_AES_CBC_128) . pack("N", strlen($data_cipher)) . $data_cipher;
  82. return $ct;
  83. }
  84. public function strong_decrypt($ct){
  85. $offset = 0;
  86. $ctype_ = unpack("C", $ct[$offset]);
  87. $ctype = $ctype_[1];
  88. $offset += 1;
  89. if($ctype != Constants::CIPHER_TYPE_REGULAR){
  90. throw new MalformedException("Unmatched CipherText Type.");
  91. }
  92. $eidLen_ = unpack("n", substr($ct, $offset, 2));
  93. $eidLen = $eidLen_[1];
  94. $offset += 2;
  95. if($eidLen !== Constants::DEFAULT_KEYID_LEN){
  96. throw new MalformedException("Corrupted ciphertext header with illegal key id length.");
  97. }
  98. $eid = substr($ct, $offset, $eidLen);
  99. $offset += $eidLen;
  100. if ($this->id != $eid){
  101. throw new MalformedException("Unmatched MKey ID.");
  102. }
  103. $atype_ = unpack("C", substr($ct, $offset, 1));
  104. $atype = $atype_[1];
  105. $offset += 1;
  106. if($atype != Constants::ALGO_TYPE_AES_CBC_128){
  107. throw new MalformedException("Unmatched Key Encryption Algorithm Type:$atype");
  108. }
  109. $kcipherLen_ = unpack("n", substr($ct, $offset, 2));
  110. $kcipherLen = $kcipherLen_[1];
  111. $offset += 2;
  112. if($kcipherLen < Constants::DEFAULT_CIPHERBLK_LEN || $kcipherLen > strlen(substr($ct, $offset))){
  113. throw new MalformedException("Corrupted ciphertext header with illegal key cipher length.");
  114. }
  115. $kcipher = substr($ct, $offset, $kcipherLen);
  116. $offset += $kcipherLen;
  117. $dkey = KeyEncryption::unwrap($this, $kcipher);
  118. $dtype_ = unpack("C", substr($ct, $offset, 1));
  119. $dtype = $dtype_[1];
  120. $offset += 1;
  121. if($dtype != Constants::ALGO_TYPE_AES_CBC_128){
  122. throw new MalformedException("Unmatched Data Encryption Algorithm Type:$dtype");
  123. }
  124. $dcipherLen_ = unpack("N", substr($ct, $offset, 4));
  125. $dcipherLen = $dcipherLen_[1];
  126. $offset += 4;
  127. if($dcipherLen != strlen(substr($ct, $offset))){
  128. throw new MalformedException("Corrupted ciphertext header with illegal data cipher length.");
  129. }
  130. $dcipher = substr($ct, $offset);
  131. $de = new DataEncryption($dkey);
  132. $pt = $de->decrypt($dcipher);
  133. return $pt;
  134. }
  135. public function decrypt($ct)
  136. {
  137. $offset = 0;
  138. // Get and validate cipher type
  139. $ctype_ = unpack("C", substr($ct, $offset, Constants::CIPHER_TYPE_LEN));
  140. $ctype = $ctype_[1];
  141. $offset += Constants::CIPHER_TYPE_LEN;
  142. if($ctype != Constants::CIPHER_TYPE_WEAK)
  143. throw new Ex\MalformedException("Unmatch Encryption Algorithm Type: $ctype");
  144. // Get and validate algo type
  145. $atype_ = unpack("C", substr($ct, $offset, Constants::ALGO_TYPE_LEN));
  146. $atype = $atype_[1];
  147. $offset += Constants::ALGO_TYPE_LEN;
  148. if($atype != Constants::ALGO_TYPE_AES_CBC_128)
  149. throw new Ex\MalformedException("Unmatch Encryption Algorithm Type: $atype");
  150. $eid = substr($ct, $offset, Constants::DEFAULT_KEYID_LEN);
  151. $offset += Constants::DEFAULT_KEYID_LEN;
  152. if($eid != $this->id)
  153. throw new MalformedException("Unmatch MKey ID.");
  154. $ct_data = substr($ct, $offset);
  155. $pt = KeyEncryption::decrypt($this, $ct_data);
  156. return $pt;
  157. }
  158. public function sign($input){
  159. if($input === NULL){
  160. throw new MalformedException("Illegal input.");
  161. }
  162. if ($this->id == NULL || strlen($this->id) != Constants::DEFAULT_KEYID_LEN) {
  163. throw new MalformedException("Illegal Signing Key.");
  164. }
  165. $rsec = Crypto::secureRandom(Constants::DEFAULT_SEED_LEN);
  166. $data = $input.$rsec;
  167. $signedData = hash_hmac(Constants::DEFAULT_TOKEN_SIGN_ALGO, $data, $this->svkey, TRUE);
  168. if($signedData === FALSE)
  169. throw new SignException("Sign fail, algo:".Constants::DEFAULT_TOKEN_SIGN_ALGO);
  170. $ret = $this->id.$rsec.$signedData;
  171. return base64_encode($ret);
  172. }
  173. public function verify($input, $sig){
  174. if($input === NULL){
  175. throw new MalformedException("Illegal input.");
  176. }
  177. if($sig === NULL || strlen($sig) < Constants::DEFAULT_KEYID_LEN + Constants::DEFAULT_SEED_LEN){
  178. throw new MalformedException("Illegal Signature.");
  179. }
  180. // skip key id
  181. $offset = Constants::DEFAULT_KEYID_LEN;
  182. $sig = base64_decode($sig);
  183. $rsec = substr($sig, $offset, Constants::DEFAULT_SEED_LEN);
  184. $offset += Constants::DEFAULT_SEED_LEN;
  185. $carriedSig = substr($sig, $offset);
  186. $data = $input.$rsec;
  187. $signedData = hash_hmac(Constants::DEFAULT_TOKEN_SIGN_ALGO, $data, $this->svkey, TRUE);
  188. return $carriedSig == $signedData;
  189. }
  190. public function getKey() { return $this->skey; }
  191. public function getRawKey(){
  192. return $this->key;
  193. }
  194. public function isValid() {
  195. return $this->isValid;
  196. }
  197. public function getID() {
  198. return $this->id;
  199. }
  200. public function getEffectiveTime(){
  201. return $this->effective;
  202. }
  203. public function getKeyStatus(){
  204. return $this->keyStatus;
  205. }
  206. public function getVersion(){
  207. return $this->ver;
  208. }
  209. public function getKeyUsage(){
  210. return $this->keyUsage;
  211. }
  212. public function getExpiredTime(){
  213. return $this->expired;
  214. }
  215. }