28
28
package org .jruby .ext .openssl ;
29
29
30
30
import java .io .PrintStream ;
31
- import java .util .ArrayList ;
31
+ import java .util .Collection ;
32
32
import java .util .HashSet ;
33
- import java .util .List ;
33
+ import java .util .LinkedHashSet ;
34
34
import java .util .Set ;
35
35
36
36
import java .security .GeneralSecurityException ;
42
42
import javax .crypto .spec .RC2ParameterSpec ;
43
43
44
44
import org .jruby .Ruby ;
45
+ import org .jruby .RubyArray ;
45
46
import org .jruby .RubyClass ;
46
47
import org .jruby .RubyModule ;
47
48
import org .jruby .RubyNumeric ;
48
49
import org .jruby .RubyObject ;
49
- import org .jruby .common .IRubyWarnings ;
50
50
import org .jruby .common .IRubyWarnings .ID ;
51
51
import org .jruby .anno .JRubyMethod ;
52
52
import org .jruby .exceptions .RaiseException ;
@@ -79,30 +79,30 @@ public static void createCipher(final Ruby runtime, final RubyModule mOSSL) {
79
79
}
80
80
81
81
public static boolean isSupportedCipher (final String name ) {
82
- initializeCiphers ();
83
- return CIPHERS .contains ( name .toUpperCase () );
82
+ final Collection < String > ciphers = getSupportedCiphers ();
83
+ return ciphers .contains ( name .toUpperCase () );
84
84
}
85
85
86
86
@ JRubyMethod (meta = true )
87
87
public static IRubyObject ciphers (final ThreadContext context , final IRubyObject self ) {
88
- initializeCiphers ();
89
88
final Ruby runtime = context .runtime ;
90
89
91
- ArrayList <IRubyObject > result = new ArrayList <IRubyObject >(CIPHERS .size () * 2 );
92
- for (String cipher : CIPHERS ) {
93
- result .add ( runtime .newString (cipher ) );
94
- result .add ( runtime .newString (cipher .toLowerCase ()) );
90
+ final Collection <String > ciphers = getSupportedCiphers ();
91
+ final RubyArray result = runtime .newArray (ciphers .size () * 2 );
92
+ for ( final String cipher : ciphers ) {
93
+ result .append ( runtime .newString (cipher ) );
94
+ result .append ( runtime .newString (cipher .toLowerCase ()) );
95
95
}
96
- return runtime . newArray ( result ) ;
96
+ return result ;
97
97
}
98
98
99
- private static boolean initialized = false ;
100
- static final List <String > CIPHERS = new ArrayList <String >();
99
+ private static boolean supportedCiphersInitialized = false ;
100
+ static final Collection <String > supportedCiphers = new LinkedHashSet <String >(72 );
101
101
102
- private static void initializeCiphers () {
103
- if ( initialized ) return ;
104
- synchronized (CIPHERS ) {
105
- if ( initialized ) return ;
102
+ private static Collection < String > getSupportedCiphers () {
103
+ if ( supportedCiphersInitialized ) return supportedCiphers ;
104
+ synchronized ( supportedCiphers ) {
105
+ if ( supportedCiphersInitialized ) return supportedCiphers ;
106
106
final String [] other = {
107
107
"AES128" , "AES192" , "AES256" ,
108
108
"BLOWFISH" ,
@@ -122,17 +122,18 @@ private static void initializeCiphers() {
122
122
for ( int k = 0 ; k < suffixes .length ; k ++ ) {
123
123
final String cipher ;
124
124
if ( tryCipher ( cipher = bases [i ] + suffixes [k ]) ) {
125
- CIPHERS .add ( cipher .toUpperCase () );
125
+ supportedCiphers .add ( cipher .toUpperCase () );
126
126
}
127
127
}
128
128
}
129
129
for ( int i = 0 ; i < other .length ; i ++ ) {
130
130
final String cipher = other [i ];
131
131
if ( tryCipher ( cipher ) ) {
132
- CIPHERS .add ( cipher .toUpperCase () );
132
+ supportedCiphers .add ( cipher .toUpperCase () );
133
133
}
134
134
}
135
- initialized = true ;
135
+ supportedCiphersInitialized = true ;
136
+ return supportedCiphers ;
136
137
}
137
138
}
138
139
@@ -141,8 +142,7 @@ public static class Algorithm {
141
142
private static final Set <String > BLOCK_MODES ;
142
143
143
144
static {
144
- BLOCK_MODES = new HashSet <String >();
145
-
145
+ BLOCK_MODES = new HashSet <String >(8 );
146
146
BLOCK_MODES .add ("CBC" );
147
147
BLOCK_MODES .add ("CFB" );
148
148
BLOCK_MODES .add ("CFB1" );
@@ -151,53 +151,52 @@ public static class Algorithm {
151
151
BLOCK_MODES .add ("OFB" );
152
152
}
153
153
154
- public static String jsseToOssl (String inName , int keyLen ) {
154
+ public static String jsseToOssl (final String cipherName , final int keyLen ) {
155
155
String cryptoBase ;
156
156
String cryptoVersion = null ;
157
157
String cryptoMode = null ;
158
- String [] parts = inName .split ("/" );
159
- if (parts .length != 1 && parts .length != 3 ) {
158
+ final String [] parts = cipherName .split ("/" );
159
+ if ( parts .length != 1 && parts .length != 3 ) {
160
160
return null ;
161
161
}
162
- cryptoBase = parts [0 ];
163
- if (parts .length > 2 ) {
162
+ if ( parts .length > 2 ) {
164
163
cryptoMode = parts [1 ];
165
164
// padding: parts[2] is not used
166
165
}
167
- if (!BLOCK_MODES .contains (cryptoMode )) {
166
+ cryptoBase = parts [0 ];
167
+ if ( ! BLOCK_MODES .contains (cryptoMode ) ) {
168
168
cryptoVersion = cryptoMode ;
169
169
cryptoMode = "CBC" ;
170
170
}
171
171
if (cryptoMode == null ) {
172
172
cryptoMode = "CBC" ;
173
173
}
174
- if (cryptoBase . equals ( "DESede" ) ) {
174
+ if ( "DESede" . equals ( cryptoBase ) ) {
175
175
cryptoBase = "DES" ;
176
176
cryptoVersion = "EDE3" ;
177
- } else if (cryptoBase .equals ("Blowfish" )) {
177
+ }
178
+ else if ( "Blowfish" .equals (cryptoBase ) ) {
178
179
cryptoBase = "BF" ;
179
180
}
180
181
if (cryptoVersion == null ) {
181
182
cryptoVersion = String .valueOf (keyLen );
182
183
}
183
- return cryptoBase + "-" + cryptoVersion + "-" + cryptoMode ;
184
+ return cryptoBase + '-' + cryptoVersion + '-' + cryptoMode ;
184
185
}
185
186
186
187
public static String getAlgorithmBase (javax .crypto .Cipher cipher ) {
187
- String algoBase = cipher .getAlgorithm ();
188
- if (algoBase .indexOf ('/' ) != -1 ) {
189
- algoBase = algoBase .split ("/" )[0 ];
190
- }
191
- return algoBase ;
188
+ final String algorithm = cipher .getAlgorithm ();
189
+ final int idx = algorithm .indexOf ('/' );
190
+ if ( idx != -1 ) return algorithm .substring (0 , idx );
191
+ return algorithm ;
192
192
}
193
193
194
- public static String [] osslToJsse (String inName ) {
195
- // assume PKCS5Padding
196
- return osslToJsse (inName , null );
194
+ public static String [] osslToJsse (final String osslName ) {
195
+ return osslToJsse (osslName , null ); // assume PKCS5Padding
197
196
}
198
197
199
- public static String [] osslToJsse (String inName , String padding ) {
200
- String [] split = inName .split ("-" );
198
+ public static String [] osslToJsse (final String osslName , final String padding ) {
199
+ final String [] split = osslName .split ("-" );
201
200
String cryptoBase = split [0 ];
202
201
String cryptoVersion = null ;
203
202
String cryptoMode ;
@@ -214,7 +213,7 @@ public static String[] osslToJsse(String inName, String padding) {
214
213
paddingType = "PKCS5Padding" ;
215
214
}
216
215
217
- if ("bf " .equalsIgnoreCase (cryptoBase )) {
216
+ if ( "BF " .equalsIgnoreCase (cryptoBase ) ) {
218
217
cryptoBase = "Blowfish" ;
219
218
}
220
219
@@ -227,73 +226,71 @@ public static String[] osslToJsse(String inName, String padding) {
227
226
cryptoMode = "CBC" ;
228
227
}
229
228
230
- if (cryptoBase . equalsIgnoreCase ( "CAST" ) ) {
229
+ if ( "CAST" . equalsIgnoreCase ( cryptoBase ) ) {
231
230
realName = "CAST5" ;
232
- } else if (cryptoBase . equalsIgnoreCase ( "DES" ) && "EDE3" .equalsIgnoreCase (cryptoVersion )) {
231
+ } else if ( "DES" . equalsIgnoreCase ( cryptoBase ) && "EDE3" .equalsIgnoreCase (cryptoVersion ) ) {
233
232
realName = "DESede" ;
234
233
} else {
235
234
realName = cryptoBase ;
236
235
}
237
236
238
- if (!BLOCK_MODES .contains (cryptoMode .toUpperCase ())) {
239
- cryptoVersion = cryptoMode ;
240
- cryptoMode = "CBC" ;
241
- } else if (cryptoMode .equalsIgnoreCase ("CFB1" )) {
242
- // uglish SunJCE cryptoMode normalization.
243
- cryptoMode = "CFB" ;
237
+ final String cryptoModeUpper = cryptoMode .toUpperCase ();
238
+ if ( ! BLOCK_MODES .contains (cryptoModeUpper ) ) {
239
+ cryptoVersion = cryptoMode ; cryptoMode = "CBC" ;
240
+ } else if ( "CFB1" .equals (cryptoModeUpper ) ) {
241
+ cryptoMode = "CFB" ; // uglish SunJCE cryptoMode normalization
244
242
}
245
243
246
- if (realName .equalsIgnoreCase ("RC4" )) {
247
- realName = "RC4" ;
248
- cryptoMode = "NONE" ;
249
- paddingType = "NoPadding" ;
244
+ if ( "RC4" .equalsIgnoreCase (realName ) ) {
245
+ realName = "RC4" ; cryptoMode = "NONE" ; paddingType = "NoPadding" ;
250
246
} else {
251
247
realName = realName + "/" + cryptoMode + "/" + paddingType ;
252
248
}
253
249
254
- return new String []{cryptoBase , cryptoVersion , cryptoMode , realName , paddingType };
250
+ return new String []{ cryptoBase , cryptoVersion , cryptoMode , realName , paddingType };
255
251
}
256
252
257
- public static int [] osslKeyIvLength (String name ) {
258
- String [] values = Algorithm .osslToJsse (name );
259
- String cryptoBase = values [0 ];
260
- String cryptoVersion = values [1 ];
261
- //String cryptoMode = values [2];
262
- //String realName = values [3];
253
+ public static int [] osslKeyIvLength (final String cipherName ) {
254
+ String [] name = Algorithm .osslToJsse (cipherName );
255
+ final String cryptoBaseUpper = name [0 ]. toUpperCase () ;
256
+ final String cryptoVersion = name [1 ];
257
+ //final String cryptoMode = name [2];
258
+ //final String realName = name [3];
263
259
264
- int keyLen = -1 ;
265
- int ivLen = -1 ;
260
+ int keyLen = -1 ; int ivLen = -1 ;
266
261
267
- if (hasLen (cryptoBase ) && null != cryptoVersion ) {
262
+ final boolean hasLen =
263
+ "AES" .equals (cryptoBaseUpper ) || "RC2" .equals (cryptoBaseUpper ) || "RC4" .equals (cryptoBaseUpper );
264
+ if ( hasLen && cryptoVersion != null ) {
268
265
try {
269
266
keyLen = Integer .parseInt (cryptoVersion ) / 8 ;
270
267
} catch (NumberFormatException e ) {
271
268
keyLen = -1 ;
272
269
}
273
270
}
274
- if (keyLen == -1 ) {
275
- if ("DES" .equalsIgnoreCase ( cryptoBase ) ) {
271
+ if ( keyLen == -1 ) {
272
+ if ( "DES" .equals ( cryptoBaseUpper ) ) {
276
273
ivLen = 8 ;
277
- if ("EDE3" .equalsIgnoreCase (cryptoVersion )) {
274
+ if ( "EDE3" .equalsIgnoreCase (cryptoVersion ) ) {
278
275
keyLen = 24 ;
279
276
} else {
280
277
keyLen = 8 ;
281
278
}
282
- } else if ("RC4" .equalsIgnoreCase ( cryptoBase ) ) {
279
+ } else if ( "RC4" .equals ( cryptoBaseUpper ) ) {
283
280
ivLen = 0 ;
284
281
keyLen = 16 ;
285
282
} else {
286
283
keyLen = 16 ;
287
284
try {
288
- int maxLen = javax .crypto .Cipher .getMaxAllowedKeyLength (name ) / 8 ;
285
+ int maxLen = javax .crypto .Cipher .getMaxAllowedKeyLength (cipherName ) / 8 ;
289
286
if (maxLen < keyLen ) keyLen = maxLen ;
290
287
}
291
288
catch (NoSuchAlgorithmException e ) { }
292
289
}
293
290
}
294
291
295
- if (ivLen == -1 ) {
296
- if ("AES" .equalsIgnoreCase ( cryptoBase ) ) {
292
+ if ( ivLen == -1 ) {
293
+ if ( "AES" .equals ( cryptoBaseUpper ) ) {
297
294
ivLen = 16 ;
298
295
} else {
299
296
ivLen = 8 ;
@@ -302,15 +299,12 @@ public static int[] osslKeyIvLength(String name) {
302
299
return new int [] { keyLen , ivLen };
303
300
}
304
301
305
- public static boolean hasLen (String cryptoBase ) {
306
- return "AES" .equalsIgnoreCase (cryptoBase ) || "RC2" .equalsIgnoreCase (cryptoBase ) || "RC4" .equalsIgnoreCase (cryptoBase );
307
- }
308
302
}
309
303
310
- private static boolean tryCipher (final String rubyName ) {
311
- String transformation = Algorithm .osslToJsse (rubyName , null )[3 ];
304
+ private static boolean tryCipher (final String osslName ) {
305
+ String realName = Algorithm .osslToJsse (osslName , null )[3 ];
312
306
try {
313
- return getCipher (transformation , true ) != null ;
307
+ return getCipher (realName , true ) != null ;
314
308
}
315
309
catch (GeneralSecurityException e ) {
316
310
return false ;
@@ -770,7 +764,11 @@ public IRubyObject _final(final ThreadContext context) {
770
764
doInitialize (runtime );
771
765
}
772
766
}
773
- catch (Exception e ) {
767
+ catch (GeneralSecurityException e ) { // cipher.doFinal
768
+ throw newCipherError (runtime , e .getMessage ());
769
+ }
770
+ catch (RuntimeException e ) {
771
+ if ( isDebug (runtime ) ) e .printStackTrace ( runtime .getOut () );
774
772
throw newCipherError (runtime , e .getMessage ());
775
773
}
776
774
return runtime .newString (str );
0 commit comments