Mit dem Ende Februar veröffentlichten BouncyCastle 1.46 wurden die APIs für die Ver- und Entschlüsselung von Nachrichten nach dem S/MIME-Standard teils stark verändert. Dies erhöht auf der einen Seite die Flexibilität, erfordert aber auf der anderen Seite eine Umstellung von bestehendem Code, wenn dieser nicht mehr die nun als “deprecated” gekennzeichneten APIs nutzen soll.
Ein Beispiel für die Verschlüsselung einer S/MIME-Nachricht sieht jetzt folgendermaßen aus:
public MimeMessage encryptMessage(MimeMessage message) throws Exception {
SMIMEEnvelopedGenerator smeg = new SMIMEEnvelopedGenerator();
for (Address recipient : message.getAllRecipients()) {
Collection certificates = getCertificates((InternetAddress) recipient);
for (Certificate cert : certificates) {
RecipientInfoGenerator recipientInfoGen = new JceKeyTransRecipientInfoGenerator(
(X509Certificate) cert);
smeg.addRecipientInfoGenerator(recipientInfoGen);
}
}
OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(
CMSAlgorithm.AES256_CBC).build();
MimeBodyPart encryptedContent = smeg.generate(message, encryptor);
MimeMessage result = new MimeMessage(message);
result.setContent(encryptedContent.getContent(), encryptedContent
.getContentType());
result.saveChanges();
return result;
}
Auf dem Rückweg, also für die Entschlüsselung, kann folgender Code verwendet werden:
public MimeMessage decryptMessage(MimeMessage encrypted)
throws MessagingException, Exception {
SMIMEEnveloped message = new SMIMEEnveloped(encrypted);
RecipientInformationStore recinfos = message.getRecipientInfos();
Enumeration aliases = this.keystore.aliases();
RecipientInformation recid = null;
String alias = null;
while ((recid == null) && aliases.hasMoreElements()) {
alias = aliases.nextElement();
if (this.keystore.isKeyEntry(alias)) {
recid = recinfos.get(new JceKeyTransRecipientId(
(X509Certificate) this.keystore.getCertificate(alias)));
}
}
if (recid == null) {
throw new RuntimeException("No decryption key found");
}
JceKeyTransEnvelopedRecipient recipient = new JceKeyTransEnvelopedRecipient(
(PrivateKey) this.keystore.getKey(alias, "changeit"
.toCharArray()));
byte[] content = recid.getContent(recipient);
MimeMessage decrypted = new MimeMessage(Session
.getDefaultInstance(System.getProperties()),
new ByteArrayInputStream(content));
decrypted.saveChanges();
return decrypted;
}
Ein komplettes Codebeispiel für die BouncyCastle 1.46
S/MIME-Funktionalität befindet sich in diesem Zip-Archiv.
Nachtrag: Die kompletten Zertifikate sind in verschlüsselten Nachrichten nicht enthalten. BouncyCastle packt die Empfängerinformationen in RecipientInformation-Instanzen. Mit folgendem Codefragment kann man diese Informationen ausgeben lassen:
public MimeMessage decryptMessage(final MimeMessage encrypted)
throws MessagingException, Exception {
SMIMEEnveloped message = new SMIMEEnveloped(encrypted);
RecipientInformationStore recinfos = message.getRecipientInfos();
printRecInfos(recinfos);
...
private void printRecInfos(final RecipientInformationStore recinfos) {
for (Object info : recinfos.getRecipients()) {
if (info instanceof RecipientInformation) {
System.out.println(((RecipientInformation) info).getRID());
}
}
}