PageRenderTime 64ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/swiftmailer/swiftmailer/tests/unit/Swift/Signers/SMimeSignerTest.php

https://gitlab.com/ealexis.t/trends
PHP | 554 lines | 419 code | 116 blank | 19 comment | 22 complexity | e1ec0b48f9939e1ec7db68e18bd43f50 MD5 | raw file
  1. <?php
  2. class Swift_Signers_SMimeSignerTest extends \PHPUnit_Framework_TestCase
  3. {
  4. /**
  5. * @var Swift_StreamFilters_StringReplacementFilterFactory
  6. */
  7. protected $replacementFactory;
  8. protected $samplesDir;
  9. public function setUp()
  10. {
  11. $this->replacementFactory = Swift_DependencyContainer::getInstance()
  12. ->lookup('transport.replacementfactory');
  13. $this->samplesDir = str_replace('\\', '/', realpath(__DIR__.'/../../../_samples/')).'/';
  14. }
  15. public function testUnSingedMessage()
  16. {
  17. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  18. ->setFrom(array('john@doe.com' => 'John Doe'))
  19. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  20. ->setBody('Here is the message itself');
  21. $this->assertEquals('Here is the message itself', $message->getBody());
  22. }
  23. public function testSingedMessage()
  24. {
  25. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  26. ->setFrom(array('john@doe.com' => 'John Doe'))
  27. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  28. ->setBody('Here is the message itself');
  29. $signer = new Swift_Signers_SMimeSigner();
  30. $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key');
  31. $message->attachSigner($signer);
  32. $messageStream = $this->newFilteredStream();
  33. $message->toByteStream($messageStream);
  34. $messageStream->commit();
  35. $entityString = $messageStream->getContent();
  36. $headers = self::getHeadersOfMessage($entityString);
  37. if (!($boundary = $this->getBoundary($headers['content-type']))) {
  38. return false;
  39. }
  40. $expectedBody = <<<OEL
  41. This is an S/MIME signed message
  42. --$boundary
  43. Content-Type: text/plain; charset=utf-8
  44. Content-Transfer-Encoding: quoted-printable
  45. Here is the message itself
  46. --$boundary
  47. Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s"
  48. Content-Transfer-Encoding: base64
  49. Content-Disposition: attachment; filename="smime\.p7s"
  50. (?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})
  51. --$boundary--
  52. OEL;
  53. $this->assertValidVerify($expectedBody, $messageStream);
  54. unset($messageStream);
  55. }
  56. public function testSingedMessageExtraCerts()
  57. {
  58. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  59. ->setFrom(array('john@doe.com' => 'John Doe'))
  60. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  61. ->setBody('Here is the message itself');
  62. $signer = new Swift_Signers_SMimeSigner();
  63. $signer->setSignCertificate($this->samplesDir.'smime/sign2.crt', $this->samplesDir.'smime/sign2.key', PKCS7_DETACHED, $this->samplesDir.'smime/intermediate.crt');
  64. $message->attachSigner($signer);
  65. $messageStream = $this->newFilteredStream();
  66. $message->toByteStream($messageStream);
  67. $messageStream->commit();
  68. $entityString = $messageStream->getContent();
  69. $headers = self::getHeadersOfMessage($entityString);
  70. if (!($boundary = $this->getBoundary($headers['content-type']))) {
  71. return false;
  72. }
  73. $expectedBody = <<<OEL
  74. This is an S/MIME signed message
  75. --$boundary
  76. Content-Type: text/plain; charset=utf-8
  77. Content-Transfer-Encoding: quoted-printable
  78. Here is the message itself
  79. --$boundary
  80. Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s"
  81. Content-Transfer-Encoding: base64
  82. Content-Disposition: attachment; filename="smime\.p7s"
  83. (?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})
  84. --$boundary--
  85. OEL;
  86. $this->assertValidVerify($expectedBody, $messageStream);
  87. unset($messageStream);
  88. }
  89. public function testSingedMessageBinary()
  90. {
  91. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  92. ->setFrom(array('john@doe.com' => 'John Doe'))
  93. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  94. ->setBody('Here is the message itself');
  95. $signer = new Swift_Signers_SMimeSigner();
  96. $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key', PKCS7_BINARY);
  97. $message->attachSigner($signer);
  98. $messageStream = $this->newFilteredStream();
  99. $message->toByteStream($messageStream);
  100. $messageStream->commit();
  101. $entityString = $messageStream->getContent();
  102. $headers = self::getHeadersOfMessage($entityString);
  103. if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=signed\-data;#', $headers['content-type'])) {
  104. $this->fail('Content-type does not match.');
  105. return false;
  106. }
  107. $this->assertEquals($headers['content-transfer-encoding'], 'base64');
  108. $this->assertEquals($headers['content-disposition'], 'attachment; filename="smime.p7m"');
  109. $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})';
  110. $messageStreamClean = $this->newFilteredStream();
  111. $this->assertValidVerify($expectedBody, $messageStream);
  112. unset($messageStreamClean, $messageStream);
  113. }
  114. public function testSingedMessageWithAttachments()
  115. {
  116. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  117. ->setFrom(array('john@doe.com' => 'John Doe'))
  118. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  119. ->setBody('Here is the message itself');
  120. $message->attach(Swift_Attachment::fromPath($this->samplesDir.'/files/textfile.zip'));
  121. $signer = new Swift_Signers_SMimeSigner();
  122. $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key');
  123. $message->attachSigner($signer);
  124. $messageStream = $this->newFilteredStream();
  125. $message->toByteStream($messageStream);
  126. $messageStream->commit();
  127. $entityString = $messageStream->getContent();
  128. $headers = self::getHeadersOfMessage($entityString);
  129. if (!($boundary = $this->getBoundary($headers['content-type']))) {
  130. return false;
  131. }
  132. $expectedBody = <<<OEL
  133. This is an S/MIME signed message
  134. --$boundary
  135. Content-Type: multipart/mixed;
  136. boundary="([a-z0-9\\'\\(\\)\\+_\\-,\\.\\/:=\\?\\ ]{0,69}[a-z0-9\\'\\(\\)\\+_\\-,\\.\\/:=\\?])"
  137. --\\1
  138. Content-Type: text/plain; charset=utf-8
  139. Content-Transfer-Encoding: quoted-printable
  140. Here is the message itself
  141. --\\1
  142. Content-Type: application/zip; name=textfile\\.zip
  143. Content-Transfer-Encoding: base64
  144. Content-Disposition: attachment; filename=textfile\\.zip
  145. UEsDBAoAAgAAAMi6VjiOTiKwLgAAAC4AAAAMABUAdGV4dGZpbGUudHh0VVQJAAN3vr5Hd76\\+R1V4
  146. BAD1AfUBVGhpcyBpcyBwYXJ0IG9mIGEgU3dpZnQgTWFpbGVyIHY0IHNtb2tlIHRlc3QuClBLAQIX
  147. AwoAAgAAAMi6VjiOTiKwLgAAAC4AAAAMAA0AAAAAAAEAAACkgQAAAAB0ZXh0ZmlsZS50eHRVVAUA
  148. A3e\\+vkdVeAAAUEsFBgAAAAABAAEARwAAAG0AAAAAAA==
  149. --\\1--
  150. --$boundary
  151. Content-Type: application/(x\-)?pkcs7-signature; name="smime\\.p7s"
  152. Content-Transfer-Encoding: base64
  153. Content-Disposition: attachment; filename="smime\\.p7s"
  154. (?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})
  155. --$boundary--
  156. OEL;
  157. $this->assertValidVerify($expectedBody, $messageStream);
  158. unset($messageStream);
  159. }
  160. public function testEncryptedMessage()
  161. {
  162. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  163. ->setFrom(array('john@doe.com' => 'John Doe'))
  164. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  165. ->setBody('Here is the message itself');
  166. $originalMessage = $this->cleanMessage($message->toString());
  167. $signer = new Swift_Signers_SMimeSigner();
  168. $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt');
  169. $message->attachSigner($signer);
  170. $messageStream = new Swift_ByteStream_TemporaryFileByteStream();
  171. $message->toByteStream($messageStream);
  172. $messageStream->commit();
  173. $entityString = $messageStream->getContent();
  174. $headers = self::getHeadersOfMessage($entityString);
  175. if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) {
  176. $this->fail('Content-type does not match.');
  177. return false;
  178. }
  179. $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})';
  180. $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream();
  181. if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) {
  182. $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string()));
  183. }
  184. $this->assertEquals($originalMessage, $decryptedMessageStream->getContent());
  185. unset($decryptedMessageStream, $messageStream);
  186. }
  187. public function testEncryptedMessageWithMultipleCerts()
  188. {
  189. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  190. ->setFrom(array('john@doe.com' => 'John Doe'))
  191. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  192. ->setBody('Here is the message itself');
  193. $originalMessage = $this->cleanMessage($message->toString());
  194. $signer = new Swift_Signers_SMimeSigner();
  195. $signer->setEncryptCertificate(array($this->samplesDir.'smime/encrypt.crt', $this->samplesDir.'smime/encrypt2.crt'));
  196. $message->attachSigner($signer);
  197. $messageStream = new Swift_ByteStream_TemporaryFileByteStream();
  198. $message->toByteStream($messageStream);
  199. $messageStream->commit();
  200. $entityString = $messageStream->getContent();
  201. $headers = self::getHeadersOfMessage($entityString);
  202. if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) {
  203. $this->fail('Content-type does not match.');
  204. return false;
  205. }
  206. $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})';
  207. $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream();
  208. if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) {
  209. $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string()));
  210. }
  211. $this->assertEquals($originalMessage, $decryptedMessageStream->getContent());
  212. unset($decryptedMessageStream);
  213. $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream();
  214. if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt2.crt', array('file://'.$this->samplesDir.'smime/encrypt2.key', 'swift'))) {
  215. $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string()));
  216. }
  217. $this->assertEquals($originalMessage, $decryptedMessageStream->getContent());
  218. unset($decryptedMessageStream, $messageStream);
  219. }
  220. public function testSignThenEncryptedMessage()
  221. {
  222. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  223. ->setFrom(array('john@doe.com' => 'John Doe'))
  224. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  225. ->setBody('Here is the message itself');
  226. $signer = new Swift_Signers_SMimeSigner();
  227. $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key');
  228. $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt');
  229. $message->attachSigner($signer);
  230. $messageStream = new Swift_ByteStream_TemporaryFileByteStream();
  231. $message->toByteStream($messageStream);
  232. $messageStream->commit();
  233. $entityString = $messageStream->getContent();
  234. $headers = self::getHeadersOfMessage($entityString);
  235. if (!preg_match('#^application/(x\-)?pkcs7-mime; smime-type=enveloped\-data;#', $headers['content-type'])) {
  236. $this->fail('Content-type does not match.');
  237. return false;
  238. }
  239. $expectedBody = '(?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})';
  240. $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream();
  241. if (!openssl_pkcs7_decrypt($messageStream->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) {
  242. $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string()));
  243. }
  244. $entityString = $decryptedMessageStream->getContent();
  245. $headers = self::getHeadersOfMessage($entityString);
  246. if (!($boundary = $this->getBoundary($headers['content-type']))) {
  247. return false;
  248. }
  249. $expectedBody = <<<OEL
  250. This is an S/MIME signed message
  251. --$boundary
  252. Content-Type: text/plain; charset=utf-8
  253. Content-Transfer-Encoding: quoted-printable
  254. Here is the message itself
  255. --$boundary
  256. Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s"
  257. Content-Transfer-Encoding: base64
  258. Content-Disposition: attachment; filename="smime\.p7s"
  259. (?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})
  260. --$boundary--
  261. OEL;
  262. if (!$this->assertValidVerify($expectedBody, $decryptedMessageStream)) {
  263. return false;
  264. }
  265. unset($decryptedMessageStream, $messageStream);
  266. }
  267. public function testEncryptThenSignMessage()
  268. {
  269. $message = Swift_SignedMessage::newInstance('Wonderful Subject')
  270. ->setFrom(array('john@doe.com' => 'John Doe'))
  271. ->setTo(array('receiver@domain.org', 'other@domain.org' => 'A name'))
  272. ->setBody('Here is the message itself');
  273. $originalMessage = $this->cleanMessage($message->toString());
  274. $signer = Swift_Signers_SMimeSigner::newInstance();
  275. $signer->setSignCertificate($this->samplesDir.'smime/sign.crt', $this->samplesDir.'smime/sign.key');
  276. $signer->setEncryptCertificate($this->samplesDir.'smime/encrypt.crt');
  277. $signer->setSignThenEncrypt(false);
  278. $message->attachSigner($signer);
  279. $messageStream = $this->newFilteredStream();
  280. $message->toByteStream($messageStream);
  281. $messageStream->commit();
  282. $entityString = $messageStream->getContent();
  283. $headers = self::getHeadersOfMessage($entityString);
  284. if (!($boundary = $this->getBoundary($headers['content-type']))) {
  285. return false;
  286. }
  287. $expectedBody = <<<OEL
  288. This is an S/MIME signed message
  289. --$boundary
  290. (?P<encrypted_message>MIME-Version: 1\.0
  291. Content-Disposition: attachment; filename="smime\.p7m"
  292. Content-Type: application/(x\-)?pkcs7-mime; smime-type=enveloped-data; name="smime\.p7m"
  293. Content-Transfer-Encoding: base64
  294. (?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})
  295. )--$boundary
  296. Content-Type: application/(x\-)?pkcs7-signature; name="smime\.p7s"
  297. Content-Transfer-Encoding: base64
  298. Content-Disposition: attachment; filename="smime\.p7s"
  299. (?:^[a-zA-Z0-9\/\\r\\n+]*={0,2})
  300. --$boundary--
  301. OEL;
  302. if (!$this->assertValidVerify($expectedBody, $messageStream)) {
  303. return false;
  304. }
  305. $expectedBody = str_replace("\n", "\r\n", $expectedBody);
  306. if (!preg_match('%'.$expectedBody.'*%m', $entityString, $entities)) {
  307. $this->fail('Failed regex match.');
  308. return false;
  309. }
  310. $messageStreamClean = new Swift_ByteStream_TemporaryFileByteStream();
  311. $messageStreamClean->write($entities['encrypted_message']);
  312. $decryptedMessageStream = new Swift_ByteStream_TemporaryFileByteStream();
  313. if (!openssl_pkcs7_decrypt($messageStreamClean->getPath(), $decryptedMessageStream->getPath(), 'file://'.$this->samplesDir.'smime/encrypt.crt', array('file://'.$this->samplesDir.'smime/encrypt.key', 'swift'))) {
  314. $this->fail(sprintf('Decrypt of the message failed. Internal error "%s".', openssl_error_string()));
  315. }
  316. $this->assertEquals($originalMessage, $decryptedMessageStream->getContent());
  317. unset($messageStreamClean, $messageStream, $decryptedMessageStream);
  318. }
  319. protected function assertValidVerify($expected, Swift_ByteStream_TemporaryFileByteStream $messageStream)
  320. {
  321. $actual = $messageStream->getContent();
  322. // File is UNIX encoded so convert them to correct line ending
  323. $expected = str_replace("\n", "\r\n", $expected);
  324. $actual = trim(self::getBodyOfMessage($actual));
  325. if (!$this->assertRegExp('%^'.$expected.'$\s*%m', $actual)) {
  326. return false;
  327. }
  328. $opensslOutput = new Swift_ByteStream_TemporaryFileByteStream();
  329. $verify = openssl_pkcs7_verify($messageStream->getPath(), null, $opensslOutput->getPath(), array($this->samplesDir.'smime/ca.crt'));
  330. if (false === $verify) {
  331. $this->fail('Verification of the message failed.');
  332. return false;
  333. } elseif (-1 === $verify) {
  334. $this->fail(sprintf('Verification of the message failed. Internal error "%s".', openssl_error_string()));
  335. return false;
  336. }
  337. return true;
  338. }
  339. protected function getBoundary($contentType)
  340. {
  341. if (!preg_match('/boundary=("[^"]+"|(?:[^\s]+|$))/is', $contentType, $contentTypeData)) {
  342. $this->fail('Failed to find Boundary parameter');
  343. return false;
  344. }
  345. return trim($contentTypeData[1], '"');
  346. }
  347. protected function newFilteredStream()
  348. {
  349. $messageStream = new Swift_ByteStream_TemporaryFileByteStream();
  350. $messageStream->addFilter($this->replacementFactory->createFilter("\r\n", "\n"), 'CRLF to LF');
  351. $messageStream->addFilter($this->replacementFactory->createFilter("\n", "\r\n"), 'LF to CRLF');
  352. return $messageStream;
  353. }
  354. protected static function getBodyOfMessage($message)
  355. {
  356. return substr($message, strpos($message, "\r\n\r\n"));
  357. }
  358. /**
  359. * Strips of the sender headers and Mime-Version.
  360. *
  361. * @param Swift_ByteStream_TemporaryFileByteStream $messageStream
  362. * @param Swift_ByteStream_TemporaryFileByteStream $inputStream
  363. */
  364. protected function cleanMessage($content)
  365. {
  366. $newContent = '';
  367. $headers = self::getHeadersOfMessage($content);
  368. foreach ($headers as $headerName => $value) {
  369. if (!in_array($headerName, array('content-type', 'content-transfer-encoding', 'content-disposition'))) {
  370. continue;
  371. }
  372. $headerName = explode('-', $headerName);
  373. $headerName = array_map('ucfirst', $headerName);
  374. $headerName = implode('-', $headerName);
  375. if (strlen($value) > 62) {
  376. $value = wordwrap($value, 62, "\n ");
  377. }
  378. $newContent .= "$headerName: $value\r\n";
  379. }
  380. return $newContent."\r\n".ltrim(self::getBodyOfMessage($content));
  381. }
  382. /**
  383. * Returns the headers of the message.
  384. *
  385. * Header-names are lowercase.
  386. *
  387. * @param string $message
  388. *
  389. * @return array
  390. */
  391. protected static function getHeadersOfMessage($message)
  392. {
  393. $headersPosEnd = strpos($message, "\r\n\r\n");
  394. $headerData = substr($message, 0, $headersPosEnd);
  395. $headerLines = explode("\r\n", $headerData);
  396. if (empty($headerLines)) {
  397. return array();
  398. }
  399. $headers = array();
  400. foreach ($headerLines as $headerLine) {
  401. if (ctype_space($headerLines[0]) || false === strpos($headerLine, ':')) {
  402. $headers[$currentHeaderName] .= ' '.trim($headerLine);
  403. continue;
  404. }
  405. $header = explode(':', $headerLine, 2);
  406. $currentHeaderName = strtolower($header[0]);
  407. $headers[$currentHeaderName] = trim($header[1]);
  408. }
  409. return $headers;
  410. }
  411. }