1
votes

I am trying to use MSI to access Azure Blob Storage containers to generate shared access signature. But every time i am trying to access , i am getting following error:

`java.lang.IllegalArgumentException: Cannot create Shared Access Signature unless the Account Key credentials are used by the ServiceClient.` 

I dont want to access blob storage container using credentials or AAD. Just want to use MSI, as this is the unique mode that we want to adapt in our application to access Azure resources. What this thing i am missing. I checked in my Splunk logs that MSI token is generating successfully. Below is the way i am creating the CloudBlobClient to access Blob container:

public CloudBlobClient cloudBlobClient() throws URISyntaxException {
    String storageAccountName = propertyUtil.getStorageAccountName();
    // Implemented some logic in AzureStorageMSICredential class to fetch access 
    // token, and its working correctly
    String msiToken = azureStorageMSICredentials.getToken();
    LOG.info("Initiating CloudBlobClient.... msitoken = " + msiToken);
    StorageCredentials storageCredentials =
      new StorageCredentialsToken(storageAccountName, msiToken);

    URI storageAccountURI = URIUtils.getStorageAccountURI(storageAccountName);
    CloudBlobClient cloudBlobClient = new CloudBlobClient(storageAccountURI, 
      storageCredentials);
    return cloudBlobClient;
  }

I searched many threads on stackoverflow, which seems dupliate of this one, but not really. Some are of 2017.

1

1 Answers

0
votes

By reviewing the Azure Storage Java SDK, I find that the generateSharedAccessSignature method will finally call the following:

public String generateSharedAccessSignature(
        final SharedAccessBlobPolicy policy, final SharedAccessBlobHeaders headers,
        final String groupPolicyIdentifier, final IPRange ipRange, final SharedAccessProtocols protocols)
        throws InvalidKeyException, StorageException {

    if (!StorageCredentialsHelper.canCredentialsSignRequest(this.blobServiceClient.getCredentials())) {
        throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY);
    }

    final String resourceName = this.getCanonicalName(true);

    final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(
            policy, headers, groupPolicyIdentifier, resourceName, ipRange, protocols, this.blobServiceClient,
            this.isSnapshot() ? Constants.QueryConstants.BLOB_SNAPSHOT_SERVICE : Constants.QueryConstants.BLOB_RESOURCE,
            this.getSnapshotID());

    final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(
            policy, headers, groupPolicyIdentifier,
            this.isSnapshot() ? Constants.QueryConstants.BLOB_SNAPSHOT_SERVICE : Constants.QueryConstants.BLOB_RESOURCE,
            ipRange, protocols, signature);

    return builder.toString();
}

The signature string is a Hmac256 string. There is a method in StorageCredentialsHelper to compute it.

public static synchronized String computeHmac256(final StorageCredentials creds, final String value) throws InvalidKeyException {
    if (creds.getClass().equals(StorageCredentialsAccountAndKey.class)) {
        byte[] utf8Bytes = null;
        try {
            utf8Bytes = value.getBytes(Constants.UTF8_CHARSET);
        }
        catch (final UnsupportedEncodingException e) {
            throw new IllegalArgumentException(e);
        }
        return Base64.encode(((StorageCredentialsAccountAndKey) creds).getHmac256().doFinal(utf8Bytes));
    }
    else {
        return null;
    }
}

In this method, a StorageCredentialsAccountAndKey is required. It is a key which can be used to sign data. However, as you were using MSI as authentication, the token you used was actually an AAD access token which cannot be used to sign in this place. You can check that with the following code:

StorageCredentials credentials = blobClient.getCredentials();
System.out.println(credentials.toString(true));

So, in the generateSharedAccessSignature method, an error will be thrown:

    if (!StorageCredentialsHelper.canCredentialsSignRequest(this.blobServiceClient.getCredentials())) {
        throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY);
    }

In conclusion, you are not able to generate SharedAccessSignature if you use MSI as authentication currently. You may post your request to the Azure Storage User Voice. If your request is voted high, the developer team may add this feature.