]> WPIA git - gigi.git/blob - src/club/wpia/gigi/crypto/SMIME.java
fix: ResultSet.getDate is often wrong as it fetches day-precision times
[gigi.git] / src / club / wpia / gigi / crypto / SMIME.java
1 package club.wpia.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.Random;
12
13 import club.wpia.gigi.util.PEM;
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         contents = normalizeNewlinesToCRLF(contents);
48
49         Signature signature = Signature.getInstance("SHA1WithRSA");
50         signature.initSign(pKey);
51         signature.update(contents.getBytes("UTF-8"));
52         byte[] signedData = signature.sign();
53
54         // "IssuerAndSerialNumber"
55         X500Name xName = X500Name.asX500Name(c.getIssuerX500Principal());
56         BigInteger serial = c.getSerialNumber();
57
58         SignerInfo sInfo = new SignerInfo(xName, serial, new AlgorithmId(AlgorithmId.SHA_oid), null, new AlgorithmId(AlgorithmId.RSAEncryption_oid), signedData, null);
59
60         // Content is outside so content here is null.
61         ContentInfo cInfo = new ContentInfo(ContentInfo.DATA_OID, null);
62
63         // Create PKCS7 Signed data
64         PKCS7 p7 = new PKCS7(new AlgorithmId[] {
65                 new AlgorithmId(AlgorithmId.SHA_oid)
66         }, cInfo, new java.security.cert.X509Certificate[] {
67                 c
68         }, new SignerInfo[] {
69                 sInfo
70         });
71
72         ByteArrayOutputStream bOut = new DerOutputStream();
73         p7.encodeSignedData(bOut);
74
75         mimeEncode(contents, PEM.formatBase64(bOut.toByteArray()), to);
76     }
77
78     private static String normalizeNewlinesToCRLF(String contents) {
79         return contents.replace("\r\n", "\r").replace("\r", "\n").replace("\n", "\r\n");
80     }
81
82     private static Random r = new Random();
83
84     private static void mimeEncode(String contents, String signature, PrintWriter to) {
85         String boundary = generateBoundary(contents, null);
86         to.print("MIME-Version: 1.0\r\n");
87         to.print("Content-Type: multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=\"sha1\"; boundary=\"" + boundary + "\"\r\n");
88         to.print("\r\n");
89         to.print("This is an S/MIME signed message\r\n");
90         to.print("\r\n");
91         to.print("--" + boundary + "\r\n");
92         to.print(contents + "\r\n");
93         to.print("--" + boundary + "\r\n");
94         to.print("Content-Type: application/x-pkcs7-signature; name=\"smime.p7s\"\r\n");
95         to.print("Content-Transfer-Encoding: base64\r\n");
96         to.print("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n");
97         to.print("\r\n");
98         to.print(signature + "\r\n");
99         to.print("\r\n");
100         to.print("--" + boundary + "--\r\n");
101     }
102
103     private static String generateBoundary(String contents, String contents2) {
104         String boundary = "";
105         while (contents.contains(boundary) || (contents2 != null && contents2.contains(boundary))) {
106             boundary = "--" + new BigInteger(16 * 8, r).toString(16).toUpperCase();
107         }
108         return boundary;
109     }
110 }