diff --git a/docs/releasenotes.html b/docs/releasenotes.html
index 99b814ca64..1289981c93 100644
--- a/docs/releasenotes.html
+++ b/docs/releasenotes.html
@@ -111,6 +111,7 @@
2.1.2 Defects Fixed
The OpenSSL PEM parsing path hardened its handling of malformed encryption metadata. A PEM block whose "Proc-Type: 4,ENCRYPTED" header was present but whose "DEK-Info" header was missing, or whose DEK-Info value lacked the IV component, previously leaked a NullPointerException (KeyPairParser) or NoSuchElementException out of PEMParser; both the "RSA/DSA/EC PRIVATE KEY" (KeyPairParser) and "PRIVATE KEY" (PrivateKeyParser) paths now reject such input with a PEMException ("malformed PEM data: missing or invalid DEK-Info header"). Separately, PemReader.readPemObject wrapped a malformed base64 body in a DecoderException (a RuntimeException) that escaped the method's declared IOException contract; the Base64 decode is now caught and re-thrown as an IOException with the original cause attached.
The OpenSSL PEMParser path for the traditional "DSA PRIVATE KEY" format silently ignored the version field of the DSAPrivateKey SEQUENCE, so a key whose version was any value other than 0 parsed without complaint. The sibling RSA path already rejects an out-of-range version ("wrong version for RSA private key") and BC's own writer always emits version 0, so the DSA parser now likewise rejects a non-zero version with "wrong version for DSA private key", bringing it into line with the RSA/EC traditional-key parsers (issue #2319).
Follow-up to CVE-2026-5588: the legacy id_alg_composite CompositeVerifier (org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder, used by X509CertificateHolder.isSignatureValid, CMS and TSP) iterated the component count from the supplied signature sequence rather than from the key, so although the earlier fix rejected an empty signature sequence, a composite signature truncated to a verifying prefix (e.g. stripped of its post-quantum or its classical component) still verified. The verifier now requires the signature to carry exactly one component for every component key, rejecting both under- and over-length composite signatures. The final-draft (IANA-OID) composite path was unaffected — it parses a fixed-length concatenation and already verifies every component.
+The S/MIME helpers that spool content to a backing file (SMIMEUtil.toMimeBodyPart / toWriteOnceBodyPart and SMIMESignedParser.getTmpFile / getSafeInstance, in the mail and jmail modules) created their temporary files with java.io.File.createTempFile, which honours the process umask and so leaves the file world-readable (mode 0644) on a typical POSIX system. The spooled bytes are plaintext -- the decrypted body of an enveloped message, the decompressed body of a compressed message, or the signed content -- so on a shared host another local user could read them from the system temp directory before the file was deleted (CWE-377). The four sites now create the file with java.nio.file.Files.createTempFile, which restricts it to the owner (mode 0600 on POSIX, an owner-only ACL on Windows); the temp-directory location and cleanup are unchanged.
2.2.3 Additional Features and Functionality
diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java
index 153c7fa7b5..1850bcef6b 100644
--- a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java
+++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java
@@ -9,6 +9,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.Files;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -72,7 +73,7 @@ private static File getTmpFile()
{
try
{
- return File.createTempFile("bcMail", ".mime");
+ return Files.createTempFile("bcMail", ".mime").toFile();
}
catch (IOException e)
{
@@ -278,7 +279,7 @@ public static SMIMESignedParser getSafeInstance(DigestCalculatorProvider digCalc
{
try
{
- File tmpFile = File.createTempFile("bcSigned", ".tmp");
+ File tmpFile = Files.createTempFile("bcSigned", ".tmp").toFile();
return getSafeInstance(digCalcProvider, message, "7bit", tmpFile);
}
catch (IOException e)
diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEUtil.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEUtil.java
index 5da42f01ae..4b31784d16 100644
--- a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEUtil.java
+++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEUtil.java
@@ -7,6 +7,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.Files;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
@@ -560,7 +561,7 @@ static FileBackedMimeBodyPart toWriteOnceBodyPart(
{
try
{
- return new WriteOnceFileBackedMimeBodyPart(content.getContentStream(), File.createTempFile("bcMail", ".mime"));
+ return new WriteOnceFileBackedMimeBodyPart(content.getContentStream(), Files.createTempFile("bcMail", ".mime").toFile());
}
catch (IOException e)
{
@@ -581,7 +582,7 @@ public static FileBackedMimeBodyPart toMimeBodyPart(
{
try
{
- return toMimeBodyPart(content, File.createTempFile("bcMail", ".mime"));
+ return toMimeBodyPart(content, Files.createTempFile("bcMail", ".mime").toFile());
}
catch (IOException e)
{