/* * Copyright (c) 2011-2019 The original author or authors * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.ext.mail; import io.vertx.codegen.annotations.Nullable; import io.vertx.core.Context; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.OpenOptions; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.impl.InboundBuffer; import io.vertx.ext.mail.impl.dkim.DKIMSigner; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; import org.apache.james.jdkim.DKIMVerifier; import org.apache.james.jdkim.MockPublicKeyRecordRetriever; import org.apache.james.jdkim.api.SignatureRecord; import org.apache.james.jdkim.impl.Message; import org.junit.Test; import org.junit.runner.RunWith; import javax.mail.internet.MimeMultipart; import java.io.ByteArrayInputStream; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; import static io.vertx.ext.mail.TestUtils.*; /** * Test sending mails with DKIM enabled. * * @author <a href="mailto:[email protected]">Lin Gao</a> */ @RunWith(VertxUnitRunner.class) @org.junit.FixMethodOrder() public class MailWithDKIMSignTest extends SMTPTestWiser { private static final String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKqSazYC8pj/JQmo\n" + "2ep0m3Shs6WGyHarknUzRJxiHWIVl2CvvOz2aCo4QCFk7nHjJbSQigA/xRrQ+Mzg\n" + "uNv4n/c+0MjMQscpyhrMYhza89jP3yMRjIEPJxiQzeMgGHTQifiBfB+2a8959YkB\n" + "oOJZuoY0TOEyB+Lm3j000B4evsRdAgMBAAECgYAdSw38dZ8iJVdABG6ANExqSEbo\n" + "22/b6XU6iXZ0AOmY6apYoXWpoFudPJHO6l2E04SrMNNyXYFFLLQ9wy4cIOOfs5yB\n" + "bdZ17tvOqSWT7nsCcuHpUvF89JNXnQvV2xwS6npp/tIuehMfxOxPLdN87Nge7BEy\n" + "6DCSW7U72pX9zjl1BQJBANv56R9X+XLWjW6n4s0tZ271XVYI4DlRxQHYHP3B7eLm\n" + "4DJtoHk65WU3kfHUeBNy/9TmpC25Gw6WTDco+mOS8wsCQQDGgVPCqhNDUcZYMeOH\n" + "X6hm+l8zBeTMF2udQbkl0dRdLFpbMtw3cg+WUjHg3AYv38P2ikSJZzgzdDyZzcxF\n" + "Hcc3AkBXoBNm8upg/mpUW/gSdzWuk3rcnKiE7LenZmkWBDw4mHNSYyz7XaSnTx2J\n" + "0XMLfFHAgyd/Ny85/lDZ4C7tn0nFAkEAkS2mz9lJa1PUZ05dZPWuGVqF47AszKNY\n" + "XlPiEGntEhPNJaQF8TsncT4+IoFouPzDun0XcRKfxOn/JFGiUu5bcwJAGbai+kPl\n" + "AoyfGLxOLu40IMNOHKhHOq8cm3dOC+HpQYpx96JGaQPY4kl3fos6e43DGp9vyOxv\n" + "VMj5fan+wzHLcw=="; // the corresponding public key for the private key above. private static final String pubKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqkms2AvKY/yUJqNnqdJt0obOl" + "hsh2q5J1M0ScYh1iFZdgr7zs9mgqOEAhZO5x4yW0kIoAP8Ua0PjM4Ljb+J/3PtDI" + "zELHKcoazGIc2vPYz98jEYyBDycYkM3jIBh00In4gXwftmvPefWJAaDiWbqGNEzh" + "Mgfi5t49NNAeHr7EXQIDAQAB"; private static final String TEXT_BODY = "This is a Multiple Lines Text\r\n.Some lines start with one dot\n..Some" + "lines start with 2 dots.\n Some line starts with \n \r\n\t\nspace and ends with space "; private static final String HTML_BODY = "this is html text, \r\n <a href=\"http://vertx.io\">vertx.io</a>"; private final String IDENTITY = "f r;[email protected]"; private final DKIMSignOptions dkimOptionsBase = new DKIMSignOptions().setPrivateKey(privateKey) .setAuid(IDENTITY).setSdid("example.com").setSelector("lgao").setSignAlgo(DKIMSignAlgorithm.RSA_SHA256); private MailClient dkimMailClient(DKIMSignOptions dkimOps) { return MailClient.create(vertx, configLogin().setEnableDKIM(true).addDKIMSignOption(dkimOps)); } @Test public void testMailSimpleSimplePlain(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleRelaxedPlain(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedRelaxedPlain(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleSimpleMultiHeaderInstances(TestContext testContext) { this.testContext = testContext; List<String> signedHeaders = Stream.of("From", "Reply-to", "Subject", "To", "Received", "Received").collect(Collectors.toList()); MailMessage message = exampleMessage().setText(TEXT_BODY).addHeader("Received", "by 2002:ab3:7755:0:0:0:0:0 with SMTP id z21csp2085702lti") .addHeader("Received", "by 2002:a05:620a:147c:: with SMTP id j28mr519391qkl.13.1575424987504"); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setSignedHeaders(signedHeaders) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, signedHeaders, testContext); }); } @Test public void testMailSimpleRelaxedMultiHeaderInstances(TestContext testContext) { this.testContext = testContext; List<String> signedHeaders = Stream.of("From", "Reply-to", "Subject", "To", "Received", "Received").collect(Collectors.toList()); MailMessage message = exampleMessage().setText(TEXT_BODY).addHeader("Received", "by 2002:ab3:7755:0:0:0:0:0 with SMTP id z21csp2085702lti") .addHeader("Received", "by 2002:a05:620a:147c:: with SMTP id j28mr519391qkl.13.1575424987504") .addHeader("Received", "by 2005:a15:725a:579c:: with SMTP id j28mR876591qkl.13.1575473987504"); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setSignedHeaders(signedHeaders) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, signedHeaders, testContext); }); } @Test public void testMailRelaxedSimpleMultiHeaderInstances(TestContext testContext) { this.testContext = testContext; List<String> signedHeaders = Stream.of("From", "Reply-to", "Subject", "To", "Received", "Received").collect(Collectors.toList()); MailMessage message = exampleMessage().setText(TEXT_BODY).addHeader("Received", "by 2002:ab3:7755:0:0:0:0:0 with SMTP id z21csp2085702lti") .addHeader("Received", "by 2002:a05:620a:147c:: with SMTP id j28mr519391qkl.13.1575424987504"); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setSignedHeaders(signedHeaders) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, signedHeaders, testContext); }); } @Test public void testMailRelaxedRelaxedMultiHeaderInstances(TestContext testContext) { this.testContext = testContext; List<String> signedHeaders = Stream.of("From", "Reply-to", "Subject", "To", "Received", "Received").collect(Collectors.toList()); MailMessage message = exampleMessage().setText(TEXT_BODY) .addHeader("Received", "by 2002:ab3:7755:0:0:0:0:0 with SMTP id z21csp2085702lti") .addHeader("Received", "by 2002:a05:620a:147c:: with SMTP id j28mr519391qkl.13.1575424987504"); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setSignedHeaders(signedHeaders) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, signedHeaders, testContext); }); } @Test public void testMailRelaxedRelaxedPlainWithLimit(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(100) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedSimplePlainWithLimit(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(20) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedSimplePlainWithLargeLimit(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(Integer.MAX_VALUE) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedSimplePlain(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleSimplePlainFullOptions(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setExpireTime(2000) .setCopiedHeaders(Stream.of("From", "To").collect(Collectors.toList())) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { testContext.assertEquals(TEXT_BODY + "\n", TestUtils.conv2nl(TestUtils.inputStreamToString(wiser.getMessages().get(0).getMimeMessage().getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSPlainNonExistedCopiedHeaders(TestContext testContext) { this.testContext = testContext; MailMessage message = exampleMessage().setText(TEXT_BODY); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setExpireTime(2000) .setCopiedHeaders(Stream.of("From", "Not-Existed-Header").collect(Collectors.toList())) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testException(dkimMailClient(dkimOps), message, RuntimeException.class); } @Test public void testMailSimpleSimpleAttachment(TestContext testContext) { this.testContext = testContext; Buffer img = vertx.fileSystem().readFileBlocking("logo-white-big.png"); testContext.assertTrue(img.length() > 0); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setData(img); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleRelaxedAttachment(TestContext testContext) { this.testContext = testContext; Buffer img = vertx.fileSystem().readFileBlocking("logo-white-big.png"); testContext.assertTrue(img.length() > 0); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setData(img); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedSimpleAttachment(TestContext testContext) { this.testContext = testContext; Buffer img = vertx.fileSystem().readFileBlocking("logo-white-big.png"); testContext.assertTrue(img.length() > 0); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setData(img); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedRelaxedAttachment(TestContext testContext) { this.testContext = testContext; Buffer img = vertx.fileSystem().readFileBlocking("logo-white-big.png"); testContext.assertTrue(img.length() > 0); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setData(img); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedRelaxedHtmlWithAttachment(TestContext testContext) { this.testContext = testContext; Buffer img = vertx.fileSystem().readFileBlocking("logo-white-big.png"); testContext.assertTrue(img.length() > 0); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setData(img); MailAttachment inLineAttachment = MailAttachment.create().setName("logo-inline").setData(img); MailMessage message = exampleMessage() .setText(TEXT_BODY) .setHtml(HTML_BODY) .setInlineAttachment(inLineAttachment) .setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { // 1. alternative multi part // 1.1: text part // 1.2: html multi part with inline attachment // 1.2.1: html body part // 1.2.2: inline attachment // 2. attachment part final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); MimeMultipart alternative = (MimeMultipart)multiPart.getBodyPart(0).getContent(); testContext.assertEquals(2, alternative.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(alternative.getBodyPart(0).getInputStream()))); MimeMultipart htmlPart = (MimeMultipart)alternative.getBodyPart(1).getContent(); testContext.assertEquals(2, htmlPart.getCount()); testContext.assertEquals(HTML_BODY, conv2nl(inputStreamToString(htmlPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(htmlPart.getBodyPart(1).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedRelaxedHtmlWithAttachmentWithLimit(TestContext testContext) { this.testContext = testContext; Buffer img = vertx.fileSystem().readFileBlocking("logo-white-big.png"); testContext.assertTrue(img.length() > 0); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setData(img); MailAttachment inLineAttachment = MailAttachment.create().setName("logo-inline").setData(img); MailMessage message = exampleMessage() .setText(TEXT_BODY) .setHtml(HTML_BODY) .setInlineAttachment(inLineAttachment) .setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(500) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { // 1. alternative multi part // 1.1: text part // 1.2: html multi part with inline attachment // 1.2.1: html body part // 1.2.2: inline attachment // 2. attachment part final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); MimeMultipart alternative = (MimeMultipart)multiPart.getBodyPart(0).getContent(); testContext.assertEquals(2, alternative.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(alternative.getBodyPart(0).getInputStream()))); MimeMultipart htmlPart = (MimeMultipart)alternative.getBodyPart(1).getContent(); testContext.assertEquals(2, htmlPart.getCount()); testContext.assertEquals(HTML_BODY, conv2nl(inputStreamToString(htmlPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(htmlPart.getBodyPart(1).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedRelaxedHtmlWithAttachmentStream(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "true"); this.testContext = testContext; Buffer img = vertx.fileSystem().readFileBlocking("logo-white-big.png"); ReadStream<Buffer> stream = vertx.fileSystem().openBlocking("logo-white-big.png", new OpenOptions()); testContext.assertTrue(img.length() > 0); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setSize(img.length()).setStream(stream); MailAttachment inLineAttachment = MailAttachment.create().setName("logo-inline").setData(img); MailMessage message = exampleMessage() .setText(TEXT_BODY) .setHtml(HTML_BODY) .setInlineAttachment(inLineAttachment) .setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { // 1. alternative multi part // 1.1: text part // 1.2: html multi part with inline attachment // 1.2.1: html body part // 1.2.2: inline attachment // 2. attachment part final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); MimeMultipart alternative = (MimeMultipart)multiPart.getBodyPart(0).getContent(); testContext.assertEquals(2, alternative.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(alternative.getBodyPart(0).getInputStream()))); MimeMultipart htmlPart = (MimeMultipart)alternative.getBodyPart(1).getContent(); testContext.assertEquals(2, htmlPart.getCount()); testContext.assertEquals(HTML_BODY, conv2nl(inputStreamToString(htmlPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(htmlPart.getBodyPart(1).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleSimpleAttachmentStream(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "true"); this.testContext = testContext; String path = "logo-white-big.png"; Buffer img = vertx.fileSystem().readFileBlocking(path); ReadStream<Buffer> stream = vertx.fileSystem().openBlocking(path, new OpenOptions()); MailAttachment attachment = MailAttachment.create().setName(path).setStream(stream).setSize(img.length()); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleRelaxedAttachmentStream(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "false"); this.testContext = testContext; String path = "logo-white-big.png"; Buffer img = vertx.fileSystem().readFileBlocking(path); ReadStream<Buffer> stream = vertx.fileSystem().openBlocking(path, new OpenOptions()); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setStream(stream).setSize(img.length()); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedSimpleAttachmentStream(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "true"); this.testContext = testContext; String path = "logo-white-big.png"; Buffer img = vertx.fileSystem().readFileBlocking(path); ReadStream<Buffer> stream = vertx.fileSystem().openBlocking(path, new OpenOptions()); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setStream(stream).setSize(img.length()); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedRelaxedAttachmentStream(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "false"); this.testContext = testContext; String path = "logo-white-big.png"; Buffer img = vertx.fileSystem().readFileBlocking(path); ReadStream<Buffer> stream = vertx.fileSystem().openBlocking(path, new OpenOptions()); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setStream(stream).setSize(img.length()); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedRelaxedAttachmentStreamWithLimit(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "true"); this.testContext = testContext; String path = "logo-white-big.png"; Buffer img = vertx.fileSystem().readFileBlocking(path); ReadStream<Buffer> stream = vertx.fileSystem().openBlocking(path, new OpenOptions()); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setStream(stream).setSize(img.length()); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(100) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.RELAXED); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailRelaxedSimpleAttachmentStreamWithLimit(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "false"); this.testContext = testContext; String path = "logo-white-big.png"; Buffer img = vertx.fileSystem().readFileBlocking(path); ReadStream<Buffer> stream = vertx.fileSystem().openBlocking(path, new OpenOptions()); MailAttachment attachment = MailAttachment.create().setName("logo-white-big.png").setStream(stream).setSize(img.length()); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(50) .setHeaderCanonAlgo(CanonicalizationAlgorithm.RELAXED).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(img.getBytes(), inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } private Buffer fakeStreamData() { String path = "logo-white-big.png"; return vertx.fileSystem().readFileBlocking(path); } private class FakeReadStream implements ReadStream<Buffer> { private final InboundBuffer<Buffer> pending; private Handler<Void> endHandler; private FakeReadStream(Context context) { this.pending = new InboundBuffer<Buffer>(context).emptyHandler(v -> checkEnd()).pause(); context.runOnContext(h -> this.pending.write(fakeStreamData())); } private void checkEnd() { if (this.pending.isEmpty()) { if (endHandler != null) { endHandler.handle(null); } } } @Override public synchronized ReadStream<Buffer> exceptionHandler(Handler<Throwable> handler) { pending.exceptionHandler(handler); return this; } @Override public synchronized ReadStream<Buffer> handler(@Nullable Handler<Buffer> handler) { pending.handler(handler); return this; } @Override public synchronized ReadStream<Buffer> pause() { pending.pause(); return this; } @Override public synchronized ReadStream<Buffer> resume() { pending.resume(); return this; } @Override public synchronized ReadStream<Buffer> fetch(long amount) { pending.fetch(amount); return this; } @Override public synchronized ReadStream<Buffer> endHandler(@Nullable Handler<Void> endHandler) { this.endHandler = endHandler; return this; } } @Test public void testMailSimpleSimpleNonFileAttachmentStream(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "false"); this.testContext = testContext; Buffer fakeData = fakeStreamData(); byte[] fakeDataBytes = fakeData.getBytes(); ReadStream<Buffer> fakeStream = new FakeReadStream(vertx.getOrCreateContext()); MailAttachment attachment = MailAttachment.create().setName("FakeStream") .setStream(fakeStream) .setSize(fakeDataBytes.length); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(fakeDataBytes, inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleSimpleNonFileAttachmentStreamWithLimit(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "false"); this.testContext = testContext; Buffer fakeData = fakeStreamData(); byte[] fakeDataBytes = fakeData.getBytes(); ReadStream<Buffer> fakeStream = new FakeReadStream(vertx.getOrCreateContext()); MailAttachment attachment = MailAttachment.create().setName("FakeStream") .setStream(fakeStream) .setSize(fakeDataBytes.length); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(50) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(fakeDataBytes, inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleSimpleNonFileAttachmentStreamCacheInFile(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "true"); this.testContext = testContext; Buffer fakeData = fakeStreamData(); byte[] fakeDataBytes = fakeData.getBytes(); ReadStream<Buffer> fakeStream = new FakeReadStream(vertx.getOrCreateContext()); MailAttachment attachment = MailAttachment.create().setName("FakeStream") .setStream(fakeStream) .setSize(fakeDataBytes.length); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(fakeDataBytes, inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } @Test public void testMailSimpleSimpleNonFileAttachmentStreamCacheInFileWithLimit(TestContext testContext) { System.setProperty("vertx.mail.attachment.cache.file", "true"); this.testContext = testContext; Buffer fakeData = fakeStreamData(); byte[] fakeDataBytes = fakeData.getBytes(); ReadStream<Buffer> fakeStream = new FakeReadStream(vertx.getOrCreateContext()); MailAttachment attachment = MailAttachment.create().setName("FakeStream") .setStream(fakeStream) .setSize(fakeDataBytes.length); MailMessage message = exampleMessage().setText(TEXT_BODY).setAttachment(attachment); DKIMSignOptions dkimOps = new DKIMSignOptions(dkimOptionsBase).setBodyLimit(50) .setHeaderCanonAlgo(CanonicalizationAlgorithm.SIMPLE).setBodyCanonAlgo(CanonicalizationAlgorithm.SIMPLE); testSuccess(dkimMailClient(dkimOps), message, () -> { final MimeMultipart multiPart = (MimeMultipart)wiser.getMessages().get(0).getMimeMessage().getContent(); testContext.assertEquals(2, multiPart.getCount()); testContext.assertEquals(TEXT_BODY, conv2nl(inputStreamToString(multiPart.getBodyPart(0).getInputStream()))); testContext.assertTrue(Arrays.equals(fakeDataBytes, inputStreamToBytes(multiPart.getBodyPart(1).getInputStream()))); testDKIMSign(dkimOps, testContext); }); } private void testDKIMSign(DKIMSignOptions dkimOps, TestContext ctx) throws Exception { testDKIMSign(dkimOps, dkimOptionsBase.getSignedHeaders(), ctx); } private void testDKIMSign(DKIMSignOptions dkimOps, List<String> signHeaders, TestContext ctx) throws Exception { Message jamesMessage = new Message(new ByteArrayInputStream(wiser.getMessages().get(0).getData())); List<String> dkimHeaders = jamesMessage.getFields(DKIMSigner.DKIM_SIGNATURE_HEADER); ctx.assertEquals(1, dkimHeaders.size()); String dkimSignTagsList = dkimHeaders.get(0); ctx.assertNotNull(dkimSignTagsList); Map<String, String> signTags = new HashMap<>(); Arrays.stream(dkimSignTagsList.substring(dkimSignTagsList.indexOf(":") + 1).split(";")).map(String::trim).forEach(part -> { int idx = part.indexOf("="); signTags.put(part.substring(0, idx), part.substring(idx + 1)); }); ctx.assertEquals("1", signTags.get("v")); ctx.assertEquals(DKIMSignAlgorithm.RSA_SHA256.dkimAlgoName(), signTags.get("a")); ctx.assertEquals(dkimOps.getHeaderCanonAlgo().algoName() + "/" + dkimOps.getBodyCanonAlgo().algoName(), signTags.get("c")); ctx.assertEquals("example.com", signTags.get("d")); ctx.assertEquals("lgao", signTags.get("s")); ctx.assertEquals(String.join(":", signHeaders), signTags.get("h")); MockPublicKeyRecordRetriever recordRetriever = new MockPublicKeyRecordRetriever(); recordRetriever.addRecord("lgao", "example.com", "v=DKIM1; k=rsa; p=" + pubKeyStr); DKIMVerifier dkimVerifier = new DKIMVerifier(recordRetriever); List<SignatureRecord> records = dkimVerifier.verify(jamesMessage, jamesMessage.getBodyInputStream()); SignatureRecord record = records.get(0); ctx.assertNotNull(record); ctx.assertEquals("lgao", record.getSelector()); ctx.assertEquals(IDENTITY, record.getIdentity()); ctx.assertEquals("example.com", record.getDToken()); ctx.assertEquals("sha-256", record.getHashAlgo()); } }