Skip to content

Commit 40d02e7

Browse files
authored
Merge pull request #4926 from luchua-bc/java/insufficient-key-size
Java: Query to detect weak encryption: insufficient key size
2 parents 0df7e9f + 5e3b6fa commit 40d02e7

File tree

7 files changed

+345
-1
lines changed

7 files changed

+345
-1
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
public class InsufficientKeySize {
2+
public void CryptoMethod() {
3+
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
4+
// BAD: Key size is less than 128
5+
keyGen1.init(64);
6+
7+
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
8+
// GOOD: Key size is no less than 128
9+
keyGen2.init(128);
10+
11+
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
12+
// BAD: Key size is less than 2048
13+
keyPairGen1.initialize(1024);
14+
15+
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
16+
// GOOD: Key size is no less than 2048
17+
keyPairGen2.initialize(2048);
18+
19+
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
20+
// BAD: Key size is less than 2048
21+
keyPairGen3.initialize(1024);
22+
23+
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
24+
// GOOD: Key size is no less than 2048
25+
keyPairGen4.initialize(2048);
26+
27+
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
28+
// BAD: Key size is less than 256
29+
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
30+
keyPairGen5.initialize(ecSpec1);
31+
32+
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
33+
// GOOD: Key size is no less than 256
34+
ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
35+
keyPairGen6.initialize(ecSpec2);
36+
}
37+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<p>This rule finds uses of encryption algorithms with too small a key size. Encryption algorithms
5+
are vulnerable to brute force attack when too small a key size is used.</p>
6+
</overview>
7+
8+
<recommendation>
9+
<p>The key should be at least 2048 bits long when using RSA and DSA encryption, 256 bits long when using EC encryption, and 128 bits long when using
10+
symmetric encryption.</p>
11+
</recommendation>
12+
13+
<references>
14+
15+
<li>
16+
Wikipedia.
17+
<a href="http://en.wikipedia.org/wiki/Key_size">Key size</a>
18+
</li>
19+
<li>
20+
SonarSource.
21+
<a href="https://rules.sonarsource.com/java/type/Vulnerability/RSPEC-4426">Cryptographic keys should be robust</a>
22+
</li>
23+
<li>
24+
CWE.
25+
<a href="https://cwe.mitre.org/data/definitions/326.html">CWE-326: Inadequate Encryption Strength</a>
26+
</li>
27+
28+
</references>
29+
</qhelp>
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/**
2+
* @name Weak encryption: Insufficient key size
3+
* @description Finds uses of encryption algorithms with too small a key size
4+
* @kind problem
5+
* @id java/insufficient-key-size
6+
* @tags security
7+
* external/cwe/cwe-326
8+
*/
9+
10+
import java
11+
import semmle.code.java.security.Encryption
12+
import semmle.code.java.dataflow.TaintTracking
13+
14+
/** The Java class `java.security.spec.ECGenParameterSpec`. */
15+
class ECGenParameterSpec extends RefType {
16+
ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
17+
}
18+
19+
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
20+
class KeyGeneratorInitMethod extends Method {
21+
KeyGeneratorInitMethod() {
22+
getDeclaringType() instanceof KeyGenerator and
23+
hasName("init")
24+
}
25+
}
26+
27+
/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
28+
class KeyPairGeneratorInitMethod extends Method {
29+
KeyPairGeneratorInitMethod() {
30+
getDeclaringType() instanceof KeyPairGenerator and
31+
hasName("initialize")
32+
}
33+
}
34+
35+
/** Returns the key size in the EC algorithm string */
36+
bindingset[algorithm]
37+
int getECKeySize(string algorithm) {
38+
algorithm.matches("sec%") and // specification such as "secp256r1"
39+
result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
40+
or
41+
algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
42+
result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
43+
or
44+
(algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
45+
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
46+
}
47+
48+
/** Taint configuration tracking flow from a key generator to a `init` method call. */
49+
class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
50+
KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
51+
52+
override predicate isSource(DataFlow::Node source) {
53+
exists(JavaxCryptoKeyGenerator jcg | jcg = source.asExpr())
54+
}
55+
56+
override predicate isSink(DataFlow::Node sink) {
57+
exists(MethodAccess ma |
58+
ma.getMethod() instanceof KeyGeneratorInitMethod and
59+
sink.asExpr() = ma.getQualifier()
60+
)
61+
}
62+
}
63+
64+
/** Taint configuration tracking flow from a keypair generator to a `initialize` method call. */
65+
class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
66+
KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
67+
68+
override predicate isSource(DataFlow::Node source) {
69+
exists(JavaSecurityKeyPairGenerator jkg | jkg = source.asExpr())
70+
}
71+
72+
override predicate isSink(DataFlow::Node sink) {
73+
exists(MethodAccess ma |
74+
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
75+
sink.asExpr() = ma.getQualifier()
76+
)
77+
}
78+
}
79+
80+
/** Holds if a symmetric `KeyGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
81+
bindingset[type]
82+
predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
83+
ma.getMethod() instanceof KeyGeneratorInitMethod and
84+
exists(
85+
JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
86+
DataFlow::PathNode dest
87+
|
88+
jcg.getAlgoSpec().(StringLiteral).getValue() = type and
89+
source.getNode().asExpr() = jcg and
90+
dest.getNode().asExpr() = ma.getQualifier() and
91+
cc.hasFlowPath(source, dest)
92+
) and
93+
ma.getArgument(0).(IntegerLiteral).getIntValue() < 128 and
94+
msg = "Key size should be at least 128 bits for " + type + " encryption."
95+
}
96+
97+
/** Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
98+
predicate hasShortAESKey(MethodAccess ma, string msg) { hasShortSymmetricKey(ma, msg, "AES") }
99+
100+
/** Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
101+
bindingset[type]
102+
predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
103+
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
104+
exists(
105+
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
106+
DataFlow::PathNode source, DataFlow::PathNode dest
107+
|
108+
jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
109+
source.getNode().asExpr() = jpg and
110+
dest.getNode().asExpr() = ma.getQualifier() and
111+
kc.hasFlowPath(source, dest)
112+
) and
113+
ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
114+
msg = "Key size should be at least 2048 bits for " + type + " encryption."
115+
}
116+
117+
/** Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
118+
predicate hasShortDSAKeyPair(MethodAccess ma, string msg) {
119+
hasShortAsymmetricKeyPair(ma, msg, "DSA") or hasShortAsymmetricKeyPair(ma, msg, "DH")
120+
}
121+
122+
/** Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
123+
predicate hasShortRSAKeyPair(MethodAccess ma, string msg) {
124+
hasShortAsymmetricKeyPair(ma, msg, "RSA")
125+
}
126+
127+
/** Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
128+
predicate hasShortECKeyPair(MethodAccess ma, string msg) {
129+
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
130+
exists(
131+
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
132+
DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
133+
|
134+
jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
135+
source.getNode().asExpr() = jpg and
136+
dest.getNode().asExpr() = ma.getQualifier() and
137+
kc.hasFlowPath(source, dest) and
138+
DataFlow::localExprFlow(cie, ma.getArgument(0)) and
139+
ma.getArgument(0).getType() instanceof ECGenParameterSpec and
140+
getECKeySize(cie.getArgument(0).(StringLiteral).getRepresentedString()) < 256
141+
) and
142+
msg = "Key size should be at least 256 bits for EC encryption."
143+
}
144+
145+
from Expr e, string msg
146+
where
147+
hasShortAESKey(e, msg) or
148+
hasShortDSAKeyPair(e, msg) or
149+
hasShortRSAKeyPair(e, msg) or
150+
hasShortECKeyPair(e, msg)
151+
select e, msg

java/ql/src/semmle/code/java/security/Encryption.qll

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ class HostnameVerifier extends RefType {
3838
HostnameVerifier() { hasQualifiedName("javax.net.ssl", "HostnameVerifier") }
3939
}
4040

41+
/** The Java class `javax.crypto.KeyGenerator`. */
42+
class KeyGenerator extends RefType {
43+
KeyGenerator() { this.hasQualifiedName("javax.crypto", "KeyGenerator") }
44+
}
45+
46+
/** The Java class `java.security.KeyPairGenerator`. */
47+
class KeyPairGenerator extends RefType {
48+
KeyPairGenerator() { this.hasQualifiedName("java.security", "KeyPairGenerator") }
49+
}
50+
4151
/** The `verify` method of the class `javax.net.ssl.HostnameVerifier`. */
4252
class HostnameVerifierVerify extends Method {
4353
HostnameVerifierVerify() {
@@ -248,7 +258,7 @@ class JavaxCryptoSecretKey extends JavaxCryptoAlgoSpec {
248258
class JavaxCryptoKeyGenerator extends JavaxCryptoAlgoSpec {
249259
JavaxCryptoKeyGenerator() {
250260
exists(Method m | m.getAReference() = this |
251-
m.getDeclaringType().getQualifiedName() = "javax.crypto.KeyGenerator" and
261+
m.getDeclaringType() instanceof KeyGenerator and
252262
m.getName() = "getInstance"
253263
)
254264
}
@@ -304,3 +314,15 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
304314

305315
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
306316
}
317+
318+
/** A method call to the Java class `java.security.KeyPairGenerator`. */
319+
class JavaSecurityKeyPairGenerator extends JavaxCryptoAlgoSpec {
320+
JavaSecurityKeyPairGenerator() {
321+
exists(Method m | m.getAReference() = this |
322+
m.getDeclaringType() instanceof KeyPairGenerator and
323+
m.getName() = "getInstance"
324+
)
325+
}
326+
327+
override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
328+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
| InsufficientKeySize.java:9:9:9:24 | init(...) | Key size should be at least 128 bits for AES encryption. |
2+
| InsufficientKeySize.java:17:9:17:36 | initialize(...) | Key size should be at least 2048 bits for RSA encryption. |
3+
| InsufficientKeySize.java:25:9:25:36 | initialize(...) | Key size should be at least 2048 bits for DSA encryption. |
4+
| InsufficientKeySize.java:34:9:34:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
5+
| InsufficientKeySize.java:38:9:38:67 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
6+
| InsufficientKeySize.java:48:9:48:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
7+
| InsufficientKeySize.java:53:9:53:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
8+
| InsufficientKeySize.java:58:9:58:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
9+
| InsufficientKeySize.java:68:9:68:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
10+
| InsufficientKeySize.java:78:9:78:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
11+
| InsufficientKeySize.java:87:9:87:37 | initialize(...) | Key size should be at least 2048 bits for DH encryption. |
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import java.security.KeyPairGenerator;
2+
import java.security.spec.ECGenParameterSpec;
3+
import javax.crypto.KeyGenerator;
4+
5+
public class InsufficientKeySize {
6+
public void CryptoMethod() {
7+
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
8+
// BAD: Key size is less than 128
9+
keyGen1.init(64);
10+
11+
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
12+
// GOOD: Key size is no less than 128
13+
keyGen2.init(128);
14+
15+
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
16+
// BAD: Key size is less than 2048
17+
keyPairGen1.initialize(1024);
18+
19+
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
20+
// GOOD: Key size is no less than 2048
21+
keyPairGen2.initialize(2048);
22+
23+
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
24+
// BAD: Key size is less than 2048
25+
keyPairGen3.initialize(1024);
26+
27+
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
28+
// GOOD: Key size is no less than 2048
29+
keyPairGen4.initialize(2048);
30+
31+
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
32+
// BAD: Key size is less than 256
33+
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
34+
keyPairGen5.initialize(ecSpec1);
35+
36+
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
37+
// BAD: Key size is less than 256
38+
keyPairGen6.initialize(new ECGenParameterSpec("secp112r1"));
39+
40+
KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
41+
// GOOD: Key size is no less than 256
42+
ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
43+
keyPairGen7.initialize(ecSpec2);
44+
45+
KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
46+
// BAD: Key size is less than 256
47+
ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2");
48+
keyPairGen8.initialize(ecSpec3);
49+
50+
KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
51+
// BAD: Key size is less than 256
52+
ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3");
53+
keyPairGen9.initialize(ecSpec4);
54+
55+
KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
56+
// BAD: Key size is less than 256
57+
ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1");
58+
keyPairGen10.initialize(ecSpec5);
59+
60+
KeyPairGenerator keyPairGen11 = KeyPairGenerator.getInstance("EC");
61+
// GOOD: Key size is no less than 256
62+
ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1");
63+
keyPairGen11.initialize(ecSpec6);
64+
65+
KeyPairGenerator keyPairGen12 = KeyPairGenerator.getInstance("EC");
66+
// BAD: Key size is less than 256
67+
ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2");
68+
keyPairGen12.initialize(ecSpec7);
69+
70+
KeyPairGenerator keyPairGen13 = KeyPairGenerator.getInstance("EC");
71+
// BAD: Key size is no less than 256
72+
ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1");
73+
keyPairGen13.initialize(ecSpec8);
74+
75+
KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
76+
// BAD: Key size is less than 256
77+
ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1");
78+
keyPairGen14.initialize(ecSpec9);
79+
80+
KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
81+
// BAD: Key size is no less than 256
82+
ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("c2tnb431r1");
83+
keyPairGen15.initialize(ecSpec10);
84+
85+
KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("dh");
86+
// BAD: Key size is less than 2048
87+
keyPairGen16.initialize(1024);
88+
89+
KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
90+
// GOOD: Key size is no less than 2048
91+
keyPairGen17.initialize(2048);
92+
}
93+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-326/InsufficientKeySize.ql

0 commit comments

Comments
 (0)