Signing a PDF on JS and inserting a signature on C # using Krypto PRO

 3r33434. 3r3-31. So. The task came. Using a browser, invite the user to sign a PDF with an electronic signature (hereinafter referred to as ES) The user must have a token containing a certificate, public and private key. Next on the server you need to insert a signature in the PDF document. After that, you need to check the signature for validity. We use ASP.NET and C # as the back-end.
 3r33434.
 3r33434. All salt in that it is necessary to use the signature in the CAdES-X Long Type 1 format, and the Russian GOST R ???-200? GOST R ???-201? etc. In addition, there can be more than one signatures, that is, users can sign a file in turn. At the same time, the previous signatures must remain valid.
 3r33434.
3r33430.
 3r33434. In the process of solving the problem, we decided to make it more difficult for us to reduce the amount of transmitted data to the client. Pass only the hash of the document, but not the document itself.
 3r33434.
 3r33434. In the source code I will omit moments of little significance for the topic, leaving only that with regard to cryptography. I will give the code on JS only for normal browsers, the JS-engines of which support Promise and function generator. I think someone needs for IE will write themselves (I had to "through I do not want").
 3r33434.
 3r33434. What you need:
 3r33434.
 3r33434.
 3r33434.
The user must obtain a pair of keys and a certificate.
 3r33434.
The user must install the plug-in from Crypto PRO. Without this, using JS, we will not be able to work with a cryptographic provider.
 3r33434.
 3r33434. Remarks: 3r343431.  3r33434.
 3r33434.
 3r33434.
For tests, I had a certificate issued by a test CA of Crypto PRO and a normal token received by one of our employees (at the time of writing the article ~ 1500r with an annual license for Crypto PRO and two certificates: but “new” and “old” GOST) 3r3453.  3r33434.
They say that the plug-in can work with ViPNet, but I have not tested it.
 3r33434.
 3r33434. Now we will assume that we have a PDF ready for signing on the server.
 3r33434. Add a script from Crypto PRO to the page:
 3r33434.
 3r33434. 3r33333.
3r33434. 3r33393. 3r33394.
 3r33434. Next, we need to wait until the cadesplugin object
is formed.  3r33434.
 3r33434. 3r33333. window.cadespluginLoaded = false; 3r33434. cadesplugin.then (function () {3r33434. window.cadespluginLoaded = true;
}); 3r33434. 3r33393. 3r33394.
 3r33434. We request from the server hash. For this, we still need to know what certificate, and therefore the user will sign the algorithm. A small remark: I combined all the functions and "variables" for working with cryptography on the client side into a CryptographyObject object.
 3r33434.
 3r33434. CryptographyObject:
