Skip to content

Commit 0b8f18b

Browse files
author
Valerie Peng
committedJun 12, 2020
8246613: Choose the default SecureRandom algo based on registration ordering
Fixed java.security.Provider and SecureRandom to use the 1st registered SecureRandom service Reviewed-by: weijun, mullan
1 parent edefd3c commit 0b8f18b

File tree

3 files changed

+220
-102
lines changed

3 files changed

+220
-102
lines changed
 

‎src/java.base/share/classes/java/security/Provider.java

+109-39
Original file line numberDiff line numberDiff line change
@@ -858,10 +858,18 @@ private void check(String directive) {
858858
// serviceMap changed since last call to getServices()
859859
private volatile transient boolean servicesChanged;
860860

861+
// Map<String,String> used to keep track of legacy registration
862+
private transient Map<String,String> legacyStrings;
863+
861864
// Map<ServiceKey,Service>
862865
// used for services added via putService(), initialized on demand
863866
private transient Map<ServiceKey,Service> serviceMap;
864867

868+
// For backward compatibility, the registration ordering of
869+
// SecureRandom (RNG) algorithms needs to be preserved for
870+
// "new SecureRandom()" calls when this provider is used
871+
private transient Set<Service> prngServices;
872+
865873
// Map<ServiceKey,Service>
866874
// used for services added via legacy methods, init on demand
867875
private transient Map<ServiceKey,Service> legacyMap;
@@ -913,12 +921,18 @@ private void readObject(ObjectInputStream in)
913921
putAll(copy);
914922
}
915923

