How to use Java produce Signature by USBKey under CryptoAPI/CSP

本文介绍如何使用Java通过JNI调用Microsoft CryptoAPI来生成数字签名,涉及USB-KEY等硬件令牌的使用。讨论了Java开发人员面临的挑战及解决方案,包括密钥管理和签名过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Perhaps someone need to use USB-KEY or other Hardware Token to generate Digital Signature , through Microsoft CryptoAPI.  Perhaps MS CryptoAPI is the only way for us to access Cryptography Device such as USB-Key. It is sure not a comfortable way because Java developers have to call CAPI funtions throught JNI(Java Native Interface). So there are some java-library to CALL CryptoAPI, but they are not free.

I hope to provide an OpenSource Java Library to do this thing : SecureX[https://sourceforge.net/projects/securex]
Here is some demo of what SecureX Library could do:
1, SecureX Library Arichtecture Demo
http://dev2dev.bea.com.cn/bbs/servlet/D2DServlet/download/29304-31620-211417-3031/securex.swf
2, SecureX USB-Key Demo
http://dev2dev.bea.com.cn/bbs/servlet/D2DServlet/download/29304-31620-213693-3060/HNISI_SecureX_USBKey.swf

OK, Came back to our topic, how to use java call CryptoAPI to produce signature.
You should know at least :
1, CryptoAPI are just a set of interface define by MS, and USB-Key Vendor just implement these interface so that our application can call the usb key to do some cryptographic operations(eg Signature, Hash, Encryption). There are a lot of CSPs located in your windows system. CSP is implementation,  but we need not care about it, All we care is what CryptoAPI could do. See MSDN for more information.
2, For Java developer, they should use JNI to access CryptoAPI but it is not an easy thing since there are some encoding difference between JDK and Windows. For example, they should know how to convert the binary Private key stream to Java PrivateKey Object.
3, Perhaps some USB-Key vendor provide PKCS#11 CSP other than CryptoAPI CSP. PKCS# CSP is  a RSA Standard [http://www.rsasecurity.com/rsalabs/node.asp?id=2133], It will be a good optional implement instead of CryptoAPI CSP.

Back to CryptoAPI CSP:

Java developer should do such a thing to generate a signature:

None.gif byte [] data  =   " http://openssl.blogjava.net " .getBytes();
None.gifSignatureUtils sigutil
= new  SignatureUtils( " MD5 " );
None.gifsigutil.initSign(privateKey);
None.gifsigutil.update(data,
0 ,data.length);
None.gif
byte [] signature  =  sigutil.sign();
None.gif
None.gifsigutil.initVerify(publicKey);
None.gifsigutil.update(data,
0 ,data.length);
None.gif
if ( ! sigutil.verify(signature))
None.gif    System.out.println(
" The signature verification failed. " );
None.gif
else
None.gif    System.out.println(
" The signature was successfully verified. " );

before we sign, we should provide a privatekey,  in the java world, private key is stored in JKS file(Java Keystore), we could get the keyEntry out through:

None.gif keyStoreStream  =   new  FileInputStream(keyStoreFilename);
None.gifkeystore 
=  KeyStore.getInstance(KeyStore.getDefaultType());
None.gifkeystore.load(keyStoreStream, keyStorePassword.toCharArray());
None.gifKey key 
=  keystore.getKey(alias, keypassword);
None.gif
if  (key  instanceof  PrivateKey)
None.gif    
return  (PrivateKey)key;

But on windows,  private key is not stored in JKS, they stored in Windows Local CertStore or in USB-Key,and in most cases, private key are not allowed to Export!

In CryptoAPI's world, you should do the following things.

None.gif call CryptAcquireContext get HCRYPTPROV handle, also known as a csp handle
None.gifcall CryptCreateHash to hash your data
None.gifcall CryptSignHash to sign
None.gif
None.gifCryptAcquireContext need Key container name 、CSP Name、CSP type、dwFlags。
None.gifCryptCreateHash need hash agorithm
None.gifCryptSignHash need dwKeySpec,right! it's the private key spec.

So, Java developer feel boring when he need to provide the private key.
The proper way is:
1,  Use Alias to get the privatekey:
     (a) if the private key is exportable, we can get it and change it to a Java Object
     (b) if the private key is not exportable, we get the private key handle.
2,  Sign the Hash
      (a) if the private key is exportable,  sigutil.initSign(privateKey) would do this job.
      (b) if the private key is not exportable, we pass the private key handle to CSP, 
and let CSP get the privatekey internally.
    
The program below is running under securex, and it can get the privatekey from
usb-key, and sign the data:
ExpandedBlockStart.gif ContractedBlock.gif      /** */ /**
InBlock.gif     * 签名,并将XML签名结果保存到signatureFile中
InBlock.gif     * 需要3个条件
InBlock.gif     * 1,KeyAlias,用于获取私钥
InBlock.gif     * 2,source_filename_to_be_signed,确定被签名的数据源
InBlock.gif     * 3,签名算法
InBlock.gif     * 
@param save_signatureFile
ExpandedBlockEnd.gif     
*/

None.gif    
public   static   void  sign()
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
byte abyte0[][];
InBlock.gif        
InBlock.gif        
if((abyte0 = CorKeyStoreJNI.getKey(getKeyAlias())) == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            System.err.println(
"这是一条不可导出的钥匙!");
InBlock.gif            
return;
ExpandedSubBlockEnd.gif        }

InBlock.gif        
InBlock.gif        
if(abyte0.length == 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            JCAPIRSAPrivateKey jcapikey 
=new JCAPIRSAPrivateKey(getKeyAlias().getBytes());
InBlock.gif            System.out.println(jcapikey.getAlgorithm()
+":"+jcapikey.getPrivateExponent());
InBlock.gif        
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/** *//**
InBlock.gif             * 签名数据
ExpandedSubBlockEnd.gif             
*/

InBlock.gif            File sourcefile
=new File(source_filename_to_be_signed);
InBlock.gif            
byte[] data =null
ExpandedSubBlockStart.gifContractedSubBlock.gif            
try dot.gif{
InBlock.gif                    data
= FileUtils.getBytesFromFile(sourcefile);
ExpandedSubBlockStart.gifContractedSubBlock.gif            }
 catch (IOException e1) dot.gif{
InBlock.gif                    e1.printStackTrace();
ExpandedSubBlockEnd.gif            }

InBlock.gif            
InBlock.gif            
byte[] signature=null;
InBlock.gif            
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/** *//**
InBlock.gif             * 产生签名
InBlock.gif             * TODO, 使用正确的签名算法,比如MD5withRSA->MD5
ExpandedSubBlockEnd.gif             
*/

InBlock.gif            SignatureUtils sigutil
=new SignatureUtils(getSignAgorithm());
ExpandedSubBlockStart.gifContractedSubBlock.gif            
try dot.gif{
InBlock.gif                sigutil.initSign(jcapikey);
InBlock.gif                sigutil.update(data,
0,data.length);
InBlock.gif                signature 
= sigutil.sign();
InBlock.gif                System.out.println(
"signature>>>dot.gif.."+new String(signature));
InBlock.gif                setRawSignature(signature);
InBlock.gif
ExpandedSubBlockStart.gifContractedSubBlock.gif            }
 catch (InvalidKeyException e) dot.gif{
InBlock.gif                e.printStackTrace();
ExpandedSubBlockStart.gifContractedSubBlock.gif            }
 catch (SignatureException e) dot.gif{
InBlock.gif                e.printStackTrace();
ExpandedSubBlockEnd.gif            }

InBlock.gif            
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/** *//**
InBlock.gif             * 保存签名到save_signatureFile
InBlock.gif             * 也就是sign的参数
ExpandedSubBlockEnd.gif             
*/

ExpandedSubBlockStart.gifContractedSubBlock.gif            
try dot.gif{
InBlock.gif                FileUtils.writeFile(getSaveSignatureFile(),signature);
ExpandedSubBlockStart.gifContractedSubBlock.gif            }
 catch (IOException e) dot.gif{
InBlock.gif                e.printStackTrace();
ExpandedSubBlockEnd.gif            }

InBlock.gif            
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

How's it done?
1) Well, Suppose I USE an USB E-Key(CSP Vendor :吉大正元www.jit.com.cn)
I know my PrivateKey Alias is  ,  Locate In "My"
I get the private key through CoreKeyStoreJNI Class which has native jni method( getKey), by which I could
tell the csp which private key i want to use!
2) I get the file to byteArray which must be Hash before sign, because for Microsoft CAPI,  its signobject can accept hash object only.
SignatureUtils sigutil = new  SignatureUtils(getSignAgorithm());
the getSignAgorithm return "MD5withRSA" "SHA1withRSA" for most cases. It meas MD5 data before RSA Sign :)
3) when the
None.gif sigutil.sign();
is Excute, a native PIN-CallBack Windows is open, it will ask you for private key protected password(you can see it in my swf demo above).
type the correct PIN , My JIT CSP will call the correct sign cryptographic operations through the USB EKey drivers.
All the cryptographic operations(Hash, Sign) are performed on the USB-Key INTERNALLY, NOT by the KEY Drivers.
OK,then CSP get the signature and Signature Verify use only the Public Key and need not access the private key, It can perform by CSP or verify using JCE, as you like.
4) Haha, I've not yet told you that you need a DLL to Load before you call these API. Wait till I put my whole project to sourceforege SecureX( http://securex.sourceforge.net/). Any Advice, please contact me on this blog or just join the QQ Group: 14966586
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值