Skip to main content

I’m playing around with the next generation .Net SDK and at least half of my calls to upload a file or create a folder are throwing an exception about RSA parameters in a cryptography library.



The full exception is like this:



System.Security.Cryptography.CryptographicException: The specified RSA parameters are not valid. Exponent and Modulus are required. If D is present, it must have the same length as Modulus. If D is present, P, Q, DP, DQ, and InverseQ are required and must have half the length of Modulus, rounded up, otherwise they must be omitted.

at System.Security.Cryptography.CngHelpers.ToBCryptBlob(RSAParameters& parameters)

at System.Security.Cryptography.RSABCrypt.ImportParameters(RSAParameters parameters)

at System.Security.Cryptography.RSA.Create(RSAParameters parameters)

at Microsoft.IdentityModel.Tokens.AsymmetricAdapter.InitializeUsingRsaSecurityKey(RsaSecurityKey rsaSecurityKey, String algorithm)

at Microsoft.IdentityModel.Tokens.AsymmetricAdapter..ctor(SecurityKey key, String algorithm, HashAlgorithm hashAlgorithm, Boolean requirePrivateKey)

at Microsoft.IdentityModel.Tokens.AsymmetricAdapter..ctor(SecurityKey key, String algorithm, HashAlgorithm hashAlgorithm, HashAlgorithmName hashAlgorithmName, Boolean requirePrivateKey)

at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.CreateAsymmetricAdapter()

at Microsoft.IdentityModel.Tokens.DisposableObjectPool`1.CreateInstance()

at Microsoft.IdentityModel.Tokens.DisposableObjectPool`1.Allocate()

at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider.Sign(Bytet] input)

at Microsoft.IdentityModel.JsonWebTokens.JwtTokenUtilities.CreateEncodedSignature(String input, SigningCredentials signingCredentials)

at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)

at Box.Sdk.Gen.JwtUtils.CreateJwtAssertion(Dictionary`2 claims, JwtKey key, JwtSignOptions options)

at Box.Sdk.Gen.BoxJwtAuth.RefreshTokenAsync(NetworkSession networkSession)

at Box.Sdk.Gen.BoxJwtAuth.RetrieveTokenAsync(NetworkSession networkSession)

at Box.Sdk.Gen.BoxJwtAuth.RetrieveAuthorizationHeaderAsync(NetworkSession networkSession)

at Fetch.HttpClientAdapter.BuildHttpRequest(String resource, FetchOptions options)

at Fetch.HttpClientAdapter.FetchAsync(String resource, FetchOptions options)

at Box.Sdk.Gen.Managers.FoldersManager.CreateFolderAsync(CreateFolderRequestBody requestBody, CreateFolderQueryParams queryParams, CreateFolderHeaders headers, Nullable`1 cancellationToken)



I’m guessing this is a bug and will be smoothed out as the SDK is further developed. But just in case I’m doing something wrong, I thought I would post here.



Sometimes the code works, sometimes it doesn’t. If I had to guess, I would say that it throws this exception the majority of the time.

Hi @jbuck79



Welcome to the forum!



I’m unfamiliar with the .NET SDK, but let me see if I can find someone from the SDK team to look at this.



I do see them generating the SDK almost on a daily base.



Best regards


Hi, I did some exploring on this and it actually seems to have something to do with how I am encrypting and decrypting the .json config from the Box app. I have no idea why because the method I am using to do this isn’t related to the Box SDK and works fine with the currently released version of the SDK. So it’s possible there is still a bug in the SDK somewhere, but I have worked around it for now.



I now have another issue with the Next Gen sdk in getting and setting metadata values for a file. The new SDK apparently has issues with number fields in metadata. I’m continuing to play with it and I’ll probably end up putting in an issue on the github


Hi,





A quick note about this. The SDK is expecting an encrypted private key, and it uses the passphrase to decrypt it when creating the JWT assertion.



Not sure if you created your own private key or if you used the generated one from the developer console. I think recently the encryption protocol has been updated to DES3. For what is worth take a look at this document for details on manually generating the private key.





Although this is odd and merits further investigation, typically the issue is a miss match in the field name. “Mysteriously” 🙂, the field names are normalized when the template is created, and often it is not what the developer expects. Query the metadata template end point to check what are the actual field names being used.



For example in this template:




curl --location 'https://api.box.com/2.0/metadata_templates/enterprise/demoMediaMetadata/schema' \

--header 'Authorization: ••••••' \



track_type is being translated to: "key": "trackType"



{

"id": "19e93588-dec7-4571-856b-f8bad8eb75b8",

"type": "metadata_template",

"templateKey": "demoMediaMetadata",

"scope": "enterprise_877840855",

"displayName": "Demo Media Metadata",

"hidden": false,

"copyInstanceOnItemCopy": false,

"fields": >

{

"id": "9c2d6147-25e3-4a28-8d16-78dbe21295ff",

"type": "string",

"key": "trackType",

"displayName": "track_type",

"hidden": false

},



Of course I’m speculating at this point, do let us know if this helps.



Cheers


Hi, so with the .json config file. I am downloading the config file from the developer console and then encrypting the whole thing. And then when I am ready to do the authentication in the SDK, I decrypt the file and send the string to the SDK. The encryption and decryption is done totally outside the SDK code. Our security team is very strict about storing credentials on the hard disk, which is why I encrypt them. I was previously using machine-level encryption, which works fine with the previous SDK, but was throwing the error in my original message when using the new SDK. I switched to user-level encryption, which is just one switch in the standard .net encryption library, and it seems to work fine now. Not sure what happened there.



As far as the metadata goes, what you are talking about is not my issue. I have been retrieving and setting the same metadata template with the old SDK just fine. I know that I am using the correct names for the fields. The problem with the new SDK is specifically that it doesn’t like number fields. Trying to retrieve a metadata instance with a number field throws this exception when deserializing:



System.Text.Json.JsonException: The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1.

---> System.InvalidOperationException: Cannot get the value of a token type 'Number' as a string.

at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType)

at System.Text.Json.Utf8JsonReader.GetString()

at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)

at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)

--- End of inner exception stack trace ---

at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)

at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)

at System.Text.Json.JsonSerializer.ReadFromSpanaTValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)

at System.Text.Json.JsonSerializer.DeserializezTValue](JsonElement element, JsonSerializerOptions options)

at Box.Sdk.Gen.Schemas.MetadataFull.OnDeserialized() in C:\d\x\Palig.CorpSys.ECM.LiftShiftToBox\Box.Sdk.Gen\Schemas\MetadataFull\MetadataFull.cs:line 55

at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)

at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)

at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)

at System.Text.Json.JsonSerializer.ReadFromSpanaTValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)

at System.Text.Json.JsonSerializer.ReadFromSpanaTValue](ReadOnlySpan`1 json, JsonTypeInfo`1 jsonTypeInfo)

at System.Text.Json.JsonSerializer.DeserializezTValue](String json, JsonSerializerOptions options)

at Serializer.SimpleJsonSerializer.Deserializezt](SerializedData obj) in C:\d\x\Palig.CorpSys.ECM.LiftShiftToBox\Box.Sdk.Gen\Serialization\Json\Serializer.cs:line 32

at Box.Sdk.Gen.Managers.FileMetadataManager.GetFileMetadataByIdAsync(String fileId, GetFileMetadataByIdScope scope, String templateKey, GetFileMetadataByIdHeaders headers, Nullable`1 cancellationToken) in C:\d\x\Palig.CorpSys.ECM.LiftShiftToBox\Box.Sdk.Gen\Managers\FileMetadata\FileMetadataManager.cs:line 80



The exception is thrown in the OnDeserialized method of MetadataFull.cs. It doesn’t like converting the number to a string. I looked up the error and found a Stackoverflow article that has this answer which I was able to add to the Deserialize method and it works fine now. Something similar will need to be done when setting metadata values on number fields because that is also throwing an exception.


Got it.



Let me see if I can get the attention of the SDK team.


Hi,


the issue with metdata was fixed in this commit: feat: add support for `ExtraData` of generic type `(box/box-codegen#5… · box/box-dotnet-sdk-gen@2a2208d · GitHub. It is now available in v0.4.0. Let me know if this solves your issue.


Best,


Lukasz


Hi @jbuck79


Could you elaborate a bit more on your workaround for the jwt issue? What do you mean by switching from machine-level to user-level encrpytion? How did you achieve that?



Can you provide code you are using for BoxJwtAuth setup? Are you using FromConfigJsonString function of JwtConfig?



Also, is it possible to try the configuration client without all the encryption/decryption and just pass the config string itself for testing purposes? Are you sure that the passed config string is the same after the encryption/decryption process?


Sure, I am using the ‘System.Security.Cryptography.ProtectedData’ library to do the encryption. I encrypt the data by using a method like this. The method changes the file extension from .json to .ejson, just to indicate that this is the encrypted version.



private void EncryptConfig(FileInfo config)

{

var unencryptedData = Encoding.UTF8.GetBytes(File.ReadAllText(config.FullName));



// To protect:

byteb] encryptedData = ProtectedData.Protect(

unencryptedData,

null,

DataProtectionScope.CurrentUser);



File.WriteAllBytes(config.FullName.Replace(".json", ".ejson"), encryptedData);

}



With the old SDK, I was using ‘DataProtectionScope.LocalMachine’, but the new SDK seems to have issues with that for some unknown reason. I switched to the CurrentUser scope and that seems to be working fine. CurrentUser is more secure, and my InfoSec team would probably prefer it anyway, so I’m going to stick with it. CurrentUser requires that the encryption/decryption be done by the same user. While LocalMachine allows any user on the PC to decrypt.



This is the method I use to decrypt and then I pass it to the FromConfigJsonString function.



private string DecryptConfigFile(string ConfigFilePath)

{

var encryptedData = File.ReadAllBytes(ConfigFilePath);



// To unprotect:

byte>] decryptedData = ProtectedData.Unprotect(

encryptedData,

null,

DataProtectionScope.CurrentUser);



return Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);

}


Hi, the new v0.4.0 release seems to have resolved the issues I was having with metadata. Thanks!


I’m glad that the new version of the SDK has solved the metadata issue. I’m still trying to wrap the head around the encryption/decryption issue. From the description, it seems that the new SDK should not affect it. I also tried locally, but both modes work fine. My best guess it that the string after decryption is different than before when DataProtectionScope.LocalMachine is used. Can you confirm that this is not the case?


Reply