916-
private static boolean isProviderInfo(Object key) {
924+
// check whether to update 'legacyString' with the specified key
925+
private boolean checkLegacy(Object key) {
917926
String keyString = (String)key;
918927
if (keyString.startsWith("Provider.")) {
919-
return true;
928+
return false;
929+
}
930+
931+
legacyChanged = true;
932+
if (legacyStrings == null) {
933+
legacyStrings = new LinkedHashMap<>();
920934
}
921-
return false;
935+
return true;
922936
}
923937

924938
/**
@@ -934,41 +948,42 @@ private void implPutAll(Map<?,?> t) {
934948

935949
private Object implRemove(Object key) {
936950
if (key instanceof String) {
937-
if (isProviderInfo(key)) {
951+
if (!checkLegacy(key)) {
938952
return null;
939953
}
940-
legacyChanged = true;
954+
legacyStrings.remove((String)key);
941955
}
942956
return super.remove(key);
943957
}
944958

945959
private boolean implRemove(Object key, Object value) {
946960
if (key instanceof String && value instanceof String) {
947-
if (isProviderInfo(key)) {
961+
if (!checkLegacy(key)) {
948962
return false;
949963
}
950-
legacyChanged = true;
964+
legacyStrings.remove((String)key, (String)value);
951965
}
952966
return super.remove(key, value);
953967
}
954968

955969
private boolean implReplace(Object key, Object oldValue, Object newValue) {
956970
if ((key instanceof String) && (oldValue instanceof String) &&
957971
(newValue instanceof String)) {
958-
if (isProviderInfo(key)) {
972+
if (!checkLegacy(key)) {
959973
return false;
960974
}
961-
legacyChanged = true;
975+
legacyStrings.replace((String)key, (String)oldValue,
976+
(String)newValue);
962977
}
963978
return super.replace(key, oldValue, newValue);
964979
}
965980

966981
private Object implReplace(Object key, Object value) {
967982
if ((key instanceof String) && (value instanceof String)) {
968-
if (isProviderInfo(key)) {
983+
if (!checkLegacy(key)) {
969984
return null;
970985
}
971-
legacyChanged = true;
986+
legacyStrings.replace((String)key, (String)value);
972987
}
973988
return super.replace(key, value);
974989
}
@@ -977,17 +992,26 @@ private Object implReplace(Object key, Object value) {
977992
private void implReplaceAll(BiFunction<? super Object, ? super Object,
978993
? extends Object> function) {
979994
legacyChanged = true;
995+
if (legacyStrings == null) {
996+
legacyStrings = new LinkedHashMap<>();
997+
} else {
998+
legacyStrings.replaceAll((BiFunction<? super String, ? super String,
999+
? extends String>) function);
1000+
}
9801001
super.replaceAll(function);
9811002
}
9821003

9831004
@SuppressWarnings("unchecked") // Function must actually operate over strings
984-
private Object implMerge(Object key, Object value, BiFunction<? super Object,
985-
? super Object, ? extends Object> remappingFunction) {
1005+
private Object implMerge(Object key, Object value,
1006+
BiFunction<? super Object, ? super Object, ? extends Object>
1007+
remappingFunction) {
9861008
if ((key instanceof String) && (value instanceof String)) {
987-
if (isProviderInfo(key)) {
1009+
if (!checkLegacy(key)) {
9881010
return null;
9891011
}
990-
legacyChanged = true;
1012+
legacyStrings.merge((String)key, (String)value,
1013+
(BiFunction<? super String, ? super String,
1014+
? extends String>) remappingFunction);
9911015
}
9921016
return super.merge(key, value, remappingFunction);
9931017
}
@@ -996,10 +1020,12 @@ private Object implMerge(Object key, Object value, BiFunction<? super Object,
9961020
private Object implCompute(Object key, BiFunction<? super Object,
9971021
? super Object, ? extends Object> remappingFunction) {
9981022
if (key instanceof String) {
999-
if (isProviderInfo(key)) {
1023+
if (!checkLegacy(key)) {
10001024
return null;
10011025
}
1002-
legacyChanged = true;
1026+
legacyStrings.compute((String) key,
1027+
(BiFunction<? super String,? super String,
1028+
? extends String>) remappingFunction);
10031029
}
10041030
return super.compute(key, remappingFunction);
10051031
}
@@ -1008,10 +1034,12 @@ private Object implCompute(Object key, BiFunction<? super Object,
10081034
private Object implComputeIfAbsent(Object key, Function<? super Object,
10091035
? extends Object> mappingFunction) {
10101036
if (key instanceof String) {
1011-
if (isProviderInfo(key)) {
1037+
if (!checkLegacy(key)) {
10121038
return null;
10131039
}
1014-
legacyChanged = true;
1040+
legacyStrings.computeIfAbsent((String) key,
1041+
(Function<? super String, ? extends String>)
1042+
mappingFunction);
10151043
}
10161044
return super.computeIfAbsent(key, mappingFunction);
10171045
}
@@ -1020,42 +1048,48 @@ private Object implComputeIfAbsent(Object key, Function<? super Object,
10201048
private Object implComputeIfPresent(Object key, BiFunction<? super Object,
10211049
? super Object, ? extends Object> remappingFunction) {
10221050
if (key instanceof String) {
1023-
if (isProviderInfo(key)) {
1051+
if (!checkLegacy(key)) {
10241052
return null;
10251053
}
1026-
legacyChanged = true;
1054+
legacyStrings.computeIfPresent((String) key,
1055+
(BiFunction<? super String, ? super String,
1056+
? extends String>) remappingFunction);
10271057
}
10281058
return super.computeIfPresent(key, remappingFunction);
10291059
}
10301060

10311061
private Object implPut(Object key, Object value) {
10321062
if ((key instanceof String) && (value instanceof String)) {
1033-
if (isProviderInfo(key)) {
1063+
if (!checkLegacy(key)) {
10341064
return null;
10351065
}
1036-
legacyChanged = true;
1066+
legacyStrings.put((String)key, (String)value);
10371067
}
10381068
return super.put(key, value);
10391069
}
10401070

10411071
private Object implPutIfAbsent(Object key, Object value) {
10421072
if ((key instanceof String) && (value instanceof String)) {
1043-
if (isProviderInfo(key)) {
1073+
if (!checkLegacy(key)) {
10441074
return null;
10451075
}
1046-
legacyChanged = true;
1076+
legacyStrings.putIfAbsent((String)key, (String)value);
10471077
}
10481078
return super.putIfAbsent(key, value);
10491079
}
10501080

10511081
private void implClear() {
1082+
if (legacyStrings != null) {
1083+
legacyStrings.clear();
1084+
}
10521085
if (legacyMap != null) {
10531086
legacyMap.clear();
10541087
}
10551088
serviceMap.clear();
10561089
legacyChanged = false;
10571090
servicesChanged = false;
10581091
serviceSet = null;
1092+
prngServices = null;
10591093
super.clear();
10601094
putId();
10611095
}
@@ -1095,7 +1129,7 @@ boolean matches(String type, String algorithm) {
10951129
* service objects.
10961130
*/
10971131
private void ensureLegacyParsed() {
1098-
if (legacyChanged == false) {
1132+
if (legacyChanged == false || (legacyStrings == null)) {
10991133
return;
11001134
}
11011135
serviceSet = null;
@@ -1104,7 +1138,7 @@ private void ensureLegacyParsed() {
11041138
} else {
11051139
legacyMap.clear();
11061140
}
1107-
for (Map.Entry<?,?> entry : super.entrySet()) {
1141+
for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
11081142
parseLegacyPut(entry.getKey(), entry.getValue());
11091143
}
11101144
removeInvalidServices(legacyMap);
@@ -1125,12 +1159,12 @@ private void removeInvalidServices(Map<ServiceKey,Service> map) {
11251159
}
11261160
}
11271161

1128-
private String[] getTypeAndAlgorithm(String key) {
1162+
private static String[] getTypeAndAlgorithm(String key) {
11291163
int i = key.indexOf('.');
11301164
if (i < 1) {
11311165
if (debug != null) {
1132-
debug.println("Ignoring invalid entry in provider "
1133-
+ name + ":" + key);
1166+
debug.println("Ignoring invalid entry in provider: "
1167+
+ key);
11341168
}
11351169
return null;
11361170
}
@@ -1143,15 +1177,7 @@ private String[] getTypeAndAlgorithm(String key) {
11431177
private static final String ALIAS_PREFIX_LOWER = "alg.alias.";
11441178
private static final int ALIAS_LENGTH = ALIAS_PREFIX.length();
11451179

1146-
private void parseLegacyPut(Object k, Object v) {
1147-
if (!(k instanceof String) || !(v instanceof String)) {
1148-
return;
1149-
}
1150-
String name = (String) k;
1151-
String value = (String) v;
1152-
if (isProviderInfo(name)) {
1153-
return;
1154-
}
1180+
private void parseLegacyPut(String name, String value) {
11551181
if (name.toLowerCase(ENGLISH).startsWith(ALIAS_PREFIX_LOWER)) {
11561182
// e.g. put("Alg.Alias.MessageDigest.SHA", "SHA-1");
11571183
// aliasKey ~ MessageDigest.SHA
@@ -1193,6 +1219,10 @@ private void parseLegacyPut(Object k, Object v) {
11931219
legacyMap.put(key, s);
11941220
}
11951221
s.className = className;
1222+
1223+
if (type.equals("SecureRandom")) {
1224+
updateSecureRandomEntries(true, s);
1225+
}
11961226
} else { // attribute
11971227
// e.g. put("MessageDigest.SHA-1 ImplementedIn", "Software");
11981228
String attributeValue = value;
@@ -1352,9 +1382,46 @@ protected void putService(Service s) {
13521382
servicesChanged = true;
13531383
synchronized (this) {
13541384
putPropertyStrings(s);
1385+
if (type.equals("SecureRandom")) {
1386+
updateSecureRandomEntries(true, s);
1387+
}
13551388
}
13561389
}
13571390

1391+
private void updateSecureRandomEntries(boolean doAdd, Service s) {
1392+
Objects.requireNonNull(s);
1393+
if (doAdd) {
1394+
if (prngServices == null) {
1395+
prngServices = new LinkedHashSet<Service>();
1396+
}
1397+
prngServices.add(s);
1398+
} else {
1399+
prngServices.remove(s);
1400+
}
1401+
1402+
if (debug != null) {
1403+
debug.println((doAdd? "Add":"Remove") + " SecureRandom algo " +
1404+
s.getAlgorithm());
1405+
}
1406+
}
1407+
1408+
// used by new SecureRandom() to find out the default SecureRandom
1409+
// service for this provider
1410+
synchronized Service getDefaultSecureRandomService() {
1411+
checkInitialized();
1412+
1413+
if (legacyChanged) {
1414+
prngServices = null;
1415+
ensureLegacyParsed();
1416+
}
1417+
1418+
if (prngServices != null && !prngServices.isEmpty()) {
1419+
return prngServices.iterator().next();
1420+
}
1421+
1422+
return null;
1423+
}
1424+
13581425
/**
13591426
* Put the string properties for this Service in this Provider's
13601427
* Hashtable.
@@ -1448,6 +1515,9 @@ private void implRemoveService(Service s) {
14481515
}
14491516
synchronized (this) {
14501517
removePropertyStrings(s);
1518+
if (type.equals("SecureRandom")) {
1519+
updateSecureRandomEntries(false, s);
1520+
}
14511521
}
14521522
}
14531523

‎src/java.base/share/classes/java/security/SecureRandom.java

+32-47
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -259,35 +259,51 @@ public SecureRandom(byte[] seed) {
259259
}
260260

261261
private void getDefaultPRNG(boolean setSeed, byte[] seed) {
262-
String prng = getPrngAlgorithm();
263-
if (prng == null) {
264-
// bummer, get the SUN implementation
265-
prng = "SHA1PRNG";
262+
Service prngService = null;
263+
String prngAlgorithm = null;
264+
for (Provider p : Providers.getProviderList().providers()) {
265+
// SUN provider uses the SunEntries.DEF_SECURE_RANDOM_ALGO
266+
// as the default SecureRandom algorithm; for other providers,
267+
// Provider.getDefaultSecureRandom() will use the 1st
268+
// registered SecureRandom algorithm
269+
if (p.getName().equals("SUN")) {
270+
prngAlgorithm = SunEntries.DEF_SECURE_RANDOM_ALGO;
271+
prngService = p.getService("SecureRandom", prngAlgorithm);
272+
break;
273+
} else {
274+
prngService = p.getDefaultSecureRandomService();
275+
if (prngService != null) {
276+
prngAlgorithm = prngService.getAlgorithm();
277+
break;
278+
}
279+
}
280+
}
281+
// per javadoc, if none of the Providers support a RNG algorithm,
282+
// then an implementation-specific default is returned.
283+
if (prngService == null) {
284+
prngAlgorithm = "SHA1PRNG";
266285
this.secureRandomSpi = new sun.security.provider.SecureRandom();
267286
this.provider = Providers.getSunProvider();
268-
if (setSeed) {
269-
this.secureRandomSpi.engineSetSeed(seed);
270-
}
271287
} else {
272288
try {
273-
SecureRandom random = SecureRandom.getInstance(prng);
274-
this.secureRandomSpi = random.getSecureRandomSpi();
275-
this.provider = random.getProvider();
276-
if (setSeed) {
277-
this.secureRandomSpi.engineSetSeed(seed);
278-
}
289+
this.secureRandomSpi = (SecureRandomSpi)
290+
prngService.newInstance(null);
291+
this.provider = prngService.getProvider();
279292
} catch (NoSuchAlgorithmException nsae) {
280-
// never happens, because we made sure the algorithm exists
293+
// should not happen
281294
throw new RuntimeException(nsae);
282295
}
283296
}
297+
if (setSeed) {
298+
this.secureRandomSpi.engineSetSeed(seed);
299+
}
284300
// JDK 1.1 based implementations subclass SecureRandom instead of
285301
// SecureRandomSpi. They will also go through this code path because
286302
// they must call a SecureRandom constructor as it is their superclass.
287303
// If we are dealing with such an implementation, do not set the
288304
// algorithm value as it would be inaccurate.
289305
if (getClass() == SecureRandom.class) {
290-
this.algorithm = prng;
306+
this.algorithm = prngAlgorithm;
291307
}
292308
}
293309

@@ -620,13 +636,6 @@ public static SecureRandom getInstance(String algorithm,
620636
instance.provider, algorithm);
621637
}
622638

623-
/**
624-
* Returns the {@code SecureRandomSpi} of this {@code SecureRandom} object.
625-
*/
626-
SecureRandomSpi getSecureRandomSpi() {
627-
return secureRandomSpi;
628-
}
629-
630639
/**
631640
* Returns the provider of this {@code SecureRandom} object.
632641
*
@@ -868,30 +877,6 @@ private static byte[] longToByteArray(long l) {
868877
return retVal;
869878
}
870879

871-
/**
872-
* Gets a default PRNG algorithm by looking through all registered
873-
* providers. Returns the first PRNG algorithm of the first provider that
874-
* has registered a {@code SecureRandom} implementation, or null if none of
875-
* the registered providers supplies a {@code SecureRandom} implementation.
876-
*/
877-
private static String getPrngAlgorithm() {
878-
for (Provider p : Providers.getProviderList().providers()) {
879-
// For SUN provider, we use SunEntries.DEFF_SECURE_RANDOM_ALGO
880-
// as the default SecureRandom algorithm; for other providers,
881-
// we continue to iterate through to the 1st SecureRandom
882-
// service
883-
if (p.getName().equals("SUN")) {
884-
return SunEntries.DEF_SECURE_RANDOM_ALGO;
885-
}
886-
for (Service s : p.getServices()) {
887-
if (s.getType().equals("SecureRandom")) {
888-
return s.getAlgorithm();
889-
}
890-
}
891-
}
892-
return null;
893-
}
894-
895880
/*
896881
* Lazily initialize since Pattern.compile() is heavy.
897882
* Effective Java (2nd Edition), Item 71.
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -22,33 +22,96 @@
2222
*/
2323

2424
import static java.lang.System.out;
25+
import java.security.Provider;
26+
import java.security.Security;
2527
import java.security.SecureRandom;
28+
import java.security.Provider.Service;
29+
import java.util.Objects;
30+
import java.util.Arrays;
2631
import sun.security.provider.SunEntries;
2732

2833
/**
2934
* @test
30-
* @bug 8228613
31-
* @summary Ensure that the default SecureRandom algo matches
32-
* SunEntries.DEF_SECURE_RANDOM_ALGO when SUN provider is used
35+
* @bug 8228613 8246613
36+
* @summary Ensure that the default SecureRandom algo used is based
37+
* on the registration ordering, and falls to next provider
38+
* if none are found
3339
* @modules java.base/sun.security.provider
3440
*/
3541
public class DefaultAlgo {
3642

3743
public static void main(String[] args) throws Exception {
38-
SecureRandom sr = new SecureRandom();
39-
String actualAlg = sr.getAlgorithm();
40-
out.println("Default SecureRandom algo: " + actualAlg);
41-
if (sr.getProvider().getName().equals("SUN")) {
42-
// when using Sun provider, compare and check if the algorithm
43-
// matches SunEntries.DEF_SECURE_RANDOM_ALGO
44-
if (actualAlg.equals(SunEntries.DEF_SECURE_RANDOM_ALGO)) {
45-
out.println("Test Passed");
46-
} else {
47-
throw new RuntimeException("Failed: Expected " +
44+
String[] algos = { "A", "B", "C" };
45+
test3rdParty(algos);
46+
// reverse the order and re-check
47+
String[] algosReversed = { "C", "B", "A" };
48+
test3rdParty(algosReversed);
49+
}
50+
51+
private static void test3rdParty(String[] algos) {
52+
Provider[] provs = {
53+
new SampleLegacyProvider(algos),
54+
new SampleServiceProvider(algos)
55+
};
56+
for (Provider p : provs) {
57+
checkDefault(p, algos);
58+
}
59+
}
60+
61+
// validate the specified SecureRandom obj to be from the specified
62+
// provider and matches the specified algorithm
63+
private static void validate(SecureRandom sr, String pName, String algo) {
64+
if (!sr.getProvider().getName().equals(pName)) {
65+
throw new RuntimeException("Failed provider check, exp: " +
66+
pName + ", got " + sr.getProvider().getName());
67+
}
68+
if (!sr.getAlgorithm().equals(algo)) {
69+
throw new RuntimeException("Failed algo check, exp: " +
70+
algo + ", got " + sr.getAlgorithm());
71+
}
72+
}
73+
74+
private static void checkDefault(Provider p, String ... algos) {
75+
out.println(p.getName() + " with " + Arrays.toString(algos));
76+
int pos = Security.insertProviderAt(p, 1);
77+
String pName = p.getName();
78+
boolean isLegacy = pName.equals("SampleLegacy");
79+
try {
80+
if (isLegacy) {
81+
for (String s : algos) {
82+
validate(new SecureRandom(), pName, s);
83+
p.remove("SecureRandom." + s);
84+
out.println("removed " + s);
85+
}
86+
validate(new SecureRandom(), "SUN",
4887
SunEntries.DEF_SECURE_RANDOM_ALGO);
88+
} else {
89+
validate(new SecureRandom(), pName, algos[0]);
90+
}
91+
out.println("=> Test Passed");
92+
} finally {
93+
if (pos != -1) {
94+
Security.removeProvider(p.getName());
95+
}
96+
}
97+
}
98+
99+
private static class SampleLegacyProvider extends Provider {
100+
SampleLegacyProvider(String[] listOfSupportedRNGs) {
101+
super("SampleLegacy", "1.0", "test provider using legacy put");
102+
for (String s : listOfSupportedRNGs) {
103+
put("SecureRandom." + s, "sun.security.provider.SecureRandom");
104+
}
105+
}
106+
}
107+
108+
private static class SampleServiceProvider extends Provider {
109+
SampleServiceProvider(String[] listOfSupportedRNGs) {
110+
super("SampleService", "1.0", "test provider using putService");
111+
for (String s : listOfSupportedRNGs) {
112+
putService(new Provider.Service(this, "SecureRandom", s,
113+
"sun.security.provider.SecureRandom", null, null));
49114
}
50-
} else {
51-
out.println("Skip test for non-Sun provider: " + sr.getProvider());
52115
}
53116
}
54117
}

0 commit comments

Comments
 (0)
Please sign in to comment.