﻿// RetCodes
var CRYPT_OK =               0;        
var CRYPT_EMPTY_DATA =       1;
var CRYPT_OPEN_STORAGE_ERR = 2;
var CRYPT_NO_CERTS =         3;
var CRYPT_CERT_NOT_FOUND =   4;
var CRYPT_SIGN_OP_ERR    =   5;
var CRYPT_SIGN_ERR       =   6;
var CRYPT_CERT_DATE_INVALID= 7;
var CRYPT_VER_SIGN_ERR   =   8;
var CRYPT_CERT_DEC_ERR   =   9;
var CRYPT_SIGN_DEC_ERR   =   10;
var CRYPT_CERT_CHAIN_ERR =   11;
var CRYPT_CERT_WAS_REVOKED = 12;
var CRYPT_INVALID_RSA_SIG  = 13;

// Consts
var CAPICOM_CURRENT_USER_STORE=2, CAPICOM_STORE_OPEN_READ_ONLY=0,
    CAPICOM_ENCODE_BINARY=1; CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME=0,
    CAPICOM_CERTIFICATE_FIND_SHA1_HASH=0,
    CAPICOM_CERTIFICATE_FIND_ROOT_NAME = 3,
    CAPICOM_CERTIFICATE_FIND_CERTIFICATE_POLICY = 8, 
    CAPICOM_ENCODE_BASE64 = 0,
    CAPICOM_CHECK_TIME_VALIDITY=2, CAPICOM_CHECK_NESTED_VALIDITY_PERIOD=100,
    CAPICOM_VERIFY_SIGNATURE_ONLY=0,CAPICOM_VERIFY_SIGNATURE_AND_CERTIFICATE=1,
    CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY=2;
    CAPICOM_HASH_ALGORITHM_MD5 = 3,
    CAPICOM_CHECK_ONLINE_REVOCATION_STATUS = 8;



navigator.plugins.refresh();
var MyStore=new CreateObject("CAPICOM.Store");
var SignedData =new CreateObject("CAPICOM.SignedData");
var Signer = new CreateObject("CAPICOM.Signer");
var Chain = new CreateObject("CAPICOM.Chain");
var Utilities = new CreateObject("CAPICOM.Utilities");

var CUR_USER_CERT_NAME="";       // Must be stored in cookies (up to 40 chars)
var CUR_USER_CERT_ID="";         // Must be stored in cookies (20 chars)
var CUR_SIGNATURE="";            // Last digital signature (base64)
var CUR_CERTIFICATE="";          // Last used certificate (base64)
var SRV_SIGN_CERT="";
var CUR_CERT_EXP_IN=-1;          // Certificate expires in, days


var MILLISECS_IN_DAY=86400000;
var CERT_EXP_WARN_BEFORE=30;     // Certificate expiration warning before, days

var detachSign;

function CreateObject(objname)
{
    if (window.ActiveXObject) return new ActiveXObject(objname);
    if (window.GeckoActiveXObject) return new GeckoActiveXObject(objname);
    alert(SupportedOnlyIEAndNS);
}

function GetDataHash(value)
{
  var HashedData = new CreateObject("CAPICOM.HashedData");
  HashedData.Algorithm = CAPICOM_HASH_ALGORITHM_MD5;
  HashedData.Hash(value);
  var result = HashedData.Value;
  HashedData = null;
  return result;
}

function CryptGetCertFromStore (cert_name, cert_id, store_name)
{
    var idx=1, ccnt=1;
    var certs;
    var fndcert=false;
    CUR_CERT_EXP_IN=-1;
    var res=CRYPT_CERT_NOT_FOUND;
    try
    {
        if (Signer.Certificate.Thumbprint==cert_name) return CRYPT_OK;
    } 
    catch (e) {} 
    try
    {
        MyStore.Open (CAPICOM_CURRENT_USER_STORE, store_name, CAPICOM_STORE_OPEN_READ_ONLY);
        ccnt=MyStore.Certificates.Count;
    } 
    catch (e) {return CRYPT_OPEN_STORAGE_ERR;}  
	try
	{
	    
		certs=MyStore.Certificates;
		if (cert_name!="")
		{
			certs=MyStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SHA1_HASH, cert_name);
			if (certs==null || certs.Count==0) throw (0);
			idx=1; fndcert=true;
		}
	}
	catch (e) {return CRYPT_CERT_NOT_FOUND;} 
    try
    {
        if (!fndcert && cert_id!="")
        {
            for (idx=1; idx<=ccnt; idx++)
            {
                var cert=certs.Item(idx);    
                if (cert.SerialNumber==cert_id) 
                {
                    fndcert=true; 
                    break; 
                }
            }    
        }
    }catch (e) {} 
    try
    {
        if (fndcert && certs.Count!=0) 
        {        
            Signer.Certificate = certs.Item(idx);
            Signer.Certificate.IsValid().CheckFlag = 
                CAPICOM_CHECK_TIME_VALIDITY | CAPICOM_CHECK_NESTED_VALIDITY_PERIOD; // | CAPICOM_CHECK_ONLINE_REVOCATION_STATUS;   
            if (!Signer.Certificate.IsValid().Result)
                return CRYPT_CERT_DATE_INVALID; 
            CUR_USER_CERT_NAME=Signer.Certificate.Thumbprint;
            CUR_USER_CERT_ID=Signer.Certificate.SerialNumber;
            CUR_CERTIFICATE=Signer.Certificate.Export(CAPICOM_ENCODE_BASE64);

            var basedt=new Date("01/01/1900"); basetm=Math.abs(basedt.getTime());
            var cdt=new Date(), cday=cdt.getDate(), bexp=false;
            for (i=CERT_EXP_WARN_BEFORE; i>=0;  i--)
            {
                cdt=new Date();
                cdt.setDate(cday+i);    
                var dtdiff=(basetm+cdt.getTime())/MILLISECS_IN_DAY+2;   
                Signer.Certificate.IsValid().VerificationTime=dtdiff;
                if (Signer.Certificate.IsValid().Result) break;
                bexp=true;
            }
            if (bexp) CUR_CERT_EXP_IN=i+1; 
            res=CRYPT_OK;
        }
    }catch (e) {} 
    return res;
}

