PKCS#11Interop.X509Store unable to find Private Key - pkcs#11

Pkcs11X509Certificate is unable to find the private key in some tokens.
Pkcs11X509Certificate.GetRSAPrivateKey() yields null. Then, when I run SignedXml.ComputeSignature(), I get the following error:
System.Security.Cryptography.CryptographicException: 'Signing key is not loaded.'

Adding the code below (proof of concept) to the Pkcs11X509Certificate.FindKey works.
Basically I removed CKA.CKA_LABEL from the search template attributes and it finds the certificate Private Key.
// Contrary to what PKCS#11 specification suggests, subject of the private key is not readable even after login.
// So if we cannot find private key with subject, we will search for private keys without subject.
if (keyHandle == null)
{
searchTemplate = new List<IObjectAttribute>()
{
session.Factories.ObjectAttributeFactory.Create(CKA.CKA_CLASS, keyClass),
session.Factories.ObjectAttributeFactory.Create(CKA.CKA_TOKEN, true),
session.Factories.ObjectAttributeFactory.Create(CKA.CKA_ID, ckaId),
//session.Factories.ObjectAttributeFactory.Create(CKA.CKA_LABEL, ckaLabel),
};
foreach (IObjectHandle foundObjectHandle in session.FindAllObjects(searchTemplate))
{
keyHandle = foundObjectHandle;
break;
}
}

Related

Sign PDF with new Belgian id card (eid middleware and itext)

