如何仅使用 Javascript 创建数字签名解决方案

2021-09-22 16:52:43 浏览数 (2786)

有时需要让用户签署文本以证明他是完成操作的人。例如,在电子银行软件中,用户可能必须签署描述交易的文本(“向 IBAN xxxxxxxxx 转账 300 美元”),或签署政府电子服务请求。这可以通过 java-applet 来实现,但由于 JRE 所有者不是大多数,因此最好使用其他方式。

当然,前提是用户有CA签发的数字签名,并按照CA的手册在浏览器中安装证书。如果这些步骤没有成功完成,下面的解决方案将不起作用。

另请注意,这使用 PKCS7(Java 开发人员:使用充气城堡来验证它),而不是 XAdES 标准。Internet Explorer 支持 XAdES,但 FireFox 不支持。

让我们看一个应该对给定文本进行签名的简单 HTML 页面:

<script src="sign.js" type="text/javascript"></script>
 
<input id="text" type="text" />
<input onclick="signDigest(document.getElementById('text').value);" type="button" value="Sign" />

然后是 JavaScript 本身:

function signDigest(text)
{
if(window.event)
window.event.cancelBubble =true;
 
var dest = sign(text);//TODO
alert(dest);
return dest;
}
 
// CAPICOM constants
var CAPICOM_STORE_OPEN_READ_ONLY = 0;
var CAPICOM_CURRENT_USER_STORE = 2;
var CAPICOM_CERTIFICATE_FIND_SHA1_HASH = 0;
var CAPICOM_CERTIFICATE_FIND_EXTENDED_PROPERTY = 6;
var CAPICOM_CERTIFICATE_FIND_TIME_VALID = 9;
var CAPICOM_CERTIFICATE_FIND_KEY_USAGE = 12;
var CAPICOM_DIGITAL_SIGNATURE_KEY_USAGE = 0x00000080;
var CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME = 0;
var CAPICOM_INFO_SUBJECT_SIMPLE_NAME = 0;
var CAPICOM_ENCODE_BASE64 = 0;
var CAPICOM_E_CANCELLED = -2138568446;
var CERT_KEY_SPEC_PROP_ID = 6;
 
function IsCAPICOMInstalled()
{
if(typeof(oCAPICOM) =="object")
{
if( (oCAPICOM.object !=null) )
{
// We found CAPICOM!
return true;
}
}
}
 
function FindCertificateByHash()
{
 
try
{
// instantiate the CAPICOM objects
var MyStore =new ActiveXObject("CAPICOM.Store");
// open the current users personal certificate store
MyStore.Open(CAPICOM_CURRENT_USER_STORE,"My", CAPICOM_STORE_OPEN_READ_ONLY);
 
// find all of the certificates that have the specified hash
var FilteredCertificates = MyStore.Certificates.Find(CAPICOM_CERTIFICATE_FIND_SHA1_HASH, strUserCertigicateThumbprint);
 
var Signer =new ActiveXObject("CAPICOM.Signer");
Signer.Certificate = FilteredCertificates.Item(1);
return Signer;
 
// Clean Up
MyStore =null;
FilteredCertificates =null;
}
catch (e)
{
if (e.number != CAPICOM_E_CANCELLED)
{
return new ActiveXObject("CAPICOM.Signer");
}
}
}
 
function sign(src)
{
if(window.crypto &amp;&amp; window.crypto.signText)
return sign_NS(src);
 
return sign_IE(src);
}
 
function sign_NS(src)
{
var s = crypto.signText(src,"ask" );
return s;
}
 
function sign_IE(src)
{
try
{
// instantiate the CAPICOM objects
var SignedData =new ActiveXObject("CAPICOM.SignedData");
var TimeAttribute =new ActiveXObject("CAPICOM.Attribute");
 
// Set the data that we want to sign
SignedData.Content = src;
var Signer = FindCertificateByHash();
 
// Set the time in which we are applying the signature
var Today =new Date();
TimeAttribute.Name = CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME;
TimeAttribute.Value = Today.getVarDate();
Today =null;
Signer.AuthenticatedAttributes.Add(TimeAttribute);
 
// Do the Sign operation
var szSignature = SignedData.Sign(Signer,true, CAPICOM_ENCODE_BASE64);
return szSignature;
}
catch (e)
{
if (e.number != CAPICOM_E_CANCELLED)
{
alert("An error occurred when attempting to sign the content, the errot was: " + e.description);
}
}
return "";
}

这应该可以解决问题,签名文本可以发送到服务器,在那里可以进行验证。