function Base64ToHex(str) {
	var result = "";
	var i = 0;
	var x;
	var shiftreg = 0;
	var count = -1;
	
	for (i=0; i < str.length; i++) {
		c = str.charAt(i);
		if ('A' <= c && c <= 'Z')
			x = str.charCodeAt(i) - 65;
		else if ('a' <= c && c <= 'z')
			x = str.charCodeAt(i) - 97 + 26;
		else if ('0' <= c && c <= '9')
			x = str.charCodeAt(i) - 48 + 52;
		else if (c == '+')
			x = 62;
		else if (c == '/')
			x = 63;
		else
			continue;

		count++;

		switch (count % 4)
		{
		case 0:
			shiftreg = x;
			continue;
		case 1:
			v = (shiftreg<<2) | (x >> 4);
			shiftreg = x & 0x0F;
			break;
		case 2:
			v = (shiftreg<<4) | (x >> 2);
			shiftreg = x & 0x03;
			break;
		case 3:
			v = (shiftreg<<6) | (x >> 0);
			shiftreg = x & 0x00;
			break;
		}
	       sv=v.toString(16);
	       if (sv.length<2 && sv!="") sv="0"+sv;
               result=result+sv;

	}	
	return result.toString();
}

function HexToBinStr(d) 
{
    var o = '';
    for (var i=0; i<d.length; i=i+4) o+=String.fromCharCode(eval('0x'+(d.substring(i+2,i+4)+d.substring(i,i+2)).toString(16)));
    return o;
}


//---------------------- Crypto API -------------------------------------

function CryptExplainErrCode(errcode)
{
    if (Math.abs(errcode)<ErrExp.length) 
        return ErrExp[errcode];
    else 
        return errcode;
}

function CryptGetCert(cert_name, cert_id)
{
    return CryptGetCertFromStore(cert_name, cert_id, "MY");
}

function CryptSign(data, cert_name, cert_id)
{
    var res="";
    CUR_SIGNATURE="";
    if (data==null || data=="")
    { 
        alert(ERROR_DataForTheSignatureAreNotSpecified);
        throw (0);
    }
    if ( (res=CryptGetCert(cert_name, cert_id))!=CRYPT_OK ) 
    {
        alert(ERROR_TheCertificateForTheSignatureIsNotFound);
        throw (0);
    }
    SignedData.Content = data;  
    Signer.Options=CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY;
    try
    {
        CUR_SIGNATURE = SignedData.Sign(Signer, detachSign, CAPICOM_ENCODE_BASE64); 
        res = CUR_SIGNATURE;
    } 
    catch (e) 
    {
        alert(ERROR_ErrorOfSignature);
        throw(0);
    }
    return res;
}

function CryptCoSign (cert_name, cert_id)
{    
    var res = "";
    CUR_SIGNATURE="";
    if (SignedData.Content==null || SignedData.Content=="")
    { 
        alert(ERROR_CheckSignatureError);
        throw (0);
    }
    if ((res=CryptGetCert(cert_name, cert_id))!=CRYPT_OK)
    { 
        alert(ERROR_TheCertificateForTheSignatureIsNotFound);
        throw (0); 
    }
    Signer.Options=CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY;
    try
    {
        CUR_SIGNATURE = SignedData.CoSign(Signer, CAPICOM_ENCODE_BASE64);
        res = CUR_SIGNATURE;
    }
    catch (e) 
    {
        alert(ERROR_ErrorOfSignature);
        throw(0);
    }
    return res;
}

function CryptVerifyAttached(pkcs7msg,data)
{
    var res=CRYPT_SIGN_ERR;
    try
    {
        MyStore.Open(CAPICOM_CURRENT_USER_STORE, "MY", CAPICOM_STORE_OPEN_READ_ONLY);
        if (detachSign)
            SignedData.Content = data;
    	SignedData.Verify(pkcs7msg, detachSign, CAPICOM_VERIFY_SIGNATURE_AND_CERTIFICATE);
    }
    catch (e)
    {
        alert(ERROR_CheckSignatureError);
        throw (0);
    }
    return SignedData.Content;
}