When we receive a one pass signed and encrypted OpenPGP message we can simply decrypt it or both decrypt it and verifythe authenticity of the sender in a single step.
This tutorial refers to version 1.7.12 or newer of the library. The tutorial for older versions is available here.
The examples below shows how to perform both decryption and verification with DidiSoft OpenPGP Library for .NET:
Decrypt and verify a file
1. with keys located in files
2. with keys located in a KeyStore
Decrypt and verify a file that may contain multiple files inside
3. with keys located in files
4. with keys located in a KeyStore
Decrypt and verify a String message
5. with keys located in files
6. with keys located in a KeyStore
Decrypt and verify password encrypted file
7. with a public key located in a file
8. with a public key located in a KeyStore
Appendix
A. Exception handling
1. Decrypt and verify file with keys located in files
This example demonstrates OpenPGP decryption and verification of an OpenPGP one pass signed and encrypted file.
We use the public key of the other party to verify the source of this signed file. Our own private key is usually used for decryption.
C# example
using System;
using DidiSoft.Pgp;
public class DecryptAndVerify
{
public void Demo()
{
// create an instance of the library
PGPLib pgp = new PGPLib();
SignatureCheckResult signatureCheck =
pgp.DecryptAndVerifyFile(@"C:\Test\OUTPUT.pgp",
@"C:\Test\my_private_key.asc",
"private key password",
@"C:\Test\sender_public_key.asc",
@"C:\Test\INPUT.txt");
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Public Class DecryptAndVerify
Public Sub Demo()
' create an instance of the library
Dim pgp As New PGPLib()
Dim signatureCheck As SignatureCheckResult = _
pgp.DecryptAndVerifyFile("C:\Test\OUTPUT.pgp", _
"C:\Test\my_private_key.asc", _
"private key password", _
"C:\Test\sender_public_key.asc", _
"C:\Test\INPUT.txt")
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
End Sub
End Class |
Back to Top
2. Decrypt and verify file with keys located in a KeyStore
In this example the public key of the sender is used for verification and our private key is used for decryption and they both are located in a KeyStore.
C# example
using System;
using DidiSoft.Pgp;
public class KeyStoreDecryptAndVerifyFile
{
public static void Demo()
{
// initialize the KeyStore
KeyStore keyStore = new KeyStore(@"c:\key.store", "key store pass");
// create an instance of the library
PGPLib pgp = new PGPLib();
// we should provide the private decryption key password too
String decryptionKeyPassword = "private key password";
SignatureCheckResult signatureCheck =
pgp.DecryptAndVerifyFile(@"c:\INPUT.pgp",
keyStore,
decryptionKeyPassword,
@"c:\OUTPUT.txt");
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Public Class KeyStoreDecryptAndVerifyFile
Public Shared Sub Demo()
' initialize the KeyStore
Dim keyStore As New KeyStore("c:\key.store", "key store pass")
' create an instance of the library
Dim pgp As New PGPLib()
' we should provide the private decryption key password too
Dim decryptionKeyPassword As String = "private key password"
Dim signatureCheck As SignatureCheckResult = _
pgp.DecryptAndVerifyFile("c:\OUTPUT.pgp", _
keyStore, _
decryptionKeyPassword, _
"c:\OUTPUT.txt")
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
End Sub
End Class |
Back to Top
3. Decrypt and verify a multiple file archive with keys located in files
This sample is useful in cases when the .pgp archive contains multiple files inside. The only drawback is that this we need to invoke two methods instead of one:
C# example
using System;
using DidiSoft.Pgp;
public class DecryptAndVerifyMulti
{
public void Demo()
{
// create an instance of the library
PGPLib pgp = new PGPLib();
// private key to be used for decryption
String privateKeyFile = @"DataFiles\private.key";
String privateKeyPassword = "changeit";
// public key of the sender, to be used for signature verficitaion
String senderPublicKeyFile = @"DataFiles\public.key";
string outputFolder = @"DataFiles";
SignatureCheckResult signatureCheck =
pgp.VerifyFileWithoutExtracting(@"DataFiles\OUTPUTse.pgp",
privateKeyFile,
privateKeyPassword,
senderPublicKeyFile);
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
// decrypts the content of the PGP archive
// and returns array with full file paths of the
// exytracted file(s)
string[] decryptedFileNames =
pgp.DecryptTo(@"DataFiles\OUTPUTse.pgp",
privateKeyFile,
privateKeyPassword,
outputFolder);
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Public Class DecryptAndVerifyMulti
Public Sub Demo()
' create an instance of the library
Dim pgp As New PGPLib()
' private key to be used for decryption
Dim privateKeyFile As [String] = "DataFiles\private.key"
Dim privateKeyPassword As [String] = "changeit"
' public key of the sender, to be used for signature verficitaion
Dim senderPublicKeyFile As [String] = "DataFiles\public.key"
Dim outputFolder As String = "DataFiles"
Dim signatureCheck As SignatureCheckResult = _
pgp.VerifyFileWithoutExtracting("DataFiles\OUTPUTse.pgp", _
privateKeyFile, _
privateKeyPassword, _
senderPublicKeyFile)
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
' decrypts the content of the PGP archive
' and returns array with full file paths of the
' exytracted file(s)
Dim decryptedFileNames As String() = _
pgp.DecryptTo("DataFiles\OUTPUTse.pgp", _
privateKeyFile, _
privateKeyPassword, _
outputFolder)
End Sub
End Class |
Back to Top
4. Decrypt and verify multiple file archive with keys located in a KeyStore
This sample is similar with the above one, but now the public key of the sender and our decryption private key are located in aKeyStore.
C# example
using System;
using DidiSoft.Pgp;
public class KeyStoreDecryptAndVerifyMulti
{
public void Demo()
{
// create an instance of the library
PGPLib pgp = new PGPLib();
KeyStore keyStore = new KeyStore(@"DataFiles\key.store", "changeit");
String privateKeyPassword = "changeit";
string outputFolder = @"DataFiles";
SignatureCheckResult signatureCheck =
pgp.VerifyFileWithoutExtracting(@"DataFiles\OUTPUTse.pgp",
keyStore,
privateKeyPassword);
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
// decrypts the content of the PGP archive
// and returns array with full file paths of the
// exytracted file(s)
string[] decryptedFileNames =
pgp.DecryptTo(@"DataFiles\OUTPUTse.pgp",
keyStore,
privateKeyPassword,
outputFolder);
}
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Public Class KeyStoreDecryptAndVerifyMulti
Public Sub Demo()
' create an instance of the library
Dim pgp As New PGPLib()
Dim keyStore As New KeyStore("DataFiles\key.store", "changeit")
Dim privateKeyPassword As [String] = "changeit"
Dim outputFolder As String = "DataFiles"
Dim signatureCheck As SignatureCheckResult = _
pgp.VerifyFileWithoutExtracting("DataFiles\OUTPUTse.pgp", _
keyStore, _
privateKeyPassword)
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
' decrypts the content of the PGP archive
' and returns array with full file paths of the
' exytracted file(s)
Dim decryptedFileNames As String() = _
pgp.DecryptTo("DataFiles\OUTPUTse.pgp", _
keyStore, _
privateKeyPassword, _
outputFolder)
End Sub
End Class |
Back to Top
5. Decrypt and verify a String message with keys located in files
If we have an encrypted String message we can decrypt it directly without writing it into a file beforehand.
C# example
using System;
using System.IO;
using DidiSoft.Pgp;
class DecryptAndVerifyString
{
public void Demo()
{
// obtain encrypted and signed message
String signedAndEncryptedMessage = ...
String plainTextExtracted;
// create an instance of the library
PGPLib pgp = new PGPLib();
// decrypt and verify
SignatureCheckResult signatureCheck =
pgp.DecryptAndVerifyString(signedAndEncryptedMessage,
new FileInfo(@"c:\my_private_key.asc"),
"private key password",
new FileInfo(@"c:\sender_public_key.asc"),
out plainTextExtracted);
// print the results
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
Console.WriteLine("Extracted message: " + plainTextExtracted);
}
} |
VB.NET example
Imports System
Imports System.IO
Imports DidiSoft.Pgp
Class DecryptAndVerifyString
Public Sub Demo()
' obtain encrypted and signed message
Dim signedAndEncryptedMessage As String = ...
Dim plainTextExtracted As String
' create an instance of the library
Dim pgp As New PGPLib()
' decrypt and verify
Dim signatureCheck As SignatureCheckResult = _
pgp.DecryptAndVerifyString(signedAndEncryptedMessage, _
New FileInfo("c:\private.key"), _
"private key password", _
New FileInfo("c:\public.key"), _
plainTextExtracted)
' print the results
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
Console.WriteLine("Extracted message: " + plainTextExtracted)
End Sub
End Class |
Back to Top
6. Decrypt and verify string message with keys located in a KeyStore
This example shows how to decrypt and verify a signed and encrypted String message with keys stored in a KeyStore.
C# example
using System;
using DidiSoft.Pgp;
class KeyStoreDecryptAndVerifyString
{
public static void Demo()
{
// obtain an OpenPGP signed and enrypted message
String signedAndEncryptedString = ...
String privateKeyDecryptionPassword = "private key password";
// Extract the message and check the validity of the signature
String plainText;
// create an instance of the library
PGPLib pgp = new PGPLib();
// initialize the key store
KeyStore ks = new KeyStore(@"c:\key.store", "key store password");
SignatureCheckResult signatureCheck =
pgp.DecryptAndVerifyString(signedAndEncryptedString,
ks,
privateKeyDecryptionPassword,
out plainText);
// Print the results
Console.WriteLine("Extracted plain text message is " + plainText);
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Class KeyStoreDecryptAndVerifyString
Public Shared Sub Demo()
' obtain an OpenPGP signed and enrypted message
Dim signedAndEncryptedString As String = ...
Dim privateKeyDecryptionPassword As String = "private key password"
' Extract the message and check the validity of the signature
Dim plainText As String
' create an instance of the library
Dim pgp As New PGPLib()
' initialize the key store
Dim ks As New KeyStore("c:\key.store", "key store password")
Dim signatureCheck As SignatureCheckResult = _
pgp.DecryptAndVerifyString(signedAndEncryptedString, _
ks, _
privateKeyDecryptionPassword, _
plainText)
' Print the results
Console.WriteLine("Extracted plain text message is " + plainText)
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
End Sub
End Class |
Back to Top
7. Decrypt and verify a password encrypted file with a public key located in a file
This example demonstrates OpenPGP decryption and verification of an OpenPGP one pass signed and password encrypted file. We use the public key of the other party to verify the source of this signed file. The decryption password is the same with the one used for encryption.
C# example
using System;
using DidiSoft.Pgp;
public class DecryptAndVerifyPBE
{
public void Demo()
{
// create an instance of the library
PGPLib pgp = new PGPLib();
SignatureCheckResult signatureCheck =
pgp.DecryptAndVerifyFilePBE(@"C:\Test\OUTPUT.pgp",
"encryption password",
@"C:\Test\sender_public_key.asc",
@"C:\Test\INPUT.txt");
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Public Class DecryptAndVerifyPBE
Public Sub Demo()
' create an instance of the library
Dim pgp As New PGPLib()
Dim signatureCheck As SignatureCheckResult = _
pgp.DecryptAndVerifyFilePBE("C:\Test\OUTPUT.pgp", _
"encryption password", _
"C:\Test\sender_public_key.asc", _
"C:\Test\INPUT.txt")
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
End Sub
End Class |
Back to Top
8. Decrypt and verify password encrypted file when the public key is located in a KeyStore
In this example the public key of the sender used for verification is located in a KeyStore and the same password used for encryption is used for decryption.
C# example
using System;
using DidiSoft.Pgp;
public class KeyStoreDecryptAndVerifyFile
{
public static void Demo()
{
// initialize the KeyStore
KeyStore keyStore = new KeyStore(@"c:\key.store", "key store pass");
// create an instance of the library
PGPLib pgp = new PGPLib();
// we should provide the private decryption key password too
String decryptionPassword = "encryption password";
SignatureCheckResult signatureCheck =
pgp.DecryptAndVerifyFilePBE(@"c:\INPUT.pgp",
keyStore,
decryptionPassword,
@"c:\OUTPUT.txt");
if (signatureCheck == SignatureCheckResult.SignatureVerified)
{
Console.WriteLine("Signare OK");
}
else if (signatureCheck == SignatureCheckResult.SignatureBroken)
{
Console.WriteLine("Signare of the message is either broken or forged");
}
else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
{
Console.WriteLine("The provided public key doesn't match the signature");
}
else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
{
Console.WriteLine("This message is not digitally signed");
}
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Public Class KeyStoreDecryptAndVerifyFile
Public Shared Sub Demo()
' initialize the KeyStore
Dim keyStore As New KeyStore("c:\key.store", "key store pass")
' create an instance of the library
Dim pgp As New PGPLib()
' we should provide the decryption password
Dim decryptionPassword As String = "encryption password"
Dim signatureCheck As SignatureCheckResult = _
pgp.DecryptAndVerifyFile("c:\OUTPUT.pgp", _
keyStore, _
decryptionPassword, _
"c:\OUTPUT.txt")
If signatureCheck = SignatureCheckResult.SignatureVerified Then
Console.WriteLine("Signare OK")
ElseIf signatureCheck = SignatureCheckResult.SignatureBroken Then
Console.WriteLine("Signare of the message is either broken or forged")
ElseIf signatureCheck = SignatureCheckResult.PublicKeyNotMatching Then
Console.WriteLine("The provided public key(s) doesn't match the signature")
ElseIf signatureCheck = SignatureCheckResult.NoSignatureFound Then
Console.WriteLine("This message is not digitally signed")
End If
End Sub
End Class |
Back to Top
Appendix A. Exception Handling
The sample code below follows the proposed general exception handling and illustrates the exceptions sub classes ofDidiSoft.Pgp.PGPException that we may expected when decrypting and verifying OpenPGP data in one pass.
C# example
using System;
using DidiSoft.Pgp;
using DidiSoft.Pgp.Exceptions;
public class DecryptAndVerify
{
public void Demo()
{
// create an instance of the library
PGPLib pgp = new PGPLib();
try
{
SignatureCheckResult signatureCheck = pgp.DecryptAndVerify ...
}
catch (PGPException e)
{
if (e is NonPGPDataException)
{
// The input data is not an OpenPGP archive or is corrupted
}
else if (e is WrongPublicKeyException)
{
// The supplied public key is not an OpenPGP public key or is corrupted
}
else if (e is WrongPrivateKeyException)
{
// The message cannot be decrypted with this private key
// or the supplied private key is not an OpenPGP private key or is corrupted
Console.WriteLine(e.Message);
}
else if (e is WrongPasswordException)
{
// The password for the private key is incorrect
}
else if (e is FileIsPBEEncryptedException)
{
// The input file is password encrypted
// We have to use DecryptAndVeifyFilePBE or DecryptAndVerifyStreamPBE
}
else if (e is IntegrityCheckException)
{
// The encrypted data is corrupted
}
else
{ // other OpenPGP error
Console.WriteLine(e.Message);
}
}
}
} |
VB.NET example
Imports System
Imports DidiSoft.Pgp
Imports DidiSoft.Pgp.Exceptions
Public Class DecryptAndVerify
Public Sub Demo()
' create an instance of the library
Dim pgp As New PGPLib()
Try
Dim signatureCheck As SignatureCheckResult = pgp.DecryptAndVerify ...
Catch e As PGPException
If TypeOf e Is NonPGPDataException Then
' The input file is not an OpenPGP archive or is corrupted
ElseIf TypeOf e Is WrongPublicKeyException Then
' The supplied public key is not an OpenPGP public key or is corrupted
ElseIf TypeOf e Is WrongPrivateKeyException Then
' The message cannot be decrypted with this private key
' or the supplied private key is not an OpenPGP private key or is corrupted
Console.WriteLine(e.Message)
ElseIf TypeOf e Is WrongPasswordException Then
' The password for the private key is incorrect
ElseIf TypeOf e Is FileIsPBEEncryptedException Then
' The input file is password encrypted.
' You have to use DecryptAndVeifyFilePBE or DecryptAndVerifyStreamPBE
ElseIf TypeOf e Is IntegrityCheckException Then
' The encrypted data is corrupted
Else
Console.WriteLine(e.Message)
End If
End Try
End Sub
End Class |
Back to Top
Summary
In this chapter we have demonstrated how to decrypt and verify in one step a single pass signed and encrypted OpenPGP data.
You may also be interested in analyzing OpenPGP archive and checking the encryption and signing Key ID’s before performing decryption and signature verification.
List of methods used:
PGPLib.DecryptAndVerifyFile | Decrypts and verifies the digital signature of an OpenPGP encrypted file |
PGPLib.DecryptAndVerifyString | Decrypts and verifies the digital signature of an OpenPGP encrypted String message |
PGPLib.DecryptAndVerifyFilePBE | Decrypts and verifies the digital signature of an OpenPGP password encrypted file |