这是我的做法:
创建新类 CryptoSmartCard,它是 Merlin 的副本并扩展 CryptoBase。新类和 Merlin 的区别在于方法 loadProperties(Properties properties, ClassLoader loader):
public void loadProperties(Properties properties, ClassLoader loader)
throws CredentialException, IOException {
if (properties == null) {
return;
}
this.properties = properties;
//
// Load the provider(s)
//
String provider = properties.getProperty(CRYPTO_KEYSTORE_PROVIDER);
if (provider != null) {
provider = provider.trim();
}
String certProvider = properties.getProperty(CRYPTO_CERT_PROVIDER);
if (certProvider != null) {
setCryptoProvider(certProvider);
}
//
// Load the KeyStore
//
String alias = properties.getProperty(KEYSTORE_ALIAS);
if (alias != null) {
alias = alias.trim();
defaultAlias = alias;
}
String keyStoreLocation = properties.getProperty(KEYSTORE_FILE);
if (keyStoreLocation == null) {
keyStoreLocation = properties.getProperty(OLD_KEYSTORE_FILE);
}
if (keyStoreLocation != null) {
keyStoreLocation = keyStoreLocation.trim();
InputStream is = loadInputStream(loader, keyStoreLocation);
try {
String passwd = properties.getProperty(KEYSTORE_PASSWORD, "security");
if (passwd != null) {
passwd = passwd.trim();
}
String type = properties.getProperty(KEYSTORE_TYPE, KeyStore.getDefaultType());
if (type != null) {
type = type.trim();
}
String pin = properties.getProperty(KEYSTORE_PRIVATE_PASSWORD);
char[] pin_arr = pin.toCharArray();
String pkcs11config = "name = SmartCard\n" + "library = " + properties.getProperty(KEYSTORE_FILE);
byte[] pkcs11configBytes = pkcs11config.getBytes();
ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11configBytes);
Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
try {
Security.addProvider(pkcs11Provider);
keystore = KeyStore.getInstance(type,pkcs11Provider);
keystore.load(null, pin_arr);
} catch (Exception ex) {
// nothing
}
//keystore = load(is, passwd, provider, type);
if (DO_DEBUG) {
LOG.debug(
"The KeyStore " + keyStoreLocation + " of type " + type
+ " has been loaded");
}
String privatePasswd = properties.getProperty(KEYSTORE_PRIVATE_PASSWORD);
if (privatePasswd != null) {
privatePasswordSet = true;
}
} finally {
if (is != null) {
is.close();
}
}
} else {
if (DO_DEBUG) {
LOG.debug("The KeyStore is not loaded as KEYSTORE_FILE is null");
}
}
//
// Load the TrustStore
//
String trustStoreLocation = properties.getProperty(TRUSTSTORE_FILE);
if (trustStoreLocation != null) {
trustStoreLocation = trustStoreLocation.trim();
InputStream is = loadInputStream(loader, trustStoreLocation);
try {
String passwd = properties.getProperty(TRUSTSTORE_PASSWORD, "changeit");
if (passwd != null) {
passwd = passwd.trim();
}
String type = properties.getProperty(TRUSTSTORE_TYPE, KeyStore.getDefaultType());
if (type != null) {
type = type.trim();
}
truststore = load(is, passwd, provider, type);
if (DO_DEBUG) {
LOG.debug(
"The TrustStore " + trustStoreLocation + " of type " + type
+ " has been loaded");
}
loadCACerts = false;
} finally {
if (is != null) {
is.close();
}
}
} else {
String loadCacerts = properties.getProperty(LOAD_CA_CERTS, "false");
if (loadCacerts != null) {
loadCacerts = loadCacerts.trim();
}
if (Boolean.valueOf(loadCacerts).booleanValue()) {
String cacertsPath = System.getProperty("java.home") + "/lib/security/cacerts";
if (cacertsPath != null) {
cacertsPath = cacertsPath.trim();
}
InputStream is = new FileInputStream(cacertsPath);
try {
String cacertsPasswd = properties.getProperty(TRUSTSTORE_PASSWORD, "changeit");
if (cacertsPasswd != null) {
cacertsPasswd = cacertsPasswd.trim();
}
truststore = load(is, cacertsPasswd, null, KeyStore.getDefaultType());
if (DO_DEBUG) {
LOG.debug("CA certs have been loaded");
}
loadCACerts = true;
} finally {
if (is != null) {
is.close();
}
}
}
}
//
// Load the CRL file
//
String crlLocation = properties.getProperty(X509_CRL_FILE);
if (crlLocation != null) {
crlLocation = crlLocation.trim();
InputStream is = loadInputStream(loader, crlLocation);
try {
CertificateFactory cf = getCertificateFactory();
X509CRL crl = (X509CRL) cf.generateCRL(is);
if (provider == null || provider.length() == 0) {
crlCertStore =
CertStore.getInstance(
"Collection",
new CollectionCertStoreParameters(Collections.singletonList(crl)));
} else {
crlCertStore =
CertStore.getInstance(
"Collection",
new CollectionCertStoreParameters(Collections.singletonList(crl)),
provider);
}
if (DO_DEBUG) {
LOG.debug(
"The CRL " + crlLocation + " has been loaded");
}
} catch (Exception e) {
if (DO_DEBUG) {
LOG.debug(e.getMessage(), e);
}
throw new CredentialException(CredentialException.IO_ERROR, "ioError00", e);
} finally {
if (is != null) {
is.close();
}
}
}
}
该方法中只有以下几行发生了变化:
String pin = properties.getProperty(KEYSTORE_PRIVATE_PASSWORD);
char[] pin_arr = pin.toCharArray();
String pkcs11config = "name = SmartCard\n" + "library = " + properties.getProperty(KEYSTORE_FILE);
byte[] pkcs11configBytes = pkcs11config.getBytes();
ByteArrayInputStream configStream = new ByteArrayInputStream(pkcs11configBytes);
Provider pkcs11Provider = new sun.security.pkcs11.SunPKCS11(configStream);
try {
Security.addProvider(pkcs11Provider);
keystore = KeyStore.getInstance(type,pkcs11Provider);
keystore.load(null, pin_arr);
} catch (Exception ex) {
// nothing
}
之后,我使用该类手动创建 Crypto 对象:
加密crypto = new CryptoSmartCard(prop);
这是签名方法的代码:
public Document signSOAPMessage3(SOAPMessage soapEnvelope)
throws SOAPException, TransformerException, WSSecurityException, CredentialException, IOException {
Source src = soapEnvelope.getSOAPPart().getContent();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMResult result = new DOMResult();
transformer.transform(src, result);
Document doc = (Document) result.getNode();
Properties prop = new Properties();
try {
InputStream input = new FileInputStream("sender.properties");
prop.load(input);
} catch (IOException ex) {
Logger.getLogger(TestSignWS1.class.getName()).log(Level.SEVERE, null, ex);
}
Crypto crypto = new CryptoSmartCard(prop);
final RequestData reqData = new RequestData();
java.util.Map msgContext = new java.util.TreeMap();
//msgContext.put(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION, "true");
msgContext.put(WSHandlerConstants.MUST_UNDERSTAND, "false");
//msgContext.put(WSHandlerConstants.SIG_PROP_FILE, "sender.properties");
String bodyPart = "{Content}{}Body";
String thumbprintPart = "{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}BinarySecurityToken";
msgContext.put(WSHandlerConstants.SIGNATURE_PARTS, thumbprintPart + ";" + bodyPart);
msgContext.put(WSHandlerConstants.IS_BSP_COMPLIANT, "false");
msgContext.put(WSHandlerConstants.SIG_ALGO, WSConstants.RSA_SHA1);
// Set this property if you want client public key (X509 certificate) sent along with document
// server will check signature using this public key
msgContext.put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");
msgContext.put("password", prop.getProperty(KEYSTORE_PRIVATE_PASSWORD));
reqData.setMsgContext(msgContext);
reqData.setUsername(crypto.getDefaultX509Identifier());
reqData.setUseSingleCert(true);
reqData.setSigCrypto(crypto);
final java.util.List actions = new java.util.ArrayList();
actions.add(new Integer(WSConstants.SIGN));
CustomHandler handler = new CustomHandler();
// sign document
handler.send(WSConstants.SIGN, doc, reqData, actions, true);
return doc;
}
和配置文件:
org.apache.ws.security.crypto.provider=hgaa.ws.crypto.CryptoSmartCard
org.apache.ws.security.crypto.merlin.keystore.provider=SunPKCS11
org.apache.ws.security.crypto.merlin.keystore.type=PKCS11
org.apache.ws.security.crypto.merlin.keystore.file=c:/windows/system32/aetpkss1.dll
org.apache.ws.security.crypto.merlin.keystore.private.password=xxxx