Hi,
I am new to this forum, and thrilled to see that there is good activity!
I am using SNAP in a Java / Maven project, and I try to apply ETAD correction to a Product:
private Product applyETAD(Product product) {
Map<String, Object> params = new HashMap<>();
params.put("orbitType", "Sentinel Precise");
params.put("applyETAD", true);
try {
return GPF.createProduct("S1-ETAD-Correction", params, product);
} catch (Exception e) {
logger.error("Error applying ETAD correction: {}", e.getMessage());
throw new RuntimeException("Failed to apply ETAD correction", e);
}
}
However, I cannot figure out how to properly configure my credentials, and get the following error:
Error applying ETAD correction: Credentials for Copernicus DataSpace not found in the credentials store.
I have credentials for CDSE, and successfully use these in other applications (inclding generating tokens etc.), but I cannot find documentation on how to set up my ‘credentials store’?
Thanks,
Patrik
For the benefit of other peeple encountering the same issue, I will share a solution (with caveat that there may well be better ones).
Since I could not find any documentation on this I stepped through the code and evetually identified the classes DataSpaces and CredentialsManager to be the targets of interest.
Stepping through the decompiled files shows that DataSpaces creates and exectures the query to pull ETAD data from the Copernicus Data Space using odata interface. The Autorisation is via token, which is generated by CredentialsManager. DataSpaces has a constructor which accepts user and password, but the used default is a constructor where the token is generated by the CredentialsManager.
In turn, the CredentialsManager tries to pull the password and user from ‘product-library.properties’, at some path (this is most likely all defined when installing and configuring via the GUI).
The solution was to make a small class that generate the required ‘product-library.properties’, with an excrypted password, as expected from SNAP.
Here is what worked for me:
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Properties;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.esa.snap.runtime.EngineConfig;
public class CredentialSetup {
public static void run() throws Exception {
// Get credentials from environment
String username = System.getenv("CDSE_USER");
String password = System.getenv("CDSE_PASS");
if (username == null || password == null) {
throw new IllegalArgumentException("CDSE_USER and CDSE_PASS environment variables must be set");
}
// Get the exact same path as CredentialsManager
Path configPath = Paths.get(EngineConfig.instance().userDir().toString(),
"config", "Preferences", "product-library.properties");
// Ensure parent directories exist
Files.createDirectories(configPath.getParent());
// Encrypt password using the same method as in CredentialsManager.decrypt()
SecretKeySpec key = createKey("Copernicus DataSpace");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, key);
String encryptedPassword = Base64.getEncoder().encodeToString(
cipher.doFinal(password.getBytes()));
// Write properties file
Properties props = new Properties();
props.setProperty("Copernicus DataSpace.username", username);
props.setProperty("Copernicus DataSpace.password", encryptedPassword);
try (OutputStream out = Files.newOutputStream(configPath)) {
props.store(out, "Copernicus DataSpace Credentials");
}
// Verify the encryption worked
try {
String decrypted = decrypt(encryptedPassword, "Copernicus DataSpace");
if (!password.equals(decrypted)) {
throw new RuntimeException("Encryption verification failed!");
}
System.out.println("Credentials successfully stored and verified at: " + configPath);
} catch (Exception e) {
System.err.println("Failed to verify encryption: " + e.getMessage());
throw e;
}
}
// Exact same methods as in CredentialsManager
private static SecretKeySpec createKey(String secretKey)
throws UnsupportedEncodingException, NoSuchAlgorithmException {
byte[] key = secretKey.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
return new SecretKeySpec(key, "AES");
}
private static String decrypt(String textToDecrypt, String secretKey)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException {
if (textToDecrypt != null && secretKey != null) {
SecretKeySpec key = createKey(secretKey);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64.getDecoder().decode(textToDecrypt)));
}
return null;
}
}