1 package org.cacert.gigi.crypto;
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5 import java.io.PrintWriter;
6 import java.math.BigInteger;
7 import java.security.GeneralSecurityException;
8 import java.security.PrivateKey;
9 import java.security.Signature;
10 import java.security.cert.X509Certificate;
11 import java.util.Base64;
12 import java.util.Random;
14 import sun.security.pkcs.ContentInfo;
15 import sun.security.pkcs.PKCS7;
16 import sun.security.pkcs.SignerInfo;
17 import sun.security.util.DerOutputStream;
18 import sun.security.x509.AlgorithmId;
19 import sun.security.x509.X500Name;
23 public static String doAlternatives(String plain, String html) {
25 plain = "Content-type: text/plain\r\n\r\n" + plain;
26 html = "Content-type: text/html\r\n\r\n" + html;
27 String boundary = generateBoundary(plain, html);
28 StringBuffer content = new StringBuffer("Content-Type: multipart/alternative; boundary=\"");
29 content.append(boundary);
30 content.append("\"\r\n\r\n");
32 content.append(boundary);
33 content.append("\r\n");
34 content.append(plain);
35 content.append("\r\n--");
36 content.append(boundary);
37 content.append("\r\n");
39 content.append("\r\n--");
40 content.append(boundary);
41 content.append("--\r\n");
42 return content.toString();
46 public static void smime(String contents, PrivateKey pKey, X509Certificate c, PrintWriter to) throws IOException, GeneralSecurityException {
48 Signature signature = Signature.getInstance("SHA1WithRSA");
49 signature.initSign(pKey);
50 signature.update(contents.getBytes("UTF-8"));
51 byte[] signedData = signature.sign();
53 // "IssuerAndSerialNumber"
54 X500Name xName = X500Name.asX500Name(c.getIssuerX500Principal());
55 BigInteger serial = c.getSerialNumber();
57 SignerInfo sInfo = new SignerInfo(xName, serial, new AlgorithmId(AlgorithmId.SHA_oid), null, new AlgorithmId(AlgorithmId.RSAEncryption_oid), signedData, null);
59 // Content is outside so content here is null.
60 ContentInfo cInfo = new ContentInfo(ContentInfo.DATA_OID, null);
62 // Create PKCS7 Signed data
63 PKCS7 p7 = new PKCS7(new AlgorithmId[] {
64 new AlgorithmId(AlgorithmId.SHA_oid)
65 }, cInfo, new java.security.cert.X509Certificate[] {
71 ByteArrayOutputStream bOut = new DerOutputStream();
72 p7.encodeSignedData(bOut);
74 mimeEncode(contents, Base64.getEncoder().encodeToString(bOut.toByteArray()).replaceAll("(.{64})(?=.)", "$1\n"), to);
77 private static Random r = new Random();
79 private static void mimeEncode(String contents, String signature, PrintWriter to) {
80 String boundary = generateBoundary(contents, null);
81 to.println("MIME-Version: 1.0");
82 to.println("Content-Type: multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=\"sha1\"; boundary=\"" + boundary + "\"");
84 to.println("This is an S/MIME signed message");
86 to.println("--" + boundary);
88 to.println("--" + boundary);
89 to.println("Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"");
90 to.println("Content-Transfer-Encoding: base64");
91 to.println("Content-Disposition: attachment; filename=\"smime.p7s\"");
93 to.println(signature);
95 to.println("--" + boundary + "--");
98 private static String generateBoundary(String contents, String contents2) {
100 while (contents.contains(boundary) || (contents2 != null && contents2.contains(boundary))) {
101 boundary = "--" + new BigInteger(16 * 8, r).toString(16).toUpperCase();