-
Notifications
You must be signed in to change notification settings - Fork 123
Description
Problem:
The AWS Encryption SDK fails when performing AwsCrypto#encryptData(KmsMasterKeyProvider, byte[], Map<String, String>) with an ExceptionInInitializerError when another JAR containing a project.properties file appears earlier in the classpath. This causes VersionInfo.versionNumber() to return null, leading to a NullPointerException in ApiName initialization.
Root Cause
The VersionInfo.versionNumber() method uses getResourceAsStream("project.properties"), making it vulnerable to classpath ordering issues when other JARs on the classpath contain the same resource. When another JAR containing project.properties and lacks a version property, and appears earlier in the classpath, it gets loaded instead of the SDK's own project.properties.
If another project.properties appears earlier in the class and does contain a version property, I assume the library would continue to load, but may run into extremely subtle problems if the version is not the expected value.
Environment
AWS Encryption SDK 2.4.0
Java 17
Spring Boot 2.6.x
Gradle
Stack trace
Exception in thread "grpc-default-executor-0" java.lang.ExceptionInInitializerError
at com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKeyProvider.getMasterKey(KmsMasterKeyProvider.java:335)
at com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKeyProvider.getMasterKey(KmsMasterKeyProvider.java:34)
at com.amazonaws.encryptionsdk.MasterKeyProvider.getMasterKey(MasterKeyProvider.java:51)
at com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKeyProvider.getMasterKeysForEncryption(KmsMasterKeyProvider.java:348)
at com.amazonaws.encryptionsdk.DefaultCryptoMaterialsManager.getMaterialsForEncrypt(DefaultCryptoMaterialsManager.java:88)
at com.amazonaws.encryptionsdk.AwsCrypto.encryptData(AwsCrypto.java:355)
at com.amazonaws.encryptionsdk.AwsCrypto.encryptData(AwsCrypto.java:335)
...
Caused by: java.lang.NullPointerException: version must not be null
at software.amazon.awssdk.utils.Validate.notNull(Validate.java:119)
at software.amazon.awssdk.core.ApiName.<init>(ApiName.java:34)
at software.amazon.awssdk.core.ApiName.<init>(ApiName.java:28)
at software.amazon.awssdk.core.ApiName$BuilderImpl.build(ApiName.java:97)
at com.amazonaws.encryptionsdk.kmssdkv2.KmsMasterKey.<clinit>(KmsMasterKey.java:47)
...
The relevant static initializer from the KmsMasterKey class is for the API_NAME field:
private static final ApiName API_NAME =
ApiName.builder().name(VersionInfo.apiName()).version(VersionInfo.versionNumber()).build();The relevant code from the VersionInfo class is:
/*
* String representation of the library version e.g. 2.3.3
*/
public static String versionNumber() {
try {
final Properties properties = new Properties();
final ClassLoader loader = VersionInfo.class.getClassLoader();
properties.load(loader.getResourceAsStream("project.properties"));
return properties.getProperty("version");
} catch (final IOException ex) {
return UNKNOWN_VERSION;
}
}Reproduction
Include the encryption-sdk and another jar containing project.properties on your classpath. Ensure the other jar is first in the classpath.
We happen to use https://github.com/googleads/google-ads-java/releases/tag/33.0.0 which (luckily) contains an empty project.properties file.
When the JVM loads the KmsMasterKey class, the class loader will find the incorrect project.properties file.
Solution:
- use a different class loader which prefers looking for the encryption-sdk first
- somehow iterate over all resources named
project.propertiesand ensure you've picked the correct one - use a more specific name for
project.properties
Out of scope:
Is there anything the solution will intentionally NOT address?