I have wcf web service that signs pdf documents when there is a request from my website. Everything works fine locally trying to sign few documents while development.
Once my code was deployed to production environment, it was working fine for few hours but suddenly i have started received Error n 48 followed by n 224.
Message : Error n. 48 Source : NCryptoki Stack Trace : at Cryptware.NCryptoki.Session.Sign(Byte[] data) at iTextSharp.text.pdf.security.MakeSignature.SignDetached(PdfSignatureAppearance sap, IExternalSignature externalSignature, ICollection
1 chain, ICollection
1 crlList, IOcspClient ocspClient, ITSAClient tsaClient, Int32 estimatedSize, CryptoStandard sigtype)Message : Error n. 224 Source : NCryptoki Stack Trace : at Cryptware.NCryptoki.Session.Sign(Byte[] data) at iTextSharp.text.pdf.security.MakeSignature.SignDetached(PdfSignatureAppearance sap, IExternalSignature externalSignature, ICollection
1 chain, ICollection
1 crlList, IOcspClient ocspClient, ITSAClient tsaClient, Int32 estimatedSize, CryptoStandard sigtype)
I struggling to understand what could be happening and how can i replicate this issue locally so i can try to fix it. I thought it could be load issue but according to logs server was only signing about 4-5 request per min.
Here is the snippet of my signing code.
1) WCF SOAP Action
public void TestSignPDF()
{
string fileName = Guid.NewGuid().ToString();
String src = @"C:\tmp\singlepage.pdf";
String DEST = string.Format(@"C:\tmp\{0}.pdf", fileName);
MemoryStream ms = new MemoryStream();
FileStream file = new FileStream(src, FileMode.Open, FileAccess.Read);
byte[] bytes = new byte[file.Length];
file.Read(bytes, 0, (int)file.Length);
ms.Write(bytes, 0, (int)file.Length);
file.Close();
ms.Seek(0, SeekOrigin.Begin);
HSMSignDocument x = new HSMSignDocument();
Stream result = x.SignDocument(ms, "334", fileName);
if (result != null)
{
FileStream file2 = new FileStream(DEST, FileMode.Create, System.IO.FileAccess.Write);
byte[] bytes2 = new byte[result.Length];
result.Read(bytes2, 0, (int)result.Length);
file2.Write(bytes2, 0, bytes2.Length);
file2.Close();
result.Close();
}
ms.Close();
}
2.) HSMSignDocument class (static)
public class HSMSignDocument : IDisposable
{
private static Cryptoki cryptoki = null;
private static Session session = null;
private static NLogger LOGGER = new NLogger();
private static readonly string HSMPartitionPwd = ConfigurationManager.AppSettings["HSMPartitionPassword"];
private string TIME_STAMPING_SERVER_URL = "xx";
private string TIME_STAMPING_SERVER_ACCOUNT = "xx";
private string TIME_STAMPING_SERVER_PASSWORD = "xx";
static HSMSignDocument()
{
Cryptoki.Licensee = "xxx";
Cryptoki.ProductKey = "xxx";
if (cryptoki == null)
{
cryptoki = new Cryptoki();
cryptoki.Attach(@"C:\Program Files\SafeNet\LunaClient\cryptoki.dll");
cryptoki.Initialize();
}
if (session == null)
{
// Reads the set of slots containing a token
SlotList slots = cryptoki.Slots;
if (slots.Count == 0)
{
LOGGER.Error("slots.Count == 0, no slots available");
return;
}
// Gets the first slot available
Slot slot = slots[0];
if (!slot.IsTokenPresent)
{
LOGGER.Error("!slot.IsTokenPresent, no tokens present");
return;;
}
// Gets the first token available
Token token = slot.Token;
// Opens a read/write serial session
session = token.OpenSession(Session.CKF_SERIAL_SESSION | Session.CKF_RW_SESSION, null, null);
int nRes = session.Login(Session.CKU_USER, HSMPartitionPwd);
if (nRes != 0)
{
LOGGER.Error("Could not login to Session");
}
}
}
private IList<Org.BouncyCastle.X509.X509Certificate> GetKeyChain()
{
//Private key pointers have been installed in
X509Store x509Store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
x509Store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certificates = x509Store.Certificates;
IList<Org.BouncyCastle.X509.X509Certificate> chain = new List<Org.BouncyCastle.X509.X509Certificate>();
X509Certificate2 pk = null;
string HSMCertSerialNumber = ConfigurationManager.AppSettings["HSMCertificateSerialNumber"];
if (certificates.Count > 0)
{
foreach (X509Certificate2 mCert in x509Store.Certificates)
{
//GETS THE KEY WE ARE LOOKING - Private key for HSM
if (String.Compare(mCert.SerialNumber, HSMCertSerialNumber, true) == 0)
{
pk = mCert;
break;
}
}
X509Chain x509chain = new X509Chain();
x509chain.Build(pk);
foreach (X509ChainElement x509ChainElement in x509chain.ChainElements)
{
// Build up root chain
chain.Add(DotNetUtilities.FromX509Certificate(x509ChainElement.Certificate));
}
}
x509Store.Close();
return chain;
}
public bool ApplySignatureDetails(Session session, String alias, Stream inputFile, string tempWorkingFile)
{
IList<Org.BouncyCastle.X509.X509Certificate> chain = GetKeyChain();
// creates a TSA client(Time Stampingserver)
ITSAClient timeStampingClient = new TSAClientBouncyCastle(TIME_STAMPING_SERVER_URL, TIME_STAMPING_SERVER_ACCOUNT, TIME_STAMPING_SERVER_PASSWORD);
IOcspClient ocspClient = new OcspClientBouncyCastle(); // Online Certificate Status Protocol - adobe requirement
List<ICrlClient> crlList = new List<ICrlClient>(); //revocation list - adobe requirement
crlList.Add(new CrlClientOnline(chain));
return PKCS11Signer.Sign(inputFile, String.Format(tempWorkingFile, alias), chain, session, alias, DigestAlgorithms.SHA256, CryptoStandard.CMS, "Doc Signed", "My Company", crlList, ocspClient, timeStampingClient, 0);
}
void IDisposable.Dispose()
{
if (session != null)
{
session.Logout();
session.Close();
session = null;
}
if (cryptoki != null)
{
cryptoki.Finalize(IntPtr.Zero);
cryptoki = null;
}
}
}
3.) PKCS11Signer Class
class PKCS11Signer
{
private static NLogger LOGGER = new NLogger();
private static Semaphore Bouncer { get; set; }
public static bool Sign(Stream inputFile, String dest, ICollection<Org.BouncyCastle.X509.X509Certificate> chain, Session session, String alias,
String digestAlgorithm, CryptoStandard subfilter, String reason, String location,
ICollection<ICrlClient> crlList, IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize)
{
// Creating the reader and the stamper
PdfReader reader = null;
PdfStamper stamper = null;
FileStream outputStream = null;
try
{
if (Bouncer == null)
{
Bouncer = new Semaphore(1, 1);
}
Bouncer.WaitOne();
LOGGER.Info("Start Actual Signing");
reader = new PdfReader(inputFile);
outputStream = new FileStream(dest, FileMode.Create, FileAccess.Write);
stamper = PdfStamper.CreateSignature(reader, outputStream, '\0');
LOGGER.Info("PDFStamper Created");
// Creating the appearance
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
LOGGER.Info("PdfSignatureAppearance Created");
appearance.Reason = reason;
appearance.Location = location;
//Uncomment to show signiture text
//appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(56, 128, 164, 160), 1, "signature");
appearance.CertificationLevel = PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED;
LOGGER.Info("PdfSignatureAppearance Created");
// Creating the signature
IExternalSignature pks = new PrivateKeySignature(session, alias);
LOGGER.Info("Encryption algorithm " + pks.GetEncryptionAlgorithm());
LOGGER.Info("Hash algorithm " + pks.GetHashAlgorithm());
LOGGER.Info("Hash code " + pks.GetHashCode());
LOGGER.Info("pks : " + pks.ToString());
LOGGER.Info("IExternalSignature Created perform sign");
MakeSignature.SignDetached(appearance, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
//System.Threading.Thread.Sleep(1000); //Globalsign requirement
LOGGER.Info("MakeSignature.SignDetached Completed");
}
catch (Exception ex)
{
LOGGER.Error(ex);
throw ex;
}
finally
{
Bouncer.Release();
if (stamper != null)
stamper.Close();
if (reader != null)
reader.Close();
if (outputStream != null)
outputStream.Close();
if (inputFile != null)
inputFile.Close();
}
return true;
}
}
}
4.) PrivateKeySignature Class
class PrivateKeySignature : IExternalSignature
{
private static NLogger LOGGER = new NLogger();
private static ObjectCache cache = MemoryCache.Default;
private readonly Session session;
RSAPrivateKey privateKey;
public PrivateKeySignature(Session session, String alias)
{
string HSMPrivateKeyCertReq = ConfigurationManager.AppSettings["HSMPrivateKeyCertReq"];
this.session = session;
LOGGER.Info("Starting to get PrivateKeySignature");
CryptokiCollection pkobjects = findTarget(session, HSMPrivateKeyCertReq);
LOGGER.Info("Search session for private key using template");
foreach(var keyObject in pkobjects)
{
LOGGER.Info("RSA/CertReq:" + HSMPrivateKeyCertReq);
privateKey = (RSAPrivateKey)keyObject;
}
if (privateKey == null)
{
throw new Exception("privateKey not found");
}
LOGGER.Info("Private Key Found");
}
public String GetHashAlgorithm()
{
return "SHA1";
}
public String GetEncryptionAlgorithm()
{
return "RSA";
}
public byte[] Sign(byte[] message)
{
LOGGER.Info("Start apply Private Key");
session.SignInit(Mechanism.SHA1_RSA_PKCS, privateKey);
LOGGER.Info("End apply Private Key");
return session.Sign(message);
}
public CryptokiCollection findTarget(Session session, string label)
{
if (cache.Contains(label))
{
return (CryptokiCollection)cache.Get(label);
}
else
{
CryptokiCollection template = new CryptokiCollection();
template.Add(new ObjectAttribute(ObjectAttribute.CKA_LABEL, label));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_CLASS, CryptokiObject.CKO_PRIVATE_KEY));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_KEY_TYPE, Key.CKK_RSA));
var pk = session.Objects.Find(template, 1);
// Store data in the cache
CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
cacheItemPolicy.SlidingExpiration = TimeSpan.FromHours(4);
cache.Add(label, pk, cacheItemPolicy);
return pk;
}
}
}
}