001// Copyright (C) 2002 IAIK
002// https://sic.tech/
003//
004// Copyright (C) 2003 - 2025 Stiftung Secure Information and 
005//                           Communication Technologies SIC
006// https://sic.tech/
007//
008// All rights reserved.
009//
010// This source is provided for inspection purposes and recompilation only,
011// unless specified differently in a contract with IAIK. This source has to
012// be kept in strict confidence and must not be disclosed to any third party
013// under any circumstances. Redistribution in source and binary forms, with
014// or without modification, are <not> permitted in any case!
015//
016// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
017// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
020// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
021// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
022// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
023// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
024// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
025// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
026// SUCH DAMAGE.
027//
028// $Header: /IAIK-CMS/current/src/demo/smime/basic/SMimeV3SHA2withDSADemo.java 10    12.02.25 17:58 Dbratko $
029// $Revision: 10 $
030//
031
032package demo.smime.basic;
033
034import iaik.asn1.structures.AlgorithmID;
035import iaik.cms.CMSAlgorithmID;
036import iaik.smime.SMimeBodyPart;
037import iaik.smime.SMimeMultipart;
038import iaik.smime.SignedContent;
039import iaik.x509.X509Certificate;
040
041import java.io.ByteArrayInputStream;
042import java.io.ByteArrayOutputStream;
043import java.io.IOException;
044import java.security.NoSuchAlgorithmException;
045import java.security.PrivateKey;
046import java.util.Date;
047
048import jakarta.activation.DataHandler;
049import jakarta.activation.FileDataSource;
050import jakarta.mail.Message;
051import jakarta.mail.MessagingException;
052import jakarta.mail.Multipart;
053import jakarta.mail.Session;
054import jakarta.mail.internet.InternetAddress;
055import jakarta.mail.internet.MimeBodyPart;
056import jakarta.mail.internet.MimeMessage;
057
058import demo.DemoSMimeUtil;
059import demo.DemoUtil;
060import demo.keystore.CMSKeyStore;
061import demo.smime.DumpMessage;
062
063/**
064 * This class demonstrates the usage of the IAIK S/MIME implementation with the 
065 * SHA2withDSA signature algorithm (FIPS 186-3). It shows how to create signed
066 * S/MIMEv3 messages and how to parse them and verify the signature.
067 * <p>
068 * To run this demo the following packages are required:
069 * <ul>
070 *    <li>
071 *       <code>iaik_cms.jar</code> (IAIK-CMS/SMIME)
072 *    </li>
073 *    <li>
074 *       <code>iaik_jce(_full).jar</code> (<a href="https://sic.tech/products/core-crypto-toolkits/jca-jce/" target="_blank">IAIK-JCE Core Crypto Library</a>).
075 *    </li>
076 *    <li>
077 *       <a href="https://jakarta.ee/specifications/mail/" target="_blank">Jakarta</a>/<a href="https://eclipse-ee4j.github.io/angus-mail/" target="_blank">Angus</a> Mail
078 *    </li>   
079 *    <li>
080 *       <a href="https://jakarta.ee/specifications/activation/" target="_blank">Jakarta Activation Framework</a>
081 *    </li> 
082 * </ul>
083 */
084public class SMimeV3SHA2withDSADemo {
085  
086//whether to print dump all generates test messages to System.out
087  final static boolean PRINT_MESSAGES = false;   
088  
089
090  String firstName = "John";
091  String lastName = "SMime";
092  String to = "smimetest@iaik.tugraz.at";     // email recipient
093  String from = "smimetest@iaik.tugraz.at";   // email sender
094  String host = "mailhost";                       // name of the mailhost
095
096  X509Certificate[] signerCertificates;          // list of certificates to include in the S/MIME message
097  X509Certificate signerCertificate;             // certificate of the signer/sender
098  PrivateKey signerPrivateKey;                   // private key of the signer/sender
099    
100  /**
101   * Default constructor. Reads certificates and keys from the demo keystore.
102   */
103  public SMimeV3SHA2withDSADemo() {
104    //  get the certificates from the KeyStore
105    signerCertificates = CMSKeyStore.getCertificateChain(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
106    signerPrivateKey = CMSKeyStore.getPrivateKey(CMSKeyStore.DSA, CMSKeyStore.SZ_3072_SIGN);
107    signerCertificate = signerCertificates[0];
108  }
109  
110  /**
111   * Starts the demo.
112   *
113   * @throws IOException if an I/O related error occurs
114   */
115  public void start() throws IOException {
116
117    // get the default Session
118        Session session = DemoSMimeUtil.getSession();
119
120        try {
121      // Create a demo Multipart
122      MimeBodyPart mbp1 = new SMimeBodyPart();
123          mbp1.setText("This is a Test of the IAIK S/MIME implementation!\n\n");
124          // attachment
125      MimeBodyPart attachment = new SMimeBodyPart();
126      attachment.setDataHandler(new DataHandler(new FileDataSource("test.html")));
127      attachment.setFileName("test.html");
128
129      Multipart mp = new SMimeMultipart();
130      mp.addBodyPart(mbp1);
131      mp.addBodyPart(attachment);
132      DataHandler multipart = new DataHandler(mp, mp.getContentType());
133
134      Message msg;    // the message to send
135      ByteArrayOutputStream baos = new ByteArrayOutputStream(); // we write to a stream
136      ByteArrayInputStream bais;  // we read from a stream
137      
138      AlgorithmID digestAlgorithm = CMSAlgorithmID.sha256;
139      AlgorithmID signatureAlgorithm = CMSAlgorithmID.dsaWithSHA256;
140
141      //    1. This is an explicitly signed message
142      msg = createSignedMessage(session, 
143                                multipart, 
144                                false,
145                                (AlgorithmID)digestAlgorithm.clone(),
146                                (AlgorithmID)signatureAlgorithm.clone());
147      System.out.println("creating explicitly signed message...");
148      baos.reset();
149      msg.saveChanges();
150      msg.writeTo(baos);
151      bais = new ByteArrayInputStream(baos.toByteArray());
152      msg = new MimeMessage(session, bais);
153      if (PRINT_MESSAGES) {
154        printMessage(msg);
155      }
156      DumpMessage.dumpMsg(msg);
157      
158      System.out.println("\n\n*****************************************\n\n");
159
160
161      // 2. This is an implicitly signed message
162      msg = createSignedMessage(session, 
163                                multipart,
164                                true, 
165                                (AlgorithmID)digestAlgorithm.clone(),
166                                (AlgorithmID)signatureAlgorithm.clone());
167      System.out.println("creating implicitly signed message...");
168      baos.reset();
169      msg.saveChanges();
170      msg.writeTo(baos);
171      bais = new ByteArrayInputStream(baos.toByteArray());
172      msg = new MimeMessage(session, bais);
173      if (PRINT_MESSAGES) {
174        printMessage(msg);
175      }
176      DumpMessage.dumpMsg(msg);
177      
178      System.out.println("\n\n*****************************************\n\n");
179
180        } catch (Exception ex) {
181      ex.printStackTrace();
182          throw new RuntimeException(ex.toString());
183        }
184  }
185  
186  /**
187   * Creates a signed message.
188   *
189   * @param session the mail session
190   * @param dataHandler the content of the message to be signed
191   * @param implicit whether to use implicit (application/pkcs7-mime) or explicit
192   *                 (multipart/signed) signing
193   * @param digestAlgorithm the digest algorithm to be used
194   * @param signatureAlgorithm the signature algorithm to be used                
195   * 
196   * @return the signed message
197   *
198   * @throws MessagingException if an error occurs when creating the message
199   */
200  public Message createSignedMessage(Session session, 
201                                     DataHandler dataHandler,
202                                     boolean implicit,
203                                     AlgorithmID digestAlgorithm,
204                                     AlgorithmID signatureAlgorithm)
205      throws MessagingException {
206
207    String subject = null;
208    StringBuffer buf = new StringBuffer();
209    
210    if (implicit) {
211      subject = "IAIK-S/MIME: Implicitly Signed";
212      buf.append("This message is implicitly signed!\n");
213      buf.append("You need an S/MIME aware mail client to view this message.\n");
214      buf.append("\n\n");
215    } else {
216      subject = "IAIK-S/MIME: Explicitly Signed";
217      buf.append("This message is explicitly signed!\n");
218      buf.append("Every mail client can view this message.\n");
219      buf.append("Non S/MIME mail clients will show the signature as attachment.\n");
220      buf.append("\n\n");
221    }
222    
223    Message msg = createMessage(session, subject);
224    
225    SignedContent sc = new SignedContent(implicit);
226    if (dataHandler != null) {
227      sc.setDataHandler(dataHandler);
228    } else {
229      sc.setText(buf.toString());
230    }
231    sc.setCertificates(signerCertificates);
232
233    try {
234      sc.addSigner(signerPrivateKey, 
235                   signerCertificate,
236                   (AlgorithmID)digestAlgorithm.clone(),
237                   (AlgorithmID)signatureAlgorithm.clone());
238    } catch (NoSuchAlgorithmException ex) {
239      throw new MessagingException("Algorithm not supported: " + ex.getMessage(), ex);
240    }
241
242    msg.setContent(sc, sc.getContentType());
243    // let the SignedContent update some message headers
244    sc.setHeaders(msg);
245    return msg;
246  }
247  
248  /**
249   * Creates a MIME message container with the given subject for the given session.
250   * 
251   * @param session the mail sesion
252   * @param subject the subject of the message
253   *
254   * @return the MIME message with FROM, TO, DATE and SUBJECT headers (without content)
255   *
256   * @throws MessagingException if the message cannot be created
257   */
258  public Message createMessage(Session session, String subject) throws MessagingException {
259    MimeMessage msg = new MimeMessage(session);
260    msg.setFrom(new InternetAddress(from));
261    msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to, false));
262    msg.setSentDate(new Date());
263    msg.setSubject(subject);
264    return msg;
265  }
266  
267  /** 
268   * Prints a dump of the given message to System.out.
269   *
270   * @param msg the message to be dumped to System.out
271   *
272   * @throws IOException if an I/O error occurs
273   */
274  static void printMessage(Message msg) throws IOException {
275    System.out.println("------------------------------------------------------------------");
276    System.out.println("Message dump: \n");
277    try {
278      msg.writeTo(System.out);
279    } catch (MessagingException ex) {
280      throw new IOException(ex.getMessage());   
281    }    
282    System.out.println("\n------------------------------------------------------------------");
283  } 
284
285
286  /**
287   * The main method.
288   */
289  public static void main(String[] argv) throws IOException {
290    double iaikProviderVersion = DemoUtil.getIaikProviderVersion(); 
291    if (iaikProviderVersion <= 3.18) {
292      System.err.println("This demo requires a IAIK provider version > 3.18! Your IAIK provider version is " + iaikProviderVersion + ".");
293    } else {
294      DemoSMimeUtil.initDemos();
295          (new SMimeV3SHA2withDSADemo()).start();
296      System.out.println("\nReady!");
297    }  
298    DemoUtil.waitKey();
299  }
300}