Skip to content

Commit 479f57c

Browse files
committed
compatibility class
1 parent 413fa88 commit 479f57c

File tree

1 file changed

+371
-0
lines changed

1 file changed

+371
-0
lines changed
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
package org.bouncycastle.pkix;
2+
3+
import java.io.IOException;
4+
import java.math.BigInteger;
5+
import java.security.AccessControlException;
6+
import java.security.AccessController;
7+
import java.security.PrivilegedAction;
8+
import java.security.Security;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
import java.util.WeakHashMap;
12+
13+
import org.bouncycastle.asn1.ASN1Integer;
14+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
15+
import org.bouncycastle.asn1.ASN1Sequence;
16+
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
17+
import org.bouncycastle.asn1.pkcs.RSAPublicKey;
18+
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
19+
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
20+
import org.bouncycastle.asn1.x9.X962Parameters;
21+
import org.bouncycastle.asn1.x9.X9FieldID;
22+
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
23+
import org.bouncycastle.crypto.CryptoServicesRegistrar;
24+
import org.bouncycastle.math.Primes;
25+
import org.bouncycastle.util.Strings;
26+
27+
/**
28+
* A checker for vetting subject public keys based on the direct checking of the ASN.1
29+
*/
30+
public class SubjectPublicKeyInfoChecker
31+
{
32+
private static final Cache validatedQs = new Cache();
33+
private static final Cache validatedMods = new Cache();
34+
35+
// Hexadecimal value of the product of the 131 smallest odd primes from 3 to 743
36+
private static final BigInteger SMALL_PRIMES_PRODUCT = new BigInteger(
37+
"8138e8a0fcf3a4e84a771d40fd305d7f4aa59306d7251de54d98af8fe95729a1f"
38+
+ "73d893fa424cd2edc8636a6c3285e022b0e3866a565ae8108eed8591cd4fe8d2"
39+
+ "ce86165a978d719ebf647f362d33fca29cd179fb42401cbaf3df0c614056f9c8"
40+
+ "f3cfd51e474afb6bc6974f78db8aba8e9e517fded658591ab7502bd41849462f",
41+
16);
42+
43+
private static final BigInteger ONE = BigInteger.valueOf(1);
44+
45+
public static void checkInfo(SubjectPublicKeyInfo pubInfo)
46+
{
47+
ASN1ObjectIdentifier algorithm = pubInfo.getAlgorithm().getAlgorithm();
48+
if (X9ObjectIdentifiers.id_ecPublicKey.equals(algorithm))
49+
{
50+
X962Parameters params = X962Parameters.getInstance(pubInfo.getAlgorithm().getParameters());
51+
if (params.isImplicitlyCA() || params.isNamedCurve())
52+
{
53+
return;
54+
}
55+
56+
ASN1Sequence ecParameters = ASN1Sequence.getInstance(params.getParameters());
57+
X9FieldID fieldID = X9FieldID.getInstance(ecParameters.getObjectAt(1));
58+
if (fieldID.getIdentifier().equals(X9FieldID.prime_field))
59+
{
60+
BigInteger q = ASN1Integer.getInstance(fieldID.getParameters()).getValue();
61+
62+
if (validatedQs.contains(q))
63+
{
64+
return;
65+
}
66+
67+
int maxBitLength = Properties.asInteger("org.bouncycastle.ec.fp_max_size", 1042); // 2 * 521
68+
int certainty = Properties.asInteger("org.bouncycastle.ec.fp_certainty", 100);
69+
70+
int qBitLength = q.bitLength();
71+
if (maxBitLength < qBitLength)
72+
{
73+
throw new IllegalArgumentException("Fp q value out of range");
74+
}
75+
if (Primes.hasAnySmallFactors(q) || !Primes.isMRProbablePrime(
76+
q, CryptoServicesRegistrar.getSecureRandom(), getNumberOfIterations(qBitLength, certainty)))
77+
{
78+
throw new IllegalArgumentException("Fp q value not prime");
79+
}
80+
81+
validatedQs.add(q);
82+
}
83+
}
84+
else if (PKCSObjectIdentifiers.rsaEncryption.equals(algorithm)
85+
|| X509ObjectIdentifiers.id_ea_rsa.equals(algorithm)
86+
|| PKCSObjectIdentifiers.id_RSAES_OAEP.equals(algorithm)
87+
|| PKCSObjectIdentifiers.id_RSASSA_PSS.equals(algorithm))
88+
{
89+
RSAPublicKey params;
90+
try
91+
{
92+
params = RSAPublicKey.getInstance(pubInfo.parsePublicKey());
93+
}
94+
catch (IOException e)
95+
{
96+
throw new IllegalArgumentException("unable to parse RSA key");
97+
}
98+
99+
if ((params.getPublicExponent().intValue() & 1) == 0)
100+
{
101+
throw new IllegalArgumentException("RSA publicExponent is even");
102+
}
103+
104+
if (!validatedMods.contains(params.getModulus()))
105+
{
106+
validate(params.getModulus());
107+
108+
validatedMods.add(params.getModulus());
109+
}
110+
}
111+
}
112+
113+
private static void validate(BigInteger modulus)
114+
{
115+
if ((modulus.intValue() & 1) == 0)
116+
{
117+
throw new IllegalArgumentException("RSA modulus is even");
118+
}
119+
120+
// If you need to set this you need to have a serious word to whoever is sending your keys.
121+
if (Properties.isOverrideSet("org.bouncycastle.rsa.allow_unsafe_mod"))
122+
{
123+
return;
124+
}
125+
126+
int maxBitLength = Properties.asInteger("org.bouncycastle.rsa.max_size", 15360);
127+
128+
int modBitLength = modulus.bitLength();
129+
if (maxBitLength < modBitLength)
130+
{
131+
throw new IllegalArgumentException("modulus value out of range");
132+
}
133+
134+
if (!modulus.gcd(SMALL_PRIMES_PRODUCT).equals(ONE))
135+
{
136+
throw new IllegalArgumentException("RSA modulus has a small prime factor");
137+
}
138+
139+
int bits = modulus.bitLength() / 2;
140+
int iterations = bits >= 1536 ? 3
141+
: bits >= 1024 ? 4
142+
: bits >= 512 ? 7
143+
: 50;
144+
145+
Primes.MROutput mr = Primes.enhancedMRProbablePrimeTest(modulus, CryptoServicesRegistrar.getSecureRandom(), iterations);
146+
if (!mr.isProvablyComposite())
147+
{
148+
throw new IllegalArgumentException("RSA modulus is not composite");
149+
}
150+
}
151+
152+
private static int getNumberOfIterations(int bits, int certainty)
153+
{
154+
/*
155+
* NOTE: We enforce a minimum 'certainty' of 100 for bits >= 1024 (else 80). Where the
156+
* certainty is higher than the FIPS 186-4 tables (C.2/C.3) cater to, extra iterations
157+
* are added at the "worst case rate" for the excess.
158+
*/
159+
if (bits >= 1536)
160+
{
161+
return certainty <= 100 ? 3
162+
: certainty <= 128 ? 4
163+
: 4 + (certainty - 128 + 1) / 2;
164+
}
165+
else if (bits >= 1024)
166+
{
167+
return certainty <= 100 ? 4
168+
: certainty <= 112 ? 5
169+
: 5 + (certainty - 112 + 1) / 2;
170+
}
171+
else if (bits >= 512)
172+
{
173+
return certainty <= 80 ? 5
174+
: certainty <= 100 ? 7
175+
: 7 + (certainty - 100 + 1) / 2;
176+
}
177+
else
178+
{
179+
return certainty <= 80 ? 40
180+
: 40 + (certainty - 80 + 1) / 2;
181+
}
182+
}
183+
184+
/**
185+
* Enable the specified override property for the current thread only.
186+
*
187+
* @param propertyName the property name for the override.
188+
* @param enable true if the override should be enabled, false if it should be disabled.
189+
* @return true if the override was already set true, false otherwise.
190+
*/
191+
public static boolean setThreadOverride(String propertyName, boolean enable)
192+
{
193+
return Properties.setThreadOverride(propertyName, enable);
194+
}
195+
196+
/**
197+
* Remove any value for the specified override property for the current thread only.
198+
*
199+
* @param propertyName the property name for the override.
200+
* @return true if the override was already set true in thread local, false otherwise.
201+
*/
202+
public static boolean removeThreadOverride(String propertyName)
203+
{
204+
return Properties.removeThreadOverride(propertyName);
205+
}
206+
207+
private static class Cache
208+
{
209+
private final Map<BigInteger, Boolean> values = new WeakHashMap<BigInteger, Boolean>();
210+
private final BigInteger[] preserve = new BigInteger[8];
211+
212+
private int preserveCounter = 0;
213+
214+
public synchronized void add(BigInteger value)
215+
{
216+
values.put(value, Boolean.TRUE);
217+
preserve[preserveCounter] = value;
218+
preserveCounter = (preserveCounter + 1) % preserve.length;
219+
}
220+
221+
public synchronized boolean contains(BigInteger value)
222+
{
223+
return values.containsKey(value);
224+
}
225+
226+
public synchronized int size()
227+
{
228+
return values.size();
229+
}
230+
231+
public synchronized void clear()
232+
{
233+
values.clear();
234+
for (int i = 0; i != preserve.length; i++)
235+
{
236+
preserve[i] = null;
237+
}
238+
}
239+
}
240+
241+
private static class Properties
242+
{
243+
private Properties()
244+
{
245+
}
246+
247+
private static final ThreadLocal threadProperties = new ThreadLocal();
248+
249+
/**
250+
* Return whether a particular override has been set to true.
251+
*
252+
* @param propertyName the property name for the override.
253+
* @return true if the property is set to "true", false otherwise.
254+
*/
255+
static boolean isOverrideSet(String propertyName)
256+
{
257+
try
258+
{
259+
return isSetTrue(getPropertyValue(propertyName));
260+
}
261+
catch (AccessControlException e)
262+
{
263+
return false;
264+
}
265+
}
266+
267+
static boolean setThreadOverride(String propertyName, boolean enable)
268+
{
269+
boolean isSet = isOverrideSet(propertyName);
270+
271+
Map localProps = (Map)threadProperties.get();
272+
if (localProps == null)
273+
{
274+
localProps = new HashMap();
275+
276+
threadProperties.set(localProps);
277+
}
278+
279+
localProps.put(propertyName, enable ? "true" : "false");
280+
281+
return isSet;
282+
}
283+
284+
static boolean removeThreadOverride(String propertyName)
285+
{
286+
Map localProps = (Map)threadProperties.get();
287+
if (localProps != null)
288+
{
289+
String p = (String)localProps.remove(propertyName);
290+
if (p != null)
291+
{
292+
return "true".equals(Strings.toLowerCase(p));
293+
}
294+
}
295+
296+
return false;
297+
}
298+
299+
/**
300+
* Return propertyName as an integer, defaultValue used if not defined.
301+
*
302+
* @param propertyName name of property.
303+
* @param defaultValue integer to return if property not defined.
304+
* @return value of property, or default if not found, as an int.
305+
*/
306+
static int asInteger(String propertyName, int defaultValue)
307+
{
308+
String p = getPropertyValue(propertyName);
309+
310+
if (p != null)
311+
{
312+
return Integer.parseInt(p);
313+
}
314+
315+
return defaultValue;
316+
}
317+
318+
/**
319+
* Return the String value of the property propertyName. Property valuation
320+
* starts with java.security, then thread local, then system properties.
321+
*
322+
* @param propertyName name of property.
323+
* @return value of property as a String, null if not defined.
324+
*/
325+
static String getPropertyValue(final String propertyName)
326+
{
327+
String val = (String)AccessController.doPrivileged(new PrivilegedAction()
328+
{
329+
public Object run()
330+
{
331+
return Security.getProperty(propertyName);
332+
}
333+
});
334+
if (val != null)
335+
{
336+
return val;
337+
}
338+
339+
Map localProps = (Map)threadProperties.get();
340+
if (localProps != null)
341+
{
342+
String p = (String)localProps.get(propertyName);
343+
if (p != null)
344+
{
345+
return p;
346+
}
347+
}
348+
349+
return (String)AccessController.doPrivileged(new PrivilegedAction()
350+
{
351+
public Object run()
352+
{
353+
return System.getProperty(propertyName);
354+
}
355+
});
356+
}
357+
358+
private static boolean isSetTrue(String p)
359+
{
360+
if (p == null || p.length() != 4)
361+
{
362+
return false;
363+
}
364+
365+
return (p.charAt(0) == 't' || p.charAt(0) == 'T')
366+
&& (p.charAt(1) == 'r' || p.charAt(1) == 'R')
367+
&& (p.charAt(2) == 'u' || p.charAt(2) == 'U')
368+
&& (p.charAt(3) == 'e' || p.charAt(3) == 'E');
369+
}
370+
}
371+
}

0 commit comments

Comments
 (0)