]> WPIA git - gigi.git/blob - src/org/cacert/gigi/crypto/SMIME.java
f3ac0f766b6f00e9eff6960abdf675f592e0f9ff
[gigi.git] / src / org / cacert / gigi / crypto / SMIME.java
1 package org.cacert.gigi.crypto;
2
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;
13
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;
20
21 public class SMIME {
22
23     public static String doAlternatives(String plain, String html) {
24
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");
31         content.append("--");
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");
38         content.append(html);
39         content.append("\r\n--");
40         content.append(boundary);
41         content.append("--\r\n");
42         return content.toString();
43
44     }
45
46     public static void smime(String contents, PrivateKey pKey, X509Certificate c, PrintWriter to) throws IOException, GeneralSecurityException {
47
48         Signature signature = Signature.getInstance("SHA1WithRSA");
49         signature.initSign(pKey);
50         signature.update(contents.getBytes("UTF-8"));
51         byte[] signedData = signature.sign();
52
53         // "IssuerAndSerialNumber"
54         X500Name xName = X500Name.asX500Name(c.getIssuerX500Principal());
55         BigInteger serial = c.getSerialNumber();
56
57         SignerInfo sInfo = new SignerInfo(xName, serial, new AlgorithmId(AlgorithmId.SHA_oid), null, new AlgorithmId(AlgorithmId.RSAEncryption_oid), signedData, null);
58
59         // Content is outside so content here is null.
60         ContentInfo cInfo = new ContentInfo(ContentInfo.DATA_OID, null);
61
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[] {
66             c
67         }, new SignerInfo[] {
68             sInfo
69         });
70
71         ByteArrayOutputStream bOut = new DerOutputStream();
72         p7.encodeSignedData(bOut);
73
74         mimeEncode(contents, Base64.getEncoder().encodeToString(bOut.toByteArray()).replaceAll("(.{64})(?=.)", "$1\n"), to);
75     }
76
77     static Random r = new Random();
78
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 + "\"");
83         to.println("");
84         to.println("This is an S/MIME signed message");
85         to.println("");
86         to.println("--" + boundary);
87         to.println(contents);
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\"");
92         to.println("");
93         to.println(signature);
94         to.println();
95         to.println("--" + boundary + "--");
96     }
97
98     private static String generateBoundary(String contents, String contents2) {
99         String boundary = "";
100         while (contents.contains(boundary) || (contents2 != null && contents2.contains(boundary))) {
101             boundary = "--" + new BigInteger(16 * 8, r).toString(16).toUpperCase();
102         }
103         return boundary;
104     }
105 }