I've got code that uploads a new asset. Creates transcode jobs for streaming and thumbnail creation. Then polls for state changes to that transcode job to update.
This all works fine on a local machine. When running on an Azure website I receive:
Access is denied.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Security.Cryptography.CryptographicException: Access is denied.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[CryptographicException: Access is denied.
]
System.Security.Cryptography.X509Certificates.X509Store.Open(OpenFlags flags) +1985499
Microsoft.WindowsAzure.MediaServices.Client.EncryptionUtils.SaveCertificateToStore(X509Certificate2 certToStore) +64
Microsoft.WindowsAzure.MediaServices.Client.ContentKeyBaseCollection.GetCertificateForProtectionKeyId(DataServiceContext dataContext, String protectionKeyId) +201
Microsoft.WindowsAzure.MediaServices.Client.JobData.ProtectTaskConfiguration(TaskData task, X509Certificate2& certToUse, DataServiceContext dataContext) +285
Microsoft.WindowsAzure.MediaServices.Client.JobData.InnerSubmit(DataServiceContext dataContext) +540
Microsoft.WindowsAzure.MediaServices.Client.JobData.SubmitAsync() +63
Microsoft.WindowsAzure.MediaServices.Client.JobData.Submit() +25
SEISMatch.MediaServices.AzureMediaServices.ProcessVideo(Video video) +498
SEISMatch.BusinessLogic.MediaServicesManager.StartProcessingMedia(Video v) +48
SEISMatch.BusinessLogic.VideoManager.UploadComplete(Guid guid) +493
lambda_method(Closure , ControllerBase , Object[] ) +155
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +14
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +182
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
System.Web.Mvc.Async.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41() +28
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +50
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +32
System.Web.Mvc.Async.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33() +58
System.Web.Mvc.Async.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49() +225
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeActionMethodWithFilters>b__36(IAsyncResult asyncResult) +10
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +50
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +34
System.Web.Mvc.Async.<>c__DisplayClass2a.<BeginInvokeAction>b__20() +24
System.Web.Mvc.Async.<>c__DisplayClass25.<BeginInvokeAction>b__22(IAsyncResult asyncResult) +99
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +50
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27
System.Web.Mvc.<>c__DisplayClass1d.<BeginExecuteCore>b__18(IAsyncResult asyncResult) +14
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +55
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +39
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +55
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +29
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__3(IAsyncResult asyncResult) +25
System.Web.Mvc.Async.<>c__DisplayClass4.<MakeVoidDelegate>b__3(IAsyncResult ar) +23
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +55
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +31
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9629296
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
I've found references to this error in other places. But no explanation on how to solve it within an Azure website context. Presumably it's possible to use an Azure website to interact with Azure media services? The error is caused by trying to save a certificate for some internal functionality within the Media Services library.
My Code looks like:
var inputAsset = mediaContext.Assets.Where(a => a.Name == video.AssetName).FirstOrDefault();
IJob job = mediaContext.Jobs.Create(video.FileName + " Processing");
var thumbnailAssetID = AddThumbnailExtractionTask(job, inputAsset);
var encodedAsset = AddEncodeTask(job, inputAsset);
job.Submit(); //Error thrown here
It is crashing in: https://github.com/WindowsAzure/azure-sdk-for-media-services/blob/3b2d5e227b2622c6d78fb10b1a733b188f1a6767/src/net/Client/DuplicatedFiles/EncryptionUtils.cs
During SaveCertificateToStore, specifically, in store.Open(OpenFlags.ReadWrite).
The save is called from here:
/// <summary>
/// Gets the certificate for protection key id.
/// </summary>
/// <param name="dataContext">The data context.</param>
/// <param name="protectionKeyId">The protection key id.</param>
/// <returns>The content key.</returns>
internal static X509Certificate2 GetCertificateForProtectionKeyId(DataServiceContext dataContext, string protectionKeyId)
{
// First check to see if we have the cert in our store already.
X509Certificate2 certToUse = EncryptionUtils.GetCertificateFromStore(protectionKeyId);
if ((certToUse == null) && (dataContext != null))
{
// If not, download it from Nimbus to use.
Uri uriGetProtectionKey = new Uri(string.Format(CultureInfo.InvariantCulture, "/GetProtectionKey?protectionKeyId='{0}'", protectionKeyId), UriKind.Relative);
IEnumerable<string> results2 = dataContext.Execute<string>(uriGetProtectionKey);
string certString = results2.Single();
byte[] certBytes = Convert.FromBase64String(certString);
certToUse = new X509Certificate2(certBytes);
// Finally save it for next time.
EncryptionUtils.SaveCertificateToStore(certToUse);
}
return certToUse;
}
This should catch the exception and sit on it because it’s serialization is not mandatory (and impossible in WebSites due to privilege restrictions).
Something like:
// Finally try to save it for next time, as an optimization.
try{
EncryptionUtils.SaveCertificateToStore(certToUse);
}
catch()
{
//Do nothing, this is not mandatory and breaks Azure WebSites deployment scenarios where they do not have rights to X509Stor.Open().
//Ref: http://stackoverflow.com/questions/18056707/create-azure-media-services-job-from-azure-shared-website
}
At this time, we don’t have the resources to test/confirm this. If you can re-build the GitHub of the SDK with the above changes, then you’d should be able to move past this.