method for filling the certificates field.  3r33434.
 3r33434. 3r33333. fillCertificates: function (failCallback) {
3r33434. cadeshpas cadesplugin. i <= certsCount; i++) {
let cert = yield certs.Item (i);
CryptographyObject.certificates.push (cert);
}
oStore.Close (); 3r33434.} catch (exc) {
failCallback (exc); 3r33434.}
}); 3r33434.}
3r33393. 3r33394.
 3r33434. Comment: try to open the certificate store. At this point, the user's system will give a warning that the site is trying to do something with certificates, cryptography, and other magical incomprehensible nonsense. The user here will need to click the "Yes" button 3r3431.  3r33434. Next, we get certificates that are valid in time (not expired) and add them to the array of certificates. This must be done because of the asynchronous nature of the cadesplugin (for IE, everything is different;)).
 3r33434.
 3r33434. Method to get hash:
 3r33434.
 3r33434. 3r33333. getHash: function (certIndex, successCallback, failCallback, some other parameters) {
try {
cadesplugin.async_spawn (function * () {
let cert = CryptographyObject.certificates[certIndex];
let certPublicKey = yield cert.PublicKey ();.
let certAlgorithm = yield certPublicKey.Algorithm;
let certAlgorithmFriendlyName = yield certAlgorithm FriendlyName;
Let hashAlgorithm;
//define the signing algorithm according to the data from the certificate and get the hash algorithm 3r3-33445. If (certAlgorithmFriendlyName.match (/??? /i))
HashAlgorithm = "20125 zartr certAlgorithmFriendlyName.match (/??? /i))
hashAlgorithm = "2012256";
else if (certAlgorithmFriendlyName.match (/2001 /i))
hashAlgorithm = "3411"; 3r3 r3445. else {
failCallback ();
return; 3r33434.}
3r33434. $ .ajax ({
url: "/Services/SignService.asmx/GetHash",
method: "POST",
contentType: "application /json; charset = utf-8",
dataType: "json ", 3r33434. dаta: JSON.stringify ({
//some data to define the document
//let's remember to check on the server whether the user has the necessary rights 3r3454. HashAlgorithm: hashAlgorithm,
}),
Complete : function (response) {
//get a response from the server, sign and send a signature to the server 3r33434. if (response.status === 200) {
CryptographyObject.signHash (response.responseJSON,
Function (data) {
$ .Ajax ({3r3343445. url: CryptographyObject.signServiceUrl,
method: "POST",
contentType: "application /json; charset = utf-8",
dataType: "json",
dаta: JSON.stringify ({
Signature: data.Signature,
//some data for defining the file
//let's not forget about server validation and authorization 3r33445.}),
complete: function (response) {
if (response.status === 200)
successCallback (); 3r33434. else
failCallback (); 3r33434.}
}); 3r33434.}, 3r33445. certIndex); 3r33434.} else {
failCallback (); 3r33434.}
}
}); 3r33434.}); 3r33434.} catch (exc) {
failCallback (exc); 3r33434.}
}
3r33393. 3r33394.
 3r33434. Comment: pay attention to cadesplugin.async_spawn, the generator function is passed to it, where next () is sequentially called, which leads to the transition to yield.
 3r33434. Thus, it turns out a certain analogue of async-await from C #. Everything looks synchronous, but it works asynchronously.
 3r33434.
 3r33434. Now what happens on the server when hash is requested.
 3r33434.
 3r33434. Firstly, you need to install the iTextSharp nuget-package (at the time of writing to become the current version ???) 3r3431.  3r33434.
 3r33434. Secondly, CryptoPro.Sharpei is needed, it goes to work with the Crypto PRO .NET SDK
 3r33434.
 3r33434. Now you can get hash
 3r33434.
 3r33434. 3r33333. //define the hash algorithm
HashAlgorithm hashAlgorithm; 3r33434. 3r33434. switch (hashAlgorithmName)
{
case "3411":
hashAlgorithm = new Gost3411CryptoServiceProvider (); 3r33434. break; 3r33434. case "2012256":
hashAlgorithm = new Gost3411_2012_256CryptoServiceProvider (); 3r33434. break; 3r33434. case "2012512":
hashAlgorithm = new Gost3411_2012_512CryptoServiceProvider (); 3r33434. break; 3r33434. default:
GetLogger (). AddError ("Unknown Hash Algorithm", $ "hashAlgorithmName: {hashAlgorithmName}"); 3r33434. return HttpStatusCode.BadRequest; 3r33434.}
//get hash in string representation, understandable cadesplugin
string hash; 3r33434. using (hashAlgorithm)
//downloadResponse.RawBytes is simply an array of bytes of the source PDF file
using (PdfReader reader = new PdfReader (downloadResponse.RawBytes))
{
//look for existing signatures
int existingSignaturesNumber = reader.AcroFields.GetSignatureNames (). Count; 3r33434. using (MemoryStream stream = new MemoryStream ())
{
//add an empty container for a new signature
using (PdfStamper st = PdfStamper.CreateSignature (reader, stream, 'how much space in bytes we allocate for the signature
//I select a lot, because CAdES-X Long Type 1 contains all the certificates in the chain up to the root center of 3r-3445. MakeSignature.SignExternalContainer (appearance, external, 65536); 3r33434. //get the stream that contains the sequence we want to sign
using (Stream contentStream = appearance.GetRangeStream ())
{
//calculate hash and translate it into a string that is understandable by cadesplugin
hash = string.Join (string.Empty,
hashAlgorithm.ComputeHash (contentStream) .Select (x => x.ToString ("X2"))); 3r33434.}
}
//save the stream where we want, it will be useful to us to insert the signature
there.}
}
3r33393. 3r33394.
 3r33434. On the client, having received hash from the server we sign it
 3r33434.
 3r33434. 3r33333. //certIndex - index in the array of certificates. Based on this particular certificate, we received an algorithm and formed hash on the server 3r-33445. signHash: function (data, callback, certIndex, failCallback) {
try {
cadesplugin.async_spawn (function * () {
certIndex = certIndex | 0;
let oSigner = yield cadesplugin.CreateObjectAsync ("CAdESCOM.CPSigner"; 3r3rftd.com Idigretry idignt.com Iddkartinynx.com Iddk)
OSigner.propset_Certificate (cert);
d.d.d.а.ааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааry our 3d3r3445.app.dex.ca. .cryptopro.ru /tsp /"); 3r33434.
let hashObject = our server, and 3r3454. .Algorithm;
let certAlgorithmFriendlyName = yield certAlgorithm.FriendlyName; 3r33434. 3r33434. if (certAlgorithmFriendlyName.match (/??? /i)) {
yield hashObject.propset_Algorithm (cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_512); 3r33434.} else if (certAlgorithmFriendlyName.match (/??? /i)) {
yield hashObject.propset_Algorithm (cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411_2012_256); 3r33434.} else if (certAlgorithmFriendlyName.match (/2001 /i)) {
yield hashObject.propset_Algorithm (cadesplugin.CADESCOM_HASH_ALGORITHM_CP_GOST_3411); 3r33434.} else {
alert ("Unable to sign a document with this certificate"); 3r33434. return; 3r33434.}
//in the hash description object, insert the already-made hash from the server
yield hashObject.SetHashValue (data.Hash); 3r33434. 3r33434. let oSignedData = yield cadesplugin.CreateObjectAsync ("CAdESCOM.CadesSignedData"); 3r33434. oSignedData.propset_ContentEncoding (cadesplugin.CADESCOM_BASE64_TO_BINARY); 3r33434. //the result of signing on base64
let signatureHex =
yield oSignedData.SignHash (hashObject, oSigner, cadesplugin.CADESCOM_CADES_X_LONG_TYPE_1); 3r33434. 3r33434. data.Signature = signatureHex; 3r33434. callback (data); 3r33434.}); 3r33434.} catch (exc) {
failCallback (exc); 3r33434.}
}
3r33393. 3r33394.
 3r33434. Comment: the received signature is sent to the server (see above)
 3r33434.
 3r33434. Well, finally, insert the signature into the document on the server side
 3r33434.
 3r33434. 3r33333. 3r33434. //all necessary checks
//downloadResponse.RawBytes - a previously created PDF with an empty signature container
using (PdfReader reader = new PdfReader (downloadResponse.RawBytes))
{
using (MemoryStream stream = new MemoryStream ())
{
//requestData.Signature is the signature from client
IExternalSignatureContainer external = new SimpleExternalSignatureContainer (Convert.FromBase64String (requestData.Signature)); 3r33434. //lastSignatureName is the name of the container that we defined during the formation of hash
MakeSignature.SignDeferred (reader, lastSignatureName, stream, external); 3r33434. 3r33434. //save the signed file
}
}
3r33393. 3r33394.
 3r33434. Comment: SimpleExternalSignatureContainer is the simplest class that implements the IExternalSignatureContainer
interface.  3r33434.
 3r33434. 3r33333. /// 3r33434. ///Simple implementation of the external signature container
/// 3r33434. private class SimpleExternalSignatureContainer: IExternalSignatureContainer
{
private readonly byte[]_signedBytes; 3r33434. 3r33434. public SimpleExternalSignatureContainer (byte[]signedBytes)
{
_signedBytes = signedBytes; 3r33434.}
3r33434. public byte[]Sign (Stream data)
{
return _signedBytes; 3r33434.}
3r33434. public void ModifySigningDictionary (PdfDictionary signDic)
{
3r33434.}
}
3r33393. 3r33394.
 3r33434. Actually with the signing of the PDF on it all. The check will be described in the continuation of the article. I hope she will be
 3r33434.
 3r33434. 3r3013. 3r3402. Sources of Inspiration [/b] 3r3404. 3r3405. www.cryptopro.ru/sites/default/files/products/cades/demopage/cades_xlong_sample.html
 3r33434. 3r3409. cpdn.cryptopro.ru/content/cades/plugin-activation.html
 3r33434. 3r33434. www.cryptopro.ru/forum2/default.aspx?g=posts&t=11119
 3r33434. 3r33417. www.cryptopro.ru/forum2/default.aspx?g=posts&t=3691&p=21
 3r33434. 3r33421. cpdn.cryptopro.ru/default.asp?url=content/cades/plugin-samples-raw-signature.html
 3r33434. 3r33434. cpdn.cryptopro.ru/default.asp?url=content/cades/plugin.html
 3r33434. 3r33434. itextsupport.com/apidocs/itext5/???/com/itextpdf/text/pdf/PdfStamper.html#createSignature-com.itextpdf.text.pdf.PdfReader-java.io.OutputStream-char-java.io.File- boolean-
 3r33434.
3r33434. 3r33434.
3r33434.
3r33434. 3r33434. 3r33434. 3r33434.
+ 0 -

Add comment