AL
AL copied to clipboard
.NET cryptography handling
I'm using System.Security.Cryptography.MD5 for files integrity check - also large ones (ZIP files over 100MB - probably too large to use Azure micro service effectively). The files are then exported with their MD5 hash appended as files postfix. The files are stored as BLOBs, then passed further to file distribution agent that is running on another server (talking to NAV via web service). The principle is to "stamp" the file with integrity checksum right after it's generated (and before it is passed further).
I can also imagine need to use other cryptographic functions (like SHA256, SHA512, maybe AES, RSA, ECC, and high quality random functions etc)... This kind of functionality is in general good to have locally, not as an external (micro) service.
Are you planning to implement equivalent of .NET cryptographic functions as a part of AL?
By the way, are you planning to still support the .NET functionality used by standard objects, e.g. Codeunit 419 (File Management), for example used for ZIP compression?
Code examples (i.e. how I use the .NET's MD5 function now):
SysSecCryptMD5 := SysSecCryptMD5.Create; ZipFileHash := DELCHR(SysBitConvrtr.ToString(SysSecCryptMD5.ComputeHash(SysIOFile.OpenRead(ServerFilePath))), '=', '-');
I am using a bit more .NET libraries:
- System.Security.Cryptography.MD5.'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
- System.BitConverter.'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
- System.IO.File.'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
Currently I am using temporary server files (used also by Codeunit #419 / "File Management") but with "AL"/Tenerife will not use files/System.IO.File, which should be replaced by equivalent streams.
.NET connection would be very fine. My customers do not want to submit data to Azure so Azure Functions is out of the question. I also need something like Newtonsoft.JSON or MimeKit or System.Threading.Tasks.
Is there any status update on this? In general, if there is no intention of providing .NET Interop capability, you will have to add a lot of base .NET functionality. I love the fact that we have now the ability for Xml and Json, but especially when communicating with web services/REST APIs, we will need MD5 implementation.
Since you mentioned MD5, check out https://security.stackexchange.com/questions/19906/is-md5-considered-insecure
You are correct, MD5 is not necessarily the best choice, but as you can see, I at least mentioned "communication with REST APIs" and I don't have control over the APIs. This is what a lot of developers still use. It also doesn't matter necessarily, even if we could use SHA-2, it would be great. The biggest issue is that we don't have access to any .NET Interop anymore and therefore cannot even "choose" what algorithms we want to use.
Please see if the new methods added to C/AL Open Library satisfy your requirement: https://github.com/Microsoft/cal-open-library/pull/10
Hash-suite in Microsoft/AL looks very good 👍 Some would find also very convenient to have System.Security.Cryptography.Xml.SignedXml & related classes ported to AL, to process W3C XmlDigSig (2000-2013) compliant signatures. First wish is signature verification. For only verification we could actually not necessarily load a certificate from BLOB/file/store (that only for signing), but take trusted authority thumbprint + IANA cert. chain policy OID(s) as parameters to check (eventually + some extra parameters). Maybe also some some generic X.509 certificate and signature operations would be nice to have (e.g. certificate and signature verification).
That's what this library is for. develop the required functionality in a generic way and create a pull request and everyone can benefit from it
Peter Zentner NAV-X, LLC Phone: +1 877.779.6289 Email: [email protected] Website: www.nav-x.com Address: 670 N Beers Street, Bldg 4 - 2nd Floor, Holmdel, NJ 07733 From: MarekJZ [email protected] Sent: Saturday, November 25, 2017 6:00:19 AM To: Microsoft/AL Cc: Peter Zentner; Comment Subject: Re: [Microsoft/AL] .NET cryptography handling (#171)
Hash-suite in Microsoft/AL looks very good 👍 Some would find also very convenient to have System.Security.Cryptography.Xml.SignedXml & related classes ported to AL, to process W3C XmlDigSig (2000-2013) compliant signatures. First wish is signature verification. For only verification we could actually not necessarily load a certificate from BLOB/file/store (that only for signing), but take trusted authority thumbprint + IANA cert. chain policy OID(s) as parameters to check (eventually + some extra parameters). Maybe also some some generic X.509 certificate and signature operations would be nice to have (e.g. certificate and signature verification).
— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/Microsoft/AL/issues/171#issuecomment-346933565, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AdEY0vU2HQEhRWRt2eRwmYuoZakHqKBvks5s5_NDgaJpZM4NIk_6.
@StanislawStempin We currently have the following 2 functions that work for certificate loading from BLOB (via Stream) and XML Digital Signature verification. XML signature verification is used against confidential and often heavy or numerous documents, so it would be really great to have that opportunity to do it internally (without need to use web service integration - i.e. sending that XML out of NAV).
PROCEDURE LoadX509CertFromStream@1101107016(CertStream@1101107000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.Stream";VAR X509Cert2@1101107001 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509Certificate2" SUPPRESSDISPOSE);
VAR
MemStream@1101107002 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";
X509CertStoreFlags@1101107003 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509KeyStorageFlags";
BEGIN
IF CertStream.Length = 0 THEN
ERROR('Missing cert content');
// Copying stream:
MemStream := MemStream.MemoryStream();
CertStream.Position := 0;
CertStream.CopyTo(MemStream);
// Loading cert:
X509CertStoreFlags := X509CertStoreFlags.DefaultKeySet;
X509Cert2 := X509Cert2.X509Certificate2();
X509Cert2.Import(MemStream.ToArray(), '', X509CertStoreFlags);
END;
PROCEDURE VerifyXmlDigSig@1101107014(XmlDigSigStream@1101107000 : DotNet "'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.Stream";X509Cert2@1101107004 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509Certificate2";AuthorityCertThumbprint@1101107008 : Text[100];CertChainPolicyOid@1101107014 : Text[1024]) IsValidSignature : Boolean;
VAR
Oid@1101107015 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.Oid";
X509Chain@1101107003 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509Chain";
X509ChainElement@1101107009 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509ChainElement";
X509VerificationFlags@1101107011 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509VerificationFlags";
X509RevocationMode@1101107013 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509RevocationMode";
X509RevocationFlag@1101107010 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Security.Cryptography.X509Certificates.X509RevocationFlag";
UrlRetrievalTimeoutDuration@1101107016 : Duration;
IsChainValid@1101107012 : Boolean;
XmlDocument@1101107002 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlDocument";
XmlNodeList@1101107006 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlNodeList";
XmlElement@1101107005 : DotNet "'System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlElement";
SignedXml@1101107001 : DotNet "'System.Security, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.System.Security.Cryptography.Xml.SignedXml";
BEGIN
IF XmlDigSigStream.Length = 0 THEN
ERROR('Missing signature content');
IF ISNULL(X509Cert2) THEN
ERROR('Missing certificate');
X509Chain := X509Chain.X509Chain();
// Perform only basic check here:
X509RevocationMode := X509RevocationMode.Online; // (or Offline/NoCheck) TODO: Make configurable
X509RevocationFlag := X509RevocationFlag.EntireChain; // (or ExcludeRoot) TODO: Make configurable
X509VerificationFlags := X509VerificationFlags.NoFlag; // TODO: Make configurable
X509Chain.ChainPolicy.RevocationMode := X509RevocationMode;
X509Chain.ChainPolicy.RevocationFlag := X509RevocationFlag;
X509Chain.ChainPolicy.VerificationFlags := X509VerificationFlags;
// Policy OID:
IF CertChainPolicyOid <> '' THEN BEGIN
Oid := Oid.Oid(CertChainPolicyOid);
X509Chain.ChainPolicy.CertificatePolicy.Add(Oid);
END;
X509Chain.ChainPolicy.VerificationTime := CURRENTDATETIME;
UrlRetrievalTimeoutDuration := 30000; // TODO: Make configurable
X509Chain.ChainPolicy.UrlRetrievalTimeout := UrlRetrievalTimeoutDuration;
// Building chain:
IsChainValid := X509Chain.Build(X509Cert2);
IF NOT IsChainValid THEN
EXIT(FALSE);
// Authority requirement:
IF AuthorityCertThumbprint <> '' THEN BEGIN
// Searching the cert chain to find element with AuthorityCertThumbprint:
IsChainValid := FALSE;
FOREACH X509ChainElement IN X509Chain.ChainElements DO BEGIN
IF X509ChainElement.Certificate.Thumbprint = AuthorityCertThumbprint THEN BEGIN
IsChainValid := TRUE;
BREAK;
END;
END;
IF NOT IsChainValid THEN
EXIT(FALSE);
END;
// Loading XML doc:
XmlDocument := XmlDocument.XmlDocument();
XmlDocument.PreserveWhitespace := TRUE;
XmlDocument.Load(XmlDigSigStream);
// Loading SignedXml:
SignedXml := SignedXml.SignedXml(XmlDocument);
XmlNodeList := XmlDocument.GetElementsByTagName('Signature');
XmlElement := XmlNodeList.Item(0);
SignedXml.LoadXml(XmlElement);
// Verifying only XML signature
// (chain already checked before):
EXIT(SignedXml.CheckSignature(X509Cert2, TRUE));
END;
There can be also a situation when signature verification is done against custom/internal root CA, with certificate installed in Windows store. Then we may also need to be able to install custom certificate in the NAV running environment, or specify it explicitly (i.e. pass the root CA certificate's stream as extra parameter). Verification against single certificate (just SignedXml.CheckSignature(X509Cert2, TRUE)) would also be useful.
This is a fairly old suggestion, and these should be tracked in https://aka.ms/bcideas, and not on the AL GitHub, which is for bugs in AL language and VC Code VSIX only.
In general, for cryptography requests, these should go into the System apps Cryptography Management module. See https://github.com/microsoft/BCApps/tree/main/src/System%20Application/App/Cryptography%20Management
Closing this issue