Facing the below error while trying to deploy an encryption java cloud function on Google cloud. [Note, function works locally].
Exception in thread "main" java.lang.NoClassDefFoundError: com/nimbusds/jose/JWEEncrypter at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3137) at java.base/java.lang.Class.getConstructor0(Class.java:3342) at java.base/java.lang.Class.getConstructor(Class.java:2151) at com.google.cloud.functions.invoker.HttpFunctionExecutor.forClass(HttpFunctionExecutor.java:53) at com.google.cloud.functions.invoker.runner.Invoker.startServer(Invoker.java:243) at com.google.cloud.functions.invoker.runner.Invoker.main(Invoker.java:121) Caused by: java.lang.ClassNotFoundException: com.nimbusds.jose.JWEEncrypter at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ... 7 more
Exception in thread "main" java.lang.NoClassDefFoundError: com/nimbusds/jose/JWEEncrypter at java.base/java.lang.Class.getDeclaredConstructors0(Native Method) at java.base/java.lang.Class.privateGetDeclaredConstructors(Class.java:3137) at java.base/java.lang.Class.getConstructor0(Class.java:3342) at java.base/java.lang.Class.getConstructor(Class.java:2151) at com.google.cloud.functions.invoker.HttpFunctionExecutor.forClass(HttpFunctionExecutor.java:53) at com.google.cloud.functions.invoker.runner.Invoker.startServer(Invoker.java:243) at com.google.cloud.functions.invoker.runner.Invoker.main(Invoker.java:121) Caused by: java.lang.ClassNotFoundException: com.nimbusds.jose.JWEEncrypter at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
Using the nimbus-jose-jwt jar. Tried with multiple versions of the jar as well. However, facing the same error. Below is snippet of it added in pom.xml:
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.15.2</version>
</dependency>
Java Runtime : Java 11 . Adding the function code below:
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.apache.http.HttpStatus;
import java.security.interfaces.*;
import javax.crypto.*;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jwt.*;
import java.util.*;
import java.security.KeyFactory;
import java.security.spec.X509EncodedKeySpec;
import java.io.File;
import java.security.PublicKey;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.DataInputStream;
public class Encrypt implements HttpFunction {
private static final Gson gson = new Gson();
private static final Logger logger = Logger.getLogger(
Encrypt.class.getName());
public void service(HttpRequest request, HttpResponse response)
throws IOException {
PrintWriter writer = new PrintWriter(response.getWriter());
String contentType = request.getContentType().orElse("");
String plaintext, ciphertext = "";
String jwtString="";
try {
JsonObject body = gson.fromJson(request.getReader(), JsonObject.class);
plaintext = body.toString();
logger.log(Level.INFO, "Plaintext: " plaintext);
RSAPublicKey publicKey = getPublicKey();
Date now = new Date();
// convert json object to map
Map<String, Object> claims = gson.fromJson(plaintext, HashMap.class);
System.out.println("claims: " claims);
JWTClaimsSet jwtClaims;
jwtClaims=generate(claims);
System.out.println("jwtClaims: " jwtClaims);
System.out.println(jwtClaims.toJSONObject());
JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM)
.type(JOSEObjectType.JWT).build();
EncryptedJWT jwt = new EncryptedJWT(header, jwtClaims);
RSAEncrypter encrypter = new RSAEncrypter(((RSAPublicKey)(publicKey)));
jwt.encrypt(encrypter);
jwtString = jwt.serialize();
System.out.println(jwtString);
jwt = EncryptedJWT.parse(jwtString);
response.setStatusCode(HttpURLConnection.HTTP_OK);
} catch (Exception e) {
logger.severe("Error parsing JSON: " e.getMessage());
}
HashMap<String, String> map = new HashMap<String, String>();
map.put("payload", jwtString);
String mapAsString = map
.keySet()
.stream()
.map(key -> "\"" key "\":\"" map.get(key) "\"")
.collect(Collectors.joining(", ", "{", "}"));
logger.log(Level.INFO, "mapAsString: " mapAsString);
writer.printf(mapAsString);
}
public static RSAPublicKey getPublicKey() {
try {
String publicKeyString = "aaa";
publicKeyString = publicKeyString.trim();
// byte[] publicKeyBytes=publicKeyString.getBytes();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] decoded = Base64.getDecoder().decode(publicKeyString);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(decoded);
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
return publicKey;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public JWTClaimsSet generate(final Map<String, Object> claims) {
// claims builder
final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
// add claims
for (final Map.Entry<String, Object> entry : claims.entrySet()) {
builder.claim(entry.getKey(), entry.getValue());
}
return builder.build();
}
public static void main(String[] args) {
}
}
Have also tried "mvn clean package" before deploying. Still face the same issue.
CodePudding user response:
Your error is stating that you are attempting to use a class which does not exist. If it works locally and it does not work on Google Cloud, that means the libraries/jar are not configured properly.
If what you are deploying is a Fat/Uber jar, unzip the jar and verify the location of nimbus-jose-jwt
.
An uber JAR is a JAR file that contains the function classes as well as all of its dependencies. You can build an uber JAR with both Maven and Gradle
If what you are deploying is a Thin Jar, ensure that your dependencies are in a package relative to the JAR deployed.
A thin JAR is a JAR file that contains only the function classes without the dependencies embedded in the same JAR file. Because the dependencies are still needed for deployment, you need to set things up as follows:
The dependencies must be in a subdirectory relative to the JAR to be deployed. The JAR must have a META-INF/MANIFEST.MF file that includes a Class-Path attribute whose value lists the required dependency paths.
The Google Cloud documentation is very clear on how you can use Maven/Gradle to deploy either.
https://cloud.google.com/functions/docs/concepts/java-deploy#deploy_from_a_jar