Skip to content

Commit e2abd0a

Browse files
committed
Changes to support min key length for encryption via the new Encrpytor.MinEncryptionKeyLength property.
1 parent d6fa7af commit e2abd0a

File tree

8 files changed

+155
-100
lines changed

8 files changed

+155
-100
lines changed

configuration/esapi/ESAPI.properties

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,28 @@ Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
199199
# DISCUSS: Better name?
200200
Encryptor.cipher_modes.additional_allowed=CBC
201201

202-
# 128-bit is almost always sufficient, and for AES, it appears to be more
203-
# to be a bit more resistant to related key attacks than is 256-bit AES.
202+
# Default key size to use for cipher specified by Encryptor.EncryptionAlgorithm.
203+
# Note that this MUST be a valid key size for the algorithm being used
204+
# (as specified by Encryptor.EncryptionAlgorithm). So for example, if AES is used,
205+
# it must be 128, 192, or 256. If DESede is chosen, then it must be either 112 or 168.
204206
#
205-
# Note: Key length must agree to what's provided as the
206-
# cipher transformation, otherwise this will be ignored after logging a
207-
# warning.
207+
# Note that 128-bits is almost always sufficient and for AES it appears to be more
208+
# somewhat more resistant to related key attacks than is 256-bit AES.)
208209
#
209-
# Defaults to 128 bits if left blank.
210+
# Defaults to 128-bits if left blank.
211+
#
212+
# NOTE: If you use a key size > 128-bits, then you MUST have the JCE Unlimited
213+
# Strength Jurisdiction Policy files installed!!!
210214
#
211215
Encryptor.EncryptionKeyLength=128
212216

217+
# This is the _minimum_ key size (in bits) that we allow with ANY symmetric
218+
# cipher for doing encryption. (There is no minimum for decryption.)
219+
#
220+
# Generally, if you only use one algorithm, this should be set the same as
221+
# the Encryptor.EncryptionKeyLength property.
222+
Encryptor.MinEncryptionKeyLength=128
223+
213224
# Because 2.x uses CBC mode by default, it requires an initialization vector (IV).
214225
# (All cipher modes except ECB require an IV.) There are two choices: we can either
215226
# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
@@ -267,12 +278,14 @@ Encryptor.PlainText.overwrite=true
267278

268279
# Do not use DES except in a legacy situations. 56-bit is way too small key size.
269280
#Encryptor.EncryptionKeyLength=56
281+
#Encryptor.MinEncryptionKeyLength=56
270282
#Encryptor.EncryptionAlgorithm=DES
271283

272284
# TripleDES is considered strong enough for most purposes.
273285
# Note: There is also a 112-bit version of DESede. Using the 168-bit version
274286
# requires downloading the special jurisdiction policy from Sun.
275287
#Encryptor.EncryptionKeyLength=168
288+
#Encryptor.MinEncryptionKeyLength=112
276289
#Encryptor.EncryptionAlgorithm=DESede
277290

278291
Encryptor.HashAlgorithm=SHA-512

documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<HTML>
33
<HEAD>
44
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=utf-8">
5-
<TITLE>ESAPI 2.0 Symmetric Encryption User Guide</TITLE>
5+
<TITLE>ESAPI 2.x Symmetric Encryption User Guide</TITLE>
66
<META NAME="GENERATOR" CONTENT="OpenOffice.org 3.0 (Linux)">
77
<META NAME="CREATED" CONTENT="20100214;0">
88
<META NAME="CHANGEDBY" CONTENT="Kevin W. Wall">
@@ -12,17 +12,9 @@
1212
<TABLE BORDER="0" BORDERCOLOR="#000000" CELLPADDING=4 CELLSPACING=0 STYLE="page-break-before: auto; page-break-after: auto; page-break-inside: auto">
1313
<TR>
1414
<TD>
15-
<OBJECT TYPE="audio/x-mpeg" data="http://www.catonmat.net/download/crypt-o.mp3"
16-
WIDTH="500" HEIGHT="64" AUTOPLAY="false">
17-
<PARAM NAME="src" VALUE="http://www.catonmat.net/download/crypt-o.mp3" />
18-
<PARAM NAME="controller" VALUE="true" />
19-
<PARAM NAME="autoplay" VALUE="false" />
20-
<PARAM NAME="autostart" VALUE="0" />
21-
</OBJECT>
22-
</TD>
23-
<TD>
2415
<FONT COLOR="#00a444" SIZE="+2">
25-
<I>Crypto song. Take a listen and enjoy! Harry Belafonte never sounded this good. ;-)</I>
16+
<A HREF="http://www.catonmat.net/download/crypt-o.mp3" TARGET="_blank"i
17+
REL="noopener noreferrer nofollow">Crypto song</A>: <I>Take a listen and enjoy! Harry Belafonte never sounded this good. ;-)</I>
2618
</FONT>
2719
</TD>
2820
</TABLE>
@@ -121,10 +113,29 @@ <H2>ESAPI.properties Properties Relevant to Symmetric Encryption</H2>
121113
<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>128</FONT></FONT></PRE>
122114
</TD>
123115
<TD WIDTH=226>
124-
<P><FONT SIZE=2>Key size, in bits. Required for cipher algorithms
116+
<P><FONT SIZE=2>Default key size, in bits. Required for cipher algorithms
125117
that support multiple key sizes.</FONT></P>
126118
</TD>
127119
</TR>
120+
<TR VALIGN=TOP>
121+
<TD WIDTH=249>
122+
<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.MinEncryptionKeyLength</FONT></FONT></PRE>
123+
</TD>
124+
<TD WIDTH=202>
125+
<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>128</FONT></FONT></PRE>
126+
</TD>
127+
<TD WIDTH=226>
128+
<P><FONT SIZE=2>Minimum key size, in bits, that ESAPI will support
129+
for <I>encryption</I>. (Note that any legitimate size is
130+
accepted for <I>decryption</I>.) So, for example, if you needed
131+
to be able to do encryption for 2-key Triple DES (aka, 2TDEA),
132+
then you would have to change this to '112'. Note that for a
133+
minimum key size of <U>larger</U> than 128-bits, you will need
134+
to have the JCE Unlimited Strength Jurisdiction Policy files
135+
installed on your runtime system.
136+
</FONT></P>
137+
</TD>
138+
</TR>
128139
<TR VALIGN=TOP>
129140
<TD WIDTH=249>
130141
<PRE><FONT COLOR="#ff0000"><FONT SIZE=2>Encryptor.ChooseIVMethod</FONT></FONT></PRE>
@@ -138,6 +149,8 @@ <H2>ESAPI.properties Properties Relevant to Symmetric Encryption</H2>
138149
compatibility with legacy or third party software. If set to
139150
“fixed”, then the property Encryptor.fixedIV must also be
140151
set to hex-encoded specific IV that you need to use.
152+
<B>NOTE:</B> "fixed" is deprecated and will be removed by
153+
release 2.3.
141154
</FONT></P><P><FONT SIZE=2>
142155
<B>CAUTION:</B> While it is not required that the IV be kept
143156
secret, encryption relying on fixed IVs can lead to a known
@@ -788,7 +801,7 @@ <H2>Acknowledgments</H2>
788801
KDF more in line with NIST's recommendations for KDFs as described in
789802
NIST Special Publication 800-108 (and specifically section 5.1). You can
790803
read about Jeff's review at
791-
<a href="http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/Analysis-of-ESAPI-2.0-KDF.pdf">
804+
<a href="https://github.com/ESAPI/esapi-java-legacy/blob/master/documentation/Analysis-of-ESAPI-2.0-KDF.pdf">
792805
Analysis of ESAPI 2.0's Key Derivation Function
793806
</a>
794807
</p>

src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java

Lines changed: 69 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,7 @@ public static Encryptor getInstance() throws EncryptionException {
133133
// decryption fails. This is to prevent information leakage that may be
134134
// valuable in various forms of ciphertext attacks, such as the
135135
// Padded Oracle attack described by Rizzo and Duong.
136-
private static final String DECRYPTION_FAILED =
137-
"Decryption failed; see logs for details.";
136+
private static final String DECRYPTION_FAILED = "Decryption failed; see logs for details.";
138137

139138
// # of seconds that all failed decryption attempts will take. Used to
140139
// help prevent side-channel timing attacks.
@@ -236,21 +235,34 @@ private JavaEncryptor() throws EncryptionException {
236235
byte[] salt = ESAPI.securityConfiguration().getMasterSalt();
237236
byte[] skey = ESAPI.securityConfiguration().getMasterKey();
238237

239-
assert salt != null : "Can't obtain master salt, Encryptor.MasterSalt";
240-
assert salt.length >= 16 : "Encryptor.MasterSalt must be at least 16 bytes. " +
241-
"Length is: " + salt.length + " bytes.";
242-
assert skey != null : "Can't obtain master key, Encryptor.MasterKey";
243-
assert skey.length >= 7 : "Encryptor.MasterKey must be at least 7 bytes. " +
244-
"Length is: " + skey.length + " bytes.";
238+
if ( salt == null ) {
239+
throw new ConfigurationException("Can't obtain master salt, Encryptor.MasterSalt");
240+
}
241+
242+
if ( salt.length < 16 ) {
243+
throw new ConfigurationException("Encryptor.MasterSalt must be at least 16 bytes. " +
244+
"Length is: " + salt.length + " bytes.");
245+
}
246+
247+
if ( skey == null ) {
248+
throw new ConfigurationException("Can't obtain master key, Encryptor.MasterKey");
249+
}
250+
251+
if ( skey.length < 7 ) {
252+
throw new ConfigurationException("Encryptor.MasterKey must be at least 7 bytes. " +
253+
"Length is: " + skey.length + " bytes.");
254+
}
245255

246256
// Set up secretKeySpec for use for symmetric encryption and decryption,
247257
// and set up the public/private keys for asymmetric encryption /
248258
// decryption.
249-
// TODO: Note: If we dump ESAPI 1.4 crypto backward compatibility,
250-
// then we probably will ditch the Encryptor.EncryptionAlgorithm
259+
// TODO: Note: Since we've dumped ESAPI 1.4 crypto backward compatibility,
260+
// then we probably can ditch the Encryptor.EncryptionAlgorithm
251261
// property. If so, encryptAlgorithm should probably use
252262
// Encryptor.CipherTransformation and just pull off the cipher
253-
// algorithm name so we can use it here.
263+
// algorithm name so we can use it here. That just requires
264+
// advance notice and proper deprecation, which I'm not prepared
265+
// to do at this time. -kevin wall
254266
synchronized(JavaEncryptor.class) {
255267
if ( ! initialized ) {
256268
//
@@ -360,7 +372,10 @@ public CipherText encrypt(SecretKey key, PlainText plain)
360372
try {
361373
xform = ESAPI.securityConfiguration().getCipherTransformation();
362374
String[] parts = xform.split("/");
363-
assert parts.length == 3 : "Malformed cipher transformation: " + xform;
375+
if ( parts.length != 3 ) {
376+
throw new ConfigurationException("Malformed cipher transformation: " + xform +
377+
". Should have format of cipher_alg/cipher_mode/padding_scheme.");
378+
}
364379
String cipherMode = parts[1];
365380

366381
// This way we can prevent modes like OFB and CFB where the IV should never
@@ -383,63 +398,33 @@ public CipherText encrypt(SecretKey key, PlainText plain)
383398
// and this method will just call that one.
384399
Cipher encrypter = Cipher.getInstance(xform);
385400
String cipherAlg = encrypter.getAlgorithm();
386-
int keyLen = ESAPI.securityConfiguration().getEncryptionKeyLength();
387401

388-
// DISCUSS: OK, what do we want to do here if keyLen != keySize? If use keyLen, encryption
389-
// could fail with an exception, but perhaps that's what we want. Or we may just be
390-
// OK with silently using keySize as long as keySize >= keyLen, which then interprets
391-
// ESAPI.EncryptionKeyLength as the *minimum* key size, but as long as we have something
392-
// stronger it's OK to use it. For now, I am just going to log warning if different, but use
393-
// keySize unless keySize is SMALLER than ESAPI.EncryptionKeyLength, in which case I'm going
394-
// to log an error.
395-
//
396-
// IMPORTANT NOTE: When we generate key sizes for both DES and DESede the result of
397-
// SecretKey.getEncoding().length includes the TRUE key size (i.e.,
398-
// *with* the even parity bits) rather than the EFFECTIVE key size
399-
// (which incidentally is what KeyGenerator.init() expects for DES
400-
// and DESede; duh! Nothing like being consistent). This leads to
401-
// the following dilemma:
402-
//
403-
// EFFECTIVE Key Size TRUE Key Size
404-
// (KeyGenerator.init()) (SecretKey.getEncoding().length)
405-
// ========================================================================
406-
// For DES: 56 bits 64 bits
407-
// For DESede: 112 bits / 168 bits 192 bits (always)
408-
//
409-
// We are trying to automatically determine the key size from SecretKey
410-
// based on 8 * SecretKey.getEncoding().length, but as you can see, the
411-
// 2 key 3DES and the 3 key 3DES both use the same key size (192 bits)
412-
// regardless of what is passed to KeyGenerator.init(). There are no advertised
413-
// methods to get the key size specified by the init() method so I'm not sure how
414-
// this is actually working internally. However, it does present a problem if we
415-
// wish to communicate the 3DES key size to a recipient for later decryption as
416-
// they would not be able to distinguish 2 key 3DES from 3 key 3DES.
417-
//
418-
// The only workaround I know is to pass the explicit key size down. However, if
419-
// we are going to do that, I'd propose passing in a CipherSpec object so we could
420-
// tell what cipher transformation to use as well instead of just the key size. Then
421-
// we would extract keySize from the CipherSpec object of from the SecretKey object.
422-
//
423-
if ( keySize != keyLen ) {
424-
// DISCUSS: Technically this is not a security "failure" per se, but not really a "success" either.
425-
logger.warning(Logger.SECURITY_FAILURE, "Encryption key length mismatch. ESAPI.EncryptionKeyLength is " +
426-
keyLen + " bits, but length of actual encryption key is " + keySize +
427-
" bits. Did you remember to regenerate your master key (if that is what you are using)???");
428-
}
429-
// DISCUSS: Reconsider these warnings. If thousands of encryptions are done in tight loop, no one needs
430-
// more than 1 warning. Should we do something more intelligent here?
431-
if ( keySize < keyLen ) {
432-
// ESAPI.EncryptionKeyLength defaults to 128, but that means that we could not use DES (as weak as it
433-
// is), even for legacy code. Therefore, this has been changed to simple log a warning rather than
434-
// throw the following exception.
435-
// throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " +
436-
// "encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits.");
437-
logger.warning(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN specified " +
438-
"encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits with cipher algorithm " + cipherAlg);
402+
int minKeyLen = 112; // Use for hard-coded default to support 2TDEA
403+
try {
404+
minKeyLen = ESAPI.securityConfiguration().getIntProp("Encryptor.MinEncryptionKeyLength");
405+
} catch( Exception ex ) {
406+
logger.warning(Logger.EVENT_FAILURE,
407+
"Property 'Encryptor.MinEncryptionKeyLength' not properly set in ESAPI.properties file; using hard coded default of 112 for min key size for encryption.",
408+
ex);
409+
; // Do NOT rethrow.
410+
}
411+
412+
if ( keySize < minKeyLen ) {
413+
// NOTE: This used to just log a warning. It now logs an error & throws an exception.
414+
//
415+
// ESAPI.EncryptionKeyLength defaults to 128. This means that someone wants to use ESAPI to
416+
// encrypt with something like 2-key TDES, they would have to set this to that property
417+
// to 112 bits.
418+
logger.error(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN MINIMUM allowed " +
419+
"encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits with cipher algorithm " + cipherAlg);
420+
throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " +
421+
"encryption key length (ESAPI.EncryptionKeyLength) of " + minKeyLen + " bits.");
439422
}
440423
if ( keySize < 112 ) { // NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030.
441-
// Note that 112 bits 'just happens' to be size of 2-key Triple DES!
442-
logger.warning(Logger.SECURITY_FAILURE, "Potentially unsecure encryption. Key size of " + keySize + "bits " +
424+
// Note that 112 bits 'just happens' to be size of 2-key Triple DES! So for example, if they
425+
// have configured ESAPI's Encryptor.EncryptionKeyLength to (say) 56 bits, we are going to
426+
// nag them like their mother! :)
427+
logger.warning(Logger.SECURITY_FAILURE, "Potentially insecure encryption. Key size of " + keySize + "bits " +
443428
"not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " +
444429
"of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57.");
445430
}
@@ -548,9 +533,9 @@ public CipherText encrypt(SecretKey key, PlainText plain)
548533
// Don't overwrite anything in the case of exceptions because they may wish to retry.
549534
if ( success && overwritePlaintext ) {
550535
plain.overwrite(); // Note: Same as overwriting 'plaintext' byte array.
551-
}
552-
}
553-
}
536+
}
537+
}
538+
}
554539

555540
/**
556541
* {@inheritDoc}
@@ -655,6 +640,8 @@ public PlainText decrypt(SecretKey key, CipherText ciphertext)
655640
// convert to from nanoseconds to milliseconds.
656641
long millis = extraSleep / 1000000L;
657642
long nanos = (extraSleep - (millis * 1000000L));
643+
644+
// N_SECS is hard-coded so assertion should be okay here.
658645
assert nanos >= 0 && nanos <= Integer.MAX_VALUE :
659646
"Nanosecs out of bounds; nanos = " + nanos;
660647
try {
@@ -830,7 +817,8 @@ public String unseal(String seal) throws EncryptionException {
830817
cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes);
831818
} catch( AssertionError e) {
832819
// Some of the tests in EncryptorTest.testVerifySeal() are examples of
833-
// this if assertions are enabled.
820+
// this if assertions are enabled, but otherwise it should not
821+
// normally happen.
834822
throw new EncryptionException("Invalid seal",
835823
"Seal passed garbarge data resulting in AssertionError: " + e);
836824
}
@@ -993,11 +981,16 @@ private SecretKey computeDerivedKey(int kdfVersion, KeyDerivationFunction.PRF_AL
993981
// We would choose a larger minimum key size, but we want to be
994982
// able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that,
995983
// we print warning.
996-
assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
984+
assert keySize >= 56 : "Key has size of " + keySize + ", which is less than absolute minimum of 56-bits.";
997985
assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
998-
assert purpose != null : "Purpose cannot be null. Should be 'encryption' or 'authenticity'.";
999-
assert purpose.equals("encryption") || purpose.equals("authenticity") :
1000-
"Purpose must be \"encryption\" or \"authenticity\".";
986+
987+
// However, this one we want as a runtime check because we don't have this check
988+
// in KeyDerivationFunction.computeDerivedKey() as we want that method
989+
// to be more general.
990+
if ( !( purpose.equals("encryption") || purpose.equals("authenticity") ) ) {
991+
String exMsg = "Programming error in ESAPI?? 'purpose' for computeDerivedKey() must be \"encryption\" or \"authenticity\".";
992+
throw new EncryptionException(exMsg, exMsg);
993+
}
1001994

1002995
KeyDerivationFunction kdf = new KeyDerivationFunction(prf);
1003996
if ( kdfVersion != 0 ) {
@@ -1034,7 +1027,8 @@ private static void initKeyPair(SecureRandom prng) throws NoSuchAlgorithmExcepti
10341027
// in the case that SHA1withDSA or SHAwithDSA was specified. This is
10351028
// all just to make these 2 work as expected. Sigh. (Note:
10361029
// this was tested with JDK 1.6.0_21, but likely fails with earlier
1037-
// versions of the JDK as well.)
1030+
// versions of the JDK as well. Haven't experimented with later
1031+
// versions.)
10381032
//
10391033
sigAlg = "DSA";
10401034
} else if ( sigAlg.endsWith("withrsa") ) {

0 commit comments

Comments
 (0)