Closed fishermans closed 1 month ago
Thank you for reporting this. I'm not sure this extra permission can be avoided when using jarsigner. When generating an Authenticode signature Jsign already calls https://{vaultname}.vault.azure.net//certificates/{alias}
for fetching the certificate, the call to list the certificates is only made when the alias parameter is omitted (for guessing the alias when the vault contains only one certificate). With jarsigner the alias parameter is mandatory and I fail to see why it lists the aliases, there is no need to do that.
Does your account have the Key Vault Crypto User and Key Vault Certificate User roles?
If it doesn't work with the Key Vault Crypto User and Key Vault Certificate User roles, try adding the Key Vault Reader role to your account.
Let me know how it works for your, I'll update the documentation afterward if necessary.
Thank you for reporting this. I'm not sure this extra permission can be avoided when using jarsigner. When generating an Authenticode signature Jsign already calls
https://{vaultname}.vault.azure.net//certificates/{alias}
for fetching the certificate, the call to list the certificates is only made when the alias parameter is omitted (for guessing the alias when the vault contains only one certificate). With jarsigner the alias parameter is mandatory and I fail to see why it lists the aliases, there is no need to do that.
I was able to sign the jar after modifying the code to return an empty list instead of throwing the exception.
Does your account have the Key Vault Crypto User and Key Vault Certificate User roles?
I am not the admin of the azure key vault and don't have permissions to have a look at.
@Override
public List<String> aliases() throws KeyStoreException {
List<String> aliases = new ArrayList<>();
try {
Map<String, ?> response = client.get("/certificates?api-version=7.2");
Object[] certificates = (Object[]) response.get("value");
for (Object certificate : certificates) {
String id = (String) ((Map) certificate).get("id");
aliases.add(id.substring(id.lastIndexOf('/') + 1));
}
} catch (IOException ignore) {
//throw new KeyStoreException("Unable to retrieve Azure Key Vault certificate aliases", ignore);
}
return aliases;
}
Is working for me.
This is where jarsigner fetches all the certificates in the keystore:
It looks like it adds all the certificates to a list of trusted certificates, I guess this is necessary when verifying a signature, but I don't think it's necessary when signing.
A possible workaround would be to throw the KeyStoreException in AzureKeyVaultSigningService.aliases()
only if no element of the stacktrace contains the sun.security.tools.jarsigner
string.
Could you try this:
@Override
public List<String> aliases() throws KeyStoreException {
List<String> aliases = new ArrayList<>();
try {
Map<String, ?> response = client.get("/certificates?api-version=7.2");
Object[] certificates = (Object[]) response.get("value");
for (Object certificate : certificates) {
String id = (String) ((Map) certificate).get("id");
aliases.add(id.substring(id.lastIndexOf('/') + 1));
}
} catch (IOException e) {
// check if the call comes from jarsigner
boolean jarsigner = false;
for (StackTraceElement element : e.getStackTrace()) {
if (element.getClassName().contains("jarsigner")) {
jarsigner = true;
break;
}
}
if (!jarsigner) {
throw new KeyStoreException("Unable to retrieve Azure Key Vault certificate aliases", e);
}
}
return aliases;
}
Looks good to me. 👍
Splitting into a separate method would make the code more readable:
@Override
public List<String> aliases() throws KeyStoreException {
List<String> aliases = new ArrayList<>();
try {
Map<String, ?> response = client.get("/certificates?api-version=7.2");
Object[] certificates = (Object[]) response.get("value");
for (Object certificate : certificates) {
String id = (String) ((Map) certificate).get("id");
aliases.add(id.substring(id.lastIndexOf('/') + 1));
}
} catch (IOException e) {
if (!isJarSignerUsed(e)) {
throw new KeyStoreException("Unable to retrieve Azure Key Vault certificate aliases", e);
}
}
return aliases;
}
private boolean isJarSignerUsed(IOException e) {
for (StackTraceElement element : e.getStackTrace()) {
if (element.getClassName().contains("jarsigner")) {
return true;
}
}
return false;
}
Perfect! Thank you so much for your very fast support! Appreciate.
"Unable to retrieve Azure Key Vault certificate aliases" is returned when trying to use JCA provider for jarsigner.
Response via Postman gives the following result:
Hence, it is required to have list permissions on the key vault.
Requesting the certificate directly via https://AAAAAAAAAA.vault.azure.net//certificates/CCCCCCCC?api-version=7.2 is working well.