One of my projects involved integrating with a Bank. The requirements were to send NAV payments electronically to the bank. In US companies still send payment through Check. This customer was writing about 1000 checks per week and although I had automated the printing of the check, they still had to fold them and put them in envelop. They also had other payment types, such as Wire and ACH that they were submitting manually. The file structure was ISO 20022 Payments message xml Files.
You can read more about it here https://www.iso20022.org/payments_messages.page.
The client is on version 2009. Newer version of NAV have ISO 20022 built. I haven’t looked at it but it I wonder why it’s for European banks, when US Bank is using it as well. I’m not going to talk about the xml file or the ISO file structure. I’m going to talk about the transmitting of the file.
Once the file is created, it needs to be transmitted to bank. Before submitting the file needed to be digitally signed with Self signed X509 Certificate.
After some research, I found Bouncy Castle. Here is their website.
After some additional research, I built csharp assembly that can be called from NAV to sign a file with a certificate.
Here is the code for the cshap DLL.
public static class SignOnly
public static void SignFile(string fileName, string keyInFileName, string SignedFileName, string passphrase)
bool armor = false;
bool compress = true;
Stream keyIn = File.OpenRead(keyInFileName);
Stream outputStream = File.Create(SignedFileName);
outputStream = new ArmoredOutputStream(outputStream);
PgpSecretKey pgpSec = ReadSigningSecretKey(keyIn);
PgpPrivateKey pgpPrivKey = pgpSec.ExtractPrivateKey(passphrase.ToCharArray());
PgpSignatureGenerator sGen = new PgpSignatureGenerator(pgpSec.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
foreach (string userId in pgpSec.PublicKey.GetUserIds())
PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
// Just the first one!
Stream cOut = outputStream;
PgpCompressedDataGenerator cGen = null;
cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);
cOut = cGen.Open(cOut);
BcpgOutputStream bOut = new BcpgOutputStream(cOut);
FileInfo file = new FileInfo(fileName);
PgpLiteralDataGenerator lGen = new PgpLiteralDataGenerator();
Stream lOut = lGen.Open(bOut, PgpLiteralData.Binary, file);
FileStream fIn = file.OpenRead();
int ch = 0;
while ((ch = fIn.ReadByte()) >= 0)
if (cGen != null)
public static PgpSecretKey ReadSigningSecretKey(Stream inStream)
PgpSecretKeyRingBundle pgpSec = CreatePgpSecretKeyRingBundle(inStream);
PgpSecretKey key = null;
IEnumerator rIt = pgpSec.GetKeyRings().GetEnumerator();
while (key == null && rIt.MoveNext())
PgpSecretKeyRing kRing = (PgpSecretKeyRing)rIt.Current;
IEnumerator kIt = kRing.GetSecretKeys().GetEnumerator();
while (key == null && kIt.MoveNext())
PgpSecretKey k = (PgpSecretKey)kIt.Current;
key = k;
if (key == null)
throw new PgpException("Can't find signing key in key ring.");
public static PgpSecretKeyRingBundle CreatePgpSecretKeyRingBundle(System.IO.Stream keyInStream)
return new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(keyInStream));
And this here is how to call it from NAV.