Skip to content

Commit a25e8b5

Browse files
committed
added JournaledAlgorithm utility
added test for the same. removed use of new SecureRandom() from AES AlgorithmParametersGenerator Added support for configuring the BER generator for octet strings to SMIMEEnvelopedWriter.
1 parent a5075e3 commit a25e8b5

File tree

4 files changed

+475
-1
lines changed

4 files changed

+475
-1
lines changed
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package org.bouncycastle.crypto.util;
2+
3+
import java.io.BufferedInputStream;
4+
import java.io.File;
5+
import java.io.FileInputStream;
6+
import java.io.FileOutputStream;
7+
import java.io.IOException;
8+
import java.io.InputStream;
9+
import java.io.ObjectInputStream;
10+
import java.io.ObjectOutputStream;
11+
import java.io.OutputStream;
12+
import java.io.Serializable;
13+
import java.security.SecureRandom;
14+
15+
import org.bouncycastle.asn1.ASN1EncodableVector;
16+
import org.bouncycastle.asn1.ASN1OctetString;
17+
import org.bouncycastle.asn1.ASN1Sequence;
18+
import org.bouncycastle.asn1.DEROctetString;
19+
import org.bouncycastle.asn1.DERSequence;
20+
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
21+
import org.bouncycastle.crypto.CryptoServicesRegistrar;
22+
import org.bouncycastle.util.Encodable;
23+
import org.bouncycastle.util.io.Streams;
24+
25+
/**
26+
* JournaledAlgorithm keeps state of the JournalingSecureRandom and the
27+
* AlgorithmIdentifier necessary to fully resume an encryption session. This
28+
* class can be used to retrieve a session even if a process is completely
29+
* stopped. NOTE: This should be used with a shutdown hook to save the state of
30+
* the journaling and the algorithm identifier even in the case of a forced
31+
* shutdown.
32+
* <p>
33+
* The raw encoding is in ASN.1 format.
34+
* </p>
35+
* <p>
36+
* Details: Use serialization of critical parameters of the the
37+
* JournalingSecureRandom and AlgorithmIdentifier. Because these two classes are
38+
* not serializable, create interior class to serialize only the critical
39+
* parameters in the form of byte[] arrays
40+
*/
41+
42+
public class JournaledAlgorithm
43+
implements Encodable, Serializable
44+
{
45+
private transient JournalingSecureRandom journaling;
46+
47+
private transient AlgorithmIdentifier algID;
48+
49+
public JournaledAlgorithm(AlgorithmIdentifier aid, JournalingSecureRandom journaling)
50+
{
51+
if (aid == null)
52+
{
53+
throw new NullPointerException("AlgorithmIdentifier passed to JournaledAlgorithm is null");
54+
}
55+
else if (journaling == null)
56+
{
57+
throw new NullPointerException("JournalingSecureRandom passed to JournaledAlgorithm is null");
58+
}
59+
60+
this.journaling = journaling;
61+
62+
this.algID = aid;
63+
}
64+
65+
/**
66+
* Construct from a previous encoding, using CryptoServicesRegistrar.getSecureRandom() as the backup source of entropy.
67+
*
68+
* @param encoding raw encoding of a previous JournaledAlgorithm.
69+
*/
70+
public JournaledAlgorithm(byte[] encoding)
71+
{
72+
this(encoding, CryptoServicesRegistrar.getSecureRandom());
73+
}
74+
75+
/**
76+
* Construct from a previous encoding, using the passed in random as a source for when the existing entropy runs out.
77+
*
78+
* @param encoding raw encoding of a previous JournaledAlgorithm.
79+
* @param random back up source of entropy.
80+
*/
81+
public JournaledAlgorithm(byte[] encoding, SecureRandom random)
82+
{
83+
if (encoding == null)
84+
{
85+
throw new NullPointerException("encoding passed to JournaledAlgorithm is null");
86+
}
87+
else if (random == null)
88+
{
89+
throw new NullPointerException("random passed to JournaledAlgorithm is null");
90+
}
91+
92+
initFromEncoding(encoding, random);
93+
}
94+
95+
private void initFromEncoding(byte[] encoding, SecureRandom random)
96+
{
97+
ASN1Sequence seq = ASN1Sequence.getInstance(encoding);
98+
99+
this.algID = AlgorithmIdentifier.getInstance(seq.getObjectAt(0));
100+
this.journaling = new JournalingSecureRandom(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets(), random);
101+
}
102+
103+
public JournalingSecureRandom getJournalingSecureRandom()
104+
{
105+
return journaling;
106+
}
107+
108+
public AlgorithmIdentifier getAlgorithmIdentifier()
109+
{
110+
return algID;
111+
}
112+
113+
/**
114+
* Store state of JournalingSecureRandom and AlgorithmIdentifier in temporary
115+
* file
116+
*
117+
* @param tempfile
118+
* @throws IOException
119+
*/
120+
public void storeState(File tempfile)
121+
throws IOException
122+
{
123+
if (tempfile == null)
124+
{
125+
throw new NullPointerException("file for storage is null in JournaledAlgorithm");
126+
}
127+
128+
// Extract key information in byte[] form
129+
FileOutputStream fOut = new FileOutputStream(tempfile);
130+
131+
try
132+
{
133+
storeState(fOut);
134+
}
135+
finally
136+
{
137+
fOut.close();
138+
}
139+
}
140+
141+
public void storeState(OutputStream out)
142+
throws IOException
143+
{
144+
if (out == null)
145+
{
146+
throw new NullPointerException("output stream for storage is null in JournaledAlgorithm");
147+
}
148+
149+
out.write(this.getEncoded());
150+
}
151+
152+
public static JournaledAlgorithm getState(InputStream stateIn, SecureRandom random)
153+
throws IOException, ClassNotFoundException
154+
{
155+
if (stateIn == null)
156+
{
157+
throw new NullPointerException("stream for loading is null in JournaledAlgorithm");
158+
}
159+
160+
InputStream fIn = new BufferedInputStream(stateIn);
161+
162+
try
163+
{
164+
return new JournaledAlgorithm(Streams.readAll(fIn), random);
165+
}
166+
finally
167+
{
168+
fIn.close();
169+
}
170+
}
171+
172+
/**
173+
* Reconstructs JournaledAlgorithm session from file containing it's raw encoding.
174+
*
175+
* @param tempfile temporary file containing serialized state
176+
* @return
177+
* @throws IOException
178+
* @throws ClassNotFoundException
179+
*/
180+
public static JournaledAlgorithm getState(File tempfile, SecureRandom random)
181+
throws IOException, ClassNotFoundException
182+
{
183+
if (tempfile == null)
184+
{
185+
throw new NullPointerException("File for loading is null in JournaledAlgorithm");
186+
}
187+
188+
InputStream fIn = new BufferedInputStream(new FileInputStream(tempfile));
189+
190+
try
191+
{
192+
return new JournaledAlgorithm(Streams.readAll(fIn), random);
193+
}
194+
finally
195+
{
196+
fIn.close();
197+
}
198+
}
199+
200+
@Override
201+
public byte[] getEncoded()
202+
throws IOException
203+
{
204+
ASN1EncodableVector v = new ASN1EncodableVector();
205+
206+
v.add(algID);
207+
v.add(new DEROctetString(journaling.getFullTranscript()));
208+
209+
return new DERSequence(v).getEncoded();
210+
}
211+
212+
private void readObject(
213+
ObjectInputStream in)
214+
throws IOException, ClassNotFoundException
215+
{
216+
in.defaultReadObject();
217+
218+
initFromEncoding((byte[])in.readObject(), CryptoServicesRegistrar.getSecureRandom());
219+
}
220+
221+
private void writeObject(
222+
ObjectOutputStream out)
223+
throws IOException
224+
{
225+
out.defaultWriteObject();
226+
227+
out.writeObject(getEncoded());
228+
}
229+
}

pkix/src/main/java/org/bouncycastle/mime/smime/SMIMEEnvelopedWriter.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ public Builder()
6060
}
6161
}
6262

63+
/**
64+
* Set the underlying string size for encapsulated data
65+
*
66+
* @param bufferSize length of octet strings to buffer the data.
67+
*/
68+
public Builder setBufferSize(
69+
int bufferSize)
70+
{
71+
this.envGen.setBufferSize(bufferSize);
72+
73+
return this;
74+
}
75+
6376
public Builder setUnprotectedAttributeGenerator(CMSAttributeTableGenerator unprotectedAttributeGenerator)
6477
{
6578
this.envGen.setUnprotectedAttributeGenerator(unprotectedAttributeGenerator);

0 commit comments

Comments
 (0)