I try to sign some pdf's with a Belgian id card.
To acheive that, I'm using the belgium eid middleware to sign the data and itext7 to stamp the pdf with the signature.
I use a PdfSigner (itext) and I have implement a IExternalSignature to call the eid middleware to sign the message.
All work well for the Belgian id card 1.7 with encryption RSA and hash SHA256.
But when I try to sign with the new Belgian id card 1.8 with encryption ECDSA and hash SHA384, the signature can't be validated by adobe reader (or other reader).
"The document has been altered or corrupted".
It seems to be a mismatch somewhere in the hashing or ...
I search for some days but I have no more idea to fix that ...
Someone have an idea about what is going wrong?
Thanks in advance for your help.
Here some additional informations.
The external signature class:
internal sealed class BeIDSignature : IExternalSignature
{
public string GetEncryptionAlgorithm()
{
return eidWrapper.Instance.GetEncryptionAlgorithm().ToString();
}
public string GetHashAlgorithm()
{
switch (EidWrapper.Instance.GetEncryptionAlgorithm())
{
case EidWrapper.EncryptionAmgorithm.RSA:
return DigestAlgorithms.SHA256;
case EidWrapper.EncryptionAmgorithm.ECDSA:
return DigestAlgorithms.SHA384;
default:
return null;
}
}
public byte[] Sign(byte[] message)
{
return EidWrapper.Instance.SignData(message);
}
}
GetEncryptionAlgorithm will return RSA or ECDSA depending of the chip.
The sign method will use the eid-mw packege to generate the signature.
A little piece of code of the sign method of the EidWrapper:
if (key.KeyType.KeyType == CKK.EC)
{
session.SignInit(new Mechanism(CKM.ECDSA_SHA384), key);
return session.Sign(data);
}
else if (key.KeyType.KeyType == CKK.RSA)
{
session.SignInit(new Mechanism(CKM.SHA256_RSA_PKCS), key);
return session.Sign(data);
}
You can find here a zip with 3 pdf files:
The original file
One signed directly with adobe (siganture is ok)
One signed with eid-mw and itext (signature is NOT ok). But remember
that is working for RSA/SHA256 siganture.
https://easyupload.io/yzscsu
Thanks again for your time.
Here is a sample of an external container for the belgian eid smartcard.
The code is not fully implemented but you have a base to make a siganture in ECDSA/SHA384 correctly.
Hope that will help someone :)
internal sealed class BeIdExternalSignatureContainer : IExternalSignatureContainer
{
private IX509Store _crls = null;
private readonly IHttpClientFactory _httpClientFactory;
private int _crlsSize = 0;
public BeIdExternalSignatureContainer(IHttpClientFactory httpClientFactory)
{
this._httpClientFactory = httpClientFactory;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
signDic.Put(PdfName.Filter, PdfName.Adobe_PPKLite);
signDic.Put(PdfName.SubFilter, PdfName.ETSI_CAdES_DETACHED);
}
public int ComputeEstimateSize()
{
// Base on itext estimation
if (this._crlsSize == 0)
{
this.InitializeCrls();
}
return (8192 + this._crlsSize + 4192) * 2 + 2;
}
public byte[] Sign(Stream data)
{
IReadOnlyDictionary<string, byte[]> certificatesBinaries = EidWrapper.Instance.GetCertificateFiles(new string[] {
Constants.SIGNATURE_CERTIFICATE_NAME,
Constants.CA_CERTIFICATE_NAME,
Constants.ROOT_CERTIFICATE_NAME
});
X509Certificate signCertificate = new(X509CertificateStructure.GetInstance(certificatesBinaries[Constants.SIGNATURE_CERTIFICATE_NAME]));
EidWrapper.EncryptionAlgorithms encryptionAlgorithm = EidWrapper.Instance.GetEncryptionAlgorithm();
SignerInfoGenerator signerGenerator = new SignerInfoGeneratorBuilder()
.WithSignedAttributeGenerator(new PadesSignedAttributeGenerator { SigningCertificate = signCertificate })
.WithUnsignedAttributeGenerator(new PadesUnsignedAttributeGenerator(this._httpClientFactory))
.Build(new BeIdSignatureFactory(encryptionAlgorithm), signCertificate);
CmsSignedDataGenerator gen = new();
gen.AddSignerInfoGenerator(signerGenerator);
IX509Store x509CertStore = X509StoreFactory.Create(
"Certificate/Collection",
new X509CollectionStoreParameters(certificatesBinaries.Values.Select(b => new X509Certificate(X509CertificateStructure.GetInstance(b))).ToList()));
gen.AddCertificates(x509CertStore);
gen.AddCrls(this.Crls);
CmsProcessableInputStream cmsProcessableInputStream = new(data);
CmsSignedData signedData = gen.Generate(cmsProcessableInputStream, false);
return signedData.GetEncoded();
}
private IX509Store Crls
{
get
{
if (this._crls == null)
{
this.InitializeCrls();
}
return this._crls;
}
}
private void InitializeCrls()
{
this._crlsSize = 0;
List<X509Crl> crls = new();
X509CrlParser crlParser = new();
HttpClient httpClient = this._httpClientFactory.CreateClient();
foreach (var crlUrl in BeIdSignatureConstants.CRL_URL_COLLECTION)
{
using HttpResponseMessage response = httpClient.Send(new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(crlUrl) });
if (response.IsSuccessStatusCode)
{
using MemoryStream ms = new MemoryStream();
response.Content.ReadAsStream().CopyTo(ms);
byte[] crlBytes = ms.ToArray();
this._crlsSize += crlBytes.Length;
crls.Add(crlParser.ReadCrl(crlBytes));
}
}
this._crls = X509StoreFactory.Create(
"CRL/Collection",
new X509CollectionStoreParameters(crls));
}
}
internal class BeIdSignatureFactory : ISignatureFactory
{
private readonly EidWrapper.EncryptionAlgorithms _encryptionAlgorithm;
public BeIdSignatureFactory(EidWrapper.EncryptionAlgorithms encryptionAlgorithm)
{
this._encryptionAlgorithm = encryptionAlgorithm;
}
public IStreamCalculator CreateCalculator()
{
BeIdSigner signer = new(this._encryptionAlgorithm);
signer.Init(true, null);
return new DefaultSignatureCalculator(signer);
}
public object AlgorithmDetails
{
get
{
return MapAlgorithm(this._encryptionAlgorithm);
}
}
public static AlgorithmIdentifier MapAlgorithm(EidWrapper.EncryptionAlgorithms encryptionAlgorithm)
{
switch (encryptionAlgorithm)
{
case EidWrapper.EncryptionAlgorithms.RSA:
return new AlgorithmIdentifier(PkcsObjectIdentifiers.Sha256WithRsaEncryption, DerNull.Instance);
case EidWrapper.EncryptionAlgorithms.ECDSA:
return new AlgorithmIdentifier(X9ObjectIdentifiers.ECDsaWithSha384, DerNull.Instance);
default:
throw new ArgumentException($"Unsupported encryption algorithm: {encryptionAlgorithm}");
}
}
}
internal class BeIdSigner : ISigner
{
private byte[] _input;
private readonly EidWrapper.EncryptionAlgorithms _encryptionAlgorithm;
public BeIdSigner(EidWrapper.EncryptionAlgorithms encryptionAlgorithm)
{
this._encryptionAlgorithm = encryptionAlgorithm;
}
public void Init(bool forSigning, ICipherParameters parameters)
{
this.Reset();
}
public void Update(byte input)
{
throw new NotImplementedException("The \"ISigner.Update\" method is not implemented for the \"BeIdSigner\" class.");
}
public void BlockUpdate(byte[] input, int inOff, int length)
{
this._input = input.Skip(inOff).Take(length).ToArray();
}
public byte[] GenerateSignature()
{
return this._encryptionAlgorithm == EidWrapper.EncryptionAlgorithms.ECDSA
? PlainToDer(EidWrapper.Instance.SignData(this._input))
: EidWrapper.Instance.SignData(this._input);
}
public bool VerifySignature(byte[] signature)
{
throw new NotImplementedException("The \"ISigner.VerifySignature\" method is not implemented for the \"BeIdSigner\" class.");
}
public void Reset()
{
this._input = null;
}
public string AlgorithmName => throw new NotImplementedException("The \"ISigner.AlgorithmName\" property is not implemented for the \"BeIdSigner\" class.");
private static byte[] PlainToDer(byte[] plain)
{
int valueLength = plain.Length / 2;
BigInteger r = new(1, plain, 0, valueLength);
BigInteger s = new(1, plain, valueLength, valueLength);
return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
}
}
This answer sums up the comments to the question.
iText and ECDSA signatures
First of all one has to realize that currently (i.e. for iText 7.2.2) ECDSA is not supported by all parts of the iText signing API.
This limitation is mostly due to the iText PdfPKCS7 class used to create and validate CMS signature containers embedded in PDFs. When it is used to create a signature container, the identifier it stores in the signatureAlgorithm field does not take the used hash algorithm into account. E.g. it uses the same value for RSA (with PKCS1 v1.5 padding) signatures, no matter if it's actually SHA1withRSA or SHA256withRSA or whichever combination.
For RSA this is ok because there indeed is an identifier that can be used for all these cases. For DSA it is somewhat ok because in many contexts DSA is limited to use with SHA1 only.
For ECDSA this is not ok, there only are identifiers taking the hash algorithm into account. iText uses the EC public key identifier in all these cases which is simply wrong. The reason why hardly anyone noticed this bug is that Adobe Acrobat validation apparently ignores the contents of this signatureAlgorithm field: You can even write the RSA identifier into this field of an ECDSA signature and the validation succeeds without an indication of a problem.
To create proper ECDSA signatures, therefore, one currently should not use the PdfPKCS7 class. As all the PdfSigner.signDetached methods internally use the PdfPKCS7 class, this in turn means that one must not use them but instead PdfSigner.signExternalContainer. As a consequence, one must not use an IExternalSignature implementation to retrieve one's signature value but instead an IExternalSignatureContainer implementation in which one builds the CMS signature container differently, for example using BouncyCastle classes.
In the case at hand the BeIDSignature implementation of IExternalSignature, therefore, must be replaced accordingly.
For further details please read the section Which Interface to Use of the iText knowledge base article Digital Signing with iText 7.
ECDSA signature formats
There are two major formats in which an ECDSA signature value can be stored, either as a TLV (DER) encoded sequence of two integers or (plain encoding) as the concatenation of fixed length representations of those two integers.
Depending on the used format one has to use specific algorithm identifiers for ECDSA and PLAIN-ECDSA respectively. If one needs a specific identifier, one can convert the signature value from one format to the other.
In the case at hand the Belgian ID card returns the ECDSA signature value in plain format. To use the more common non-PLAIN ECDSA identifiers, one has to convert that value to the DER format. This can be done using this method:
byte[] PlainToDer(byte[] plain)
{
int valueLength = plain.Length / 2;
BigInteger r = new BigInteger(1, plain, 0, valueLength);
BigInteger s = new BigInteger(1, plain, valueLength, valueLength);
return new DerSequence(new DerInteger(r), new DerInteger(s)).GetEncoded(Asn1Encodable.Der);
}
(X509Certificate2Signature helper method from the code examples of Digital Signing with iText 7)
Some thoughts:
Are you sure that there is an ECDSA key available on the chip ? I had a short look at the documentation (not sure it's up to date - cf. eid-mw github), which only mentions RSA. Additionally if you can sign using RSA/SHA256, having ECDSA support as well would mean that there's a second key pair on the card - I have some doubts about this ;
Try to sign with ECDSA / SHA384 in Adobe Reader using your eID - check whether you can validate the signature ;
Validate the signature online, using the SD-DSS tool: the diagnostic data may help you in pin-pointing what is wrong (e.g. a sha384 digest was generated, but the signature structure mentions sha256 as digest algo).

report.CRITICAL: Empty or no Private Key provided [] []

After the upgrade from M2.3.3 to M2.4.2 checkout page is not working.
In system.log file I am getting an error:
[2021-04-27 10:35:21] report.CRITICAL: Empty or no Private Key provided [] []
Anyone having idea on this?
There's little to no details to go on on based on your error, alas with some googling I've ended up at this exception:
https://github.com/open-pay/openpay-php/blob/master/data/OpenpayApiConnector.php#L45
$myApiKey = Openpay::getApiKey();
if (!$myApiKey) {
throw new OpenpayApiAuthError("Empty or no Private Key provided");
} else if (!preg_match('/^sk_[a-z0-9]{32}$/i', $myApiKey)) {
throw new OpenpayApiAuthError("Invalid Private Key '".$myApiKey."'");
}
Which I suppose indicates you're using a module to interact with OpenPay's API ( maybe this one? ) in which case you may wanna double-check your private key are all sorted out in your back-end config:

Using HyperLedger Fabric certificates for signing a message

We have generated public and private keys in Hyperledger Fabric using its API which are getting stored in crypto_material folder.
Now, on one machine we want to sign a message using private key and verify its validity on second machine using corresponding public key. To achieve this task we have written code as follows.
var crypto = require("crypto");
var secp256k1 = require("secp256k1");
var fs = require("fs");
var sha256 = require('sha256')
let pub_key_file = "./keyfiles/4d4a0d669d507ff05c73c9e1ca40fb5f909f9806df138a82767a739e9de31240-pub"
fs.access(pub_key_file, fs.F_OK, (err) => {
if (err) {
console.log("readfile error : ", err);
return;
}
})
var public_key = fs.readFileSync(pub_key_file);
// Reading private key from file system.
let priv_key_file = "./keyfiles/4d4a0d669d507ff05c73c9e1ca40fb5f909f9806df138a82767a739e9de31240-priv"
fs.access(priv_key_file, fs.F_OK, (err) => {
if (err) {
console.log("readfile error : ", err);
return;
}
})
var private_key = fs.readFileSync(priv_key_file);
var privKey = new Uint8Array(sha256(private_Key).match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
var pubKey = new Uint8Array(sha256(public_Key).match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
console.log(pubKey instanceof Uint8Array) //returning true here
console.log("value : ", secp256k1.privateKeyVerify(privKey)); //returning true here
console.log("value : ", secp256k1.publicKeyVerify(pubKey)); // Getting error like expected public key to be an Uint8Array with length [33, 65] hyperledger fabric
Why the secp256k1 module is successfully verifying privateKey but failing with publicKey.
I assume that you performed an enroll after you registered the identity using register.
You will actually have multiple issues with what you are attempting to do.
Fabric CA generates EC keys using NIST-approved curves (e.g. secp256r1,secp384r1) and not secp256k1 used by Bitcoin
Fabric CA produces a private key and an X509 certificate from the corresponding public key
The private key and X509 certificate are returned as PEM-encoded blobs as well.
There are many libraries out there which you can use to sign/verify using PEM encoded private keys and X509 certificates. jsrsasign is fairly straightforward.
You can also use some of the modules provided by the Fabric Node SDK as well depending on what you are doing.

Implement Custom Authentication In Windows Azure Mobile Services

Windows Azure Mobile Services currently doesn't have an option for custom authentication and looking at the feature request
http://feedback.azure.com/forums/216254-mobile-services/suggestions/3313778-custom-user-auth
It isn't coming anytime soon.
With a .NET backend and a .NET application how do you implement custom authentication, so that you don't have to use Facebook, Google or any of their other current providers?
There are plenty of partially completed tutorials on how this this is done with a JS backend and iOS and Android but where are the .NET examples?
I finally worked through the solution, with some help of the articles listed below, some intellisense and some trial and error.
How WAMS Works
First I wanted to describe what WAMS is in a very simple form as this part confused me for a while until it finally clicked. WAMS is just a collection of pre-existing technologies packaged up for rapid deployment. What you need to know for this scenario is:
As you can see WAMS is really just a container for a WebAPI and other things, which I won't go into detail here. When you create a new Mobile Service in Azure you get to download a project that contains the WebAPI. The example they use is the TodoItem, so you will see code for this scenario through the project.
Below is where you download this example from (I was just doing a Windows Phone 8 app)
I could go on further about this but this tutorial will get you started:
http://azure.microsoft.com/en-us/documentation/articles/mobile-services-dotnet-backend-windows-store-dotnet-get-started/
Setup WAMS Project
You will need your MasterKey and ApplicationKey. You can get them from the Azure Portal, clicking on your Mobile Services App and pressing Manage Keys at the bottom
The project you just downloaded, in the Controllers folder I just created a new controller called AccountController.cs and inside I put
public HttpResponseMessage GetLogin(String username, String password)
{
String masterKey = "[enter your master key here]";
bool isValidated = true;
if (isValidated)
return new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("{ 'UserId' : 'F907F58C-09FE-4F25-A26B-3248CD30F835', 'token' : '" + GetSecurityToken(new TimeSpan(1,0, 0), String.Empty, "F907F58C-09FE-4F25-A26B-3248CD30F835", masterKey) + "' }") };
else
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Username and password are incorrect");
}
private static string GetSecurityToken(TimeSpan periodBeforeExpires, string aud, string userId, string masterKey)
{
var now = DateTime.UtcNow;
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var payload = new
{
exp = (int)now.Add(periodBeforeExpires).Subtract(utc0).TotalSeconds,
iss = "urn:microsoft:windows-azure:zumo",
ver = 2,
aud = "urn:microsoft:windows-azure:zumo",
uid = userId
};
var keyBytes = Encoding.UTF8.GetBytes(masterKey + "JWTSig");
var segments = new List<string>();
//kid changed to a string
var header = new { alg = "HS256", typ = "JWT", kid = "0" };
byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));
segments.Add(Base64UrlEncode(headerBytes));
segments.Add(Base64UrlEncode(payloadBytes));
var stringToSign = string.Join(".", segments.ToArray());
var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);
SHA256Managed hash = new SHA256Managed();
byte[] signingBytes = hash.ComputeHash(keyBytes);
var sha = new HMACSHA256(signingBytes);
byte[] signature = sha.ComputeHash(bytesToSign);
segments.Add(Base64UrlEncode(signature));
return string.Join(".", segments.ToArray());
}
// from JWT spec
private static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input);
output = output.Split('=')[0]; // Remove any trailing '='s
output = output.Replace('+', '-'); // 62nd char of encoding
output = output.Replace('/', '_'); // 63rd char of encoding
return output;
}
You can replace what is in GetLogin, with your own validation code. Once validated, it will return a security token (JWT) that is needed.
If you are testing on you localhost, remember to go into your web.config file and fill in the following keys
<add key="MS_MasterKey" value="Overridden by portal settings" />
<add key="MS_ApplicationKey" value="Overridden by portal settings" />
You need to enter in your Master and Application Keys here. They will be overridden when you upload them but they need to be entered if you are running everything locally.
At the top of the TodoItemController add the AuthorizeLevel attribute as shown below
[AuthorizeLevel(AuthorizationLevel.User)]
public class TodoItemController : TableController<TodoItem>
You will need to modify most of the functions in your TodoItemController but here is an example of the Get All function.
public IQueryable<TodoItem> GetAllTodoItems()
{
var currentUser = User as ServiceUser;
Guid id = new Guid(currentUser.Id);
return Query().Where(todo => todo.UserId == id);
}
Just a side note I am using UserId as Guid (uniqueidentifier) and you need to add this to the todo model definition. You can make the UserId as any type you want, e.g. Int32
Windows Phone/Store App
Please note that this is just an example and you should clean the code up in your main application once you have it working.
On your Client App
Install NuGet Package: Windows Azure Mobile Services
Go into App.xaml.cs and add this to the top
public static MobileServiceClient MobileService = new MobileServiceClient(
"http://localhost:50527/",
"[enter application key here]"
);
In the MainPage.xaml.cs I created
public class Token
{
public Guid UserId { get; set; }
public String token { get; set; }
}
In the main class add an Authenticate function
private bool Authenticate(String username, String password)
{
HttpClient client = new HttpClient();
// Enter your own localhost settings here
client.BaseAddress = new Uri("http://localhost:50527/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync(String.Format("api/Account/Login?username={0}&password={1}", username, password)).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var token = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(response.Content.ReadAsStringAsync().Result);
App.MobileService.CurrentUser = new MobileServiceUser(token.UserId.ToString());
App.MobileService.CurrentUser.MobileServiceAuthenticationToken = token.token;
return true;
}
else
{
//Something has gone wrong, handle it here
return false;
}
}
Then in the Main_Loaded function
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Authenticate("test", "test");
RefreshTodoItems();
}
If you have break points in the WebAPI, you will see it come in, get the token, then come back to the ToDoItemController and the currentUser will be filled with the UserId and token.
You will need to create your own login page as with this method you can't use the automatically created one with the other identity providers. However I much prefer creating my own login screen anyway.
Any other questions let me know in the comments and I will help if I can.
Security Note
Remember to use SSL.
References
[] http://www.thejoyofcode.com/Exploring_custom_identity_in_Mobile_Services_Day_12_.aspx
[] http://www.contentmaster.com/azure/creating-a-jwt-token-to-access-windows-azure-mobile-services/
[] http://chrisrisner.com/Custom-Authentication-with-Azure-Mobile-Services-and-LensRocket
This is exactly how you do it. This man needs 10 stars and a 5 crates of beer!
One thing, I used the mobile Service LoginResult for login like:
var token = Newtonsoft.Json.JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
Hope to get this into Android now!

The given key was not present in the dictionary

I am developing a plugin in crm 5.0 to retrieve date "ct_valuedate" from an entity called "ct_marketvalue" and formatting and saving in a field called "ct_dateserial"
I get an error while I debug "The given key was not present in the dictionary"
public class MarketValueDateFormatting : PluginBase
{
protected override void ExecutePlugin()
{
try
{
switch (_crmMessage)
{
case CrmPluginMessageEnum.Create:
if (_context.InputParameters.Contains("ct_marketvalue"))
{
//Obtain the logical name of the entity
string moniker1 = ((EntityReference)_context.InputParameters["EntityMoniker"]).LogicalName;
//Verify that the target entity represents an Account.
//If not, this plug-in was not registered correctly.
if (moniker1.Equals("ct_marketvalue"))
{
Entity marketvalueimage = (Entity)_context.PostEntityImages["ct_marketvalue"];
Guid marketvalueid = marketvalueimage.Id;
if (marketvalueimage.Contains("ct_valuedate"))
{
DateTime dateserial = (DateTime)marketvalueimage.Attributes["ct_valuedate"];
String dateserialstring = dateserial.ToString("YYYYMMdd");
Ct_marketvalue marketvalue = new Ct_marketvalue();
marketvalue.Ct_dateserial = dateserialstring;
marketvalue.Id = marketvalueid;
_serviceContext.UpdateObject(marketvalue);
}
}
}
break;
}
catch (Exception ex)
{
throw ex;
}
}
}
}
Few notes about your code.
You should check in your code that _context.PostEntityImages contains "ct_marketvalue". It's possible either to forgot register or to do a mistake in image name.
Might be better use .ToEntity rather than access attributes using .Attributes["ct_valuedate"].
I'm not sure what is purpose of the plugin you wrote, but it looks it is post stage plugin and it updates the same entity instance, that was in InputParameters. Might be better to make this plugin pre stage and update value directly in InputParameters. Because, if not "The given key was not present in the dictionary" exception, it will cause infinite loop. You will need check context.Depth.