package org.jboss.resteasy.test.crypto;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.RecipientId;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.RecipientInformationStore;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMEEnveloped;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.bouncycastle.mail.smime.SMIMEException;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.operator.OutputEncryptor;
import org.jboss.resteasy.security.PemUtils;
import org.jboss.resteasy.util.Base64;
import org.jboss.resteasy.utils.TestUtil;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.mail.MessagingException;
import javax.mail.internet.InternetHeaders;
import javax.mail.internet.MimeBodyPart;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
/**
* @tpSubChapter Crypto
* @tpChapter Unit tests
* @tpTestCaseDetails Test for sign by content of message signed by X509Certificate.
* @tpSince RESTEasy 3.0.16
*/
public class EnvelopedTest {
protected static final Logger logger = LogManager.getLogger(EnvelopedTest.class.getName());
private static String python_smime = "MIIBagYJKoZIhvcNAQcDoIIBWzCCAVcCAQAxgewwgekCAQAwUjBFMQswCQYDVQQG\n" +
"EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk\n" +
"Z2l0cyBQdHkgTHRkAgkA7oW81OriflAwDQYJKoZIhvcNAQEBBQAEgYA1AWIoRMsb\n" +
"Gv2DsHLjcvu6URZPqS0atjGW7uqlthmoQ4XB+l0y+iy2rXFuJnz+iLp/EIn92UpR\n" +
"ZeFHPoQEDklkk5QqRaIBvkZiJgiPs9VuWiXVfHeOei9Oneyfja9Q88eFWHFToWok\n" +
"LIDie+Wt/mMYY23QSVTY3r+cgTnOyV8gyDBjBgkqhkiG9w0BBwEwFAYIKoZIhvcN\n" +
"AwcECJYFaD/eHDkHgEDIBLBzczEdLLk7nQzORmVist6gv30Ez9LCzHlnFteU+jVr\n" +
"zAUGo6VoZZMmyLVeYEZoXqEjY6fN+rpSWoUVtNQM";
private static X509Certificate cert;
private static PrivateKey privateKey;
static final String certPemPath = TestUtil.getResourcePath(EnvelopedTest.class, "SignedMycert.pem");
static final String certPrivatePemPath = TestUtil.getResourcePath(EnvelopedTest.class, "MycertPrivate.pem");
@BeforeClass
public static void setup() throws Exception {
Security.addProvider(new BouncyCastleProvider());
InputStream certIs = new FileInputStream(certPemPath);
cert = PemUtils.decodeCertificate(certIs);
InputStream privateIs = new FileInputStream(certPrivatePemPath);
privateKey = PemUtils.decodePrivateKey(privateIs);
}
/**
* @tpTestDetails Check body of message content
* @tpSince RESTEasy 3.0.16
*/
@Test
public void testBody() throws Exception {
OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC)
.setProvider("BC")
.build();
JceKeyTransRecipientInfoGenerator infoGenerator = new JceKeyTransRecipientInfoGenerator(cert);
CMSEnvelopedDataStreamGenerator generator = new CMSEnvelopedDataStreamGenerator();
generator.addRecipientInfoGenerator(infoGenerator);
InternetHeaders internetHeaders = new InternetHeaders();
internetHeaders.addHeader("Content-Type", "application/xml");
MimeBodyPart mimeBodyPart = new MimeBodyPart(internetHeaders, "<customer name=\"bill\"/>".getBytes());
ByteArrayOutputStream os = new ByteArrayOutputStream();
OutputStream encrypted = generator.open(os, encryptor);
mimeBodyPart.writeTo(encrypted);
encrypted.close();
String str = Base64.encodeBytes(os.toByteArray(), Base64.DO_BREAK_LINES);
internetHeaders = new InternetHeaders();
internetHeaders.addHeader("Content-Disposition", "attachment; filename=\"smime.p7m\"");
internetHeaders.addHeader("Content-Type", "application/pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\"");
internetHeaders.addHeader("Content-Transfer-Encoding", "base64");
MimeBodyPart newMimeBodyPart = new MimeBodyPart(internetHeaders, str.getBytes());
newMimeBodyPart = decode2Mime(newMimeBodyPart);
Assert.assertEquals("Wrong type of mimeBodyPart content", "application/xml", newMimeBodyPart.getContentType());
String body = toString(newMimeBodyPart.getInputStream());
Assert.assertEquals("Wrong decoded content", "<customer name=\"bill\"/>", body.trim());
}
/**
* @tpTestDetails Check headers
* @tpSince RESTEasy 3.0.16
*/
@Test
public void testHeaders()
throws Exception {
InternetHeaders internetHeaders = new InternetHeaders();
internetHeaders.addHeader("Content-Type", "application/xml");
MimeBodyPart _msg = new MimeBodyPart(internetHeaders, "<customer name=\"bill\"/>".getBytes());
SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();
OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC)
.setProvider("BC")
.build();
gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(cert).setProvider("BC"));
// generate a MimeBodyPart object which encapsulates the content
// we want encrypted.
MimeBodyPart mp = gen.generate(_msg, encryptor);
output(mp);
}
private void output(MimeBodyPart mimeBodyPart) throws IOException, MessagingException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
mimeBodyPart.writeTo(os);
String s = new String(os.toByteArray());
logger.info(s);
}
/**
* @tpTestDetails Check bython generated content
* @tpSince RESTEasy 3.0.16
*/
@Test
public void testFromPythonGenerated() throws Exception {
{
InternetHeaders internetHeaders = new InternetHeaders();
internetHeaders.addHeader("Content-Disposition", "attachment; filename=\"smime.p7m\"");
internetHeaders.addHeader("Content-Type", "application/pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\"");
internetHeaders.addHeader("Content-Transfer-Encoding", "base64");
MimeBodyPart mp = new MimeBodyPart(internetHeaders, python_smime.getBytes());
output(mp);
logger.info("------------");
mp = decode2Mime(mp);
Assert.assertEquals("Wrong media type of content", "application/xml", mp.getContentType());
String body = toString(mp.getInputStream());
Assert.assertEquals("Wrong decoded content", "<customer name=\"bill\"/>", body.trim());
}
{
ByteArrayInputStream is = new ByteArrayInputStream(python_smime.getBytes(StandardCharsets.UTF_8));
MimeBodyPart mp = decode2Mime(is);
Assert.assertEquals("Wrong media type of content", "application/xml", mp.getContentType());
String body = toString(mp.getInputStream());
Assert.assertEquals("Wrong decoded content", "<customer name=\"bill\"/>", body.trim());
}
}
private static String toString(InputStream is) throws Exception {
DataInputStream dis = new DataInputStream(is);
byte[] bytes = new byte[dis.available()];
dis.readFully(bytes);
dis.close();
return new String(bytes);
}
private MimeBodyPart decode2Mime(InputStream body) throws MessagingException, CMSException, SMIMEException, NoSuchProviderException, IOException {
StringBuilder builder = new StringBuilder();
builder.append("Content-Disposition: attachment; filename=\"smime.p7m\"\r\n");
builder.append("Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=\"smime.p7m\"\r\n");
builder.append("Content-Transfer-Encoding: base64\r\n\r\n");
ByteArrayInputStream is = new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8));
MimeBodyPart mp = new MimeBodyPart(new SequenceInputStream(is, body));
return decode2Mime(mp);
}
private MimeBodyPart decode2Mime(MimeBodyPart mp) throws MessagingException, CMSException, SMIMEException, NoSuchProviderException, IOException {
SMIMEEnveloped m = new SMIMEEnveloped(mp);
RecipientId recId = new JceKeyTransRecipientId(cert);
RecipientInformationStore recipients = m.getRecipientInfos();
RecipientInformation recipient = recipients.get(recId);
JceKeyTransRecipient pKeyRecp = new JceKeyTransEnvelopedRecipient(privateKey);
return SMIMEUtil.toMimeBodyPart(recipient.getContent(pKeyRecp));
}
}