/*******************************************************************************
* Copyright (c) 2009 MATERNA Information & Communications. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html. For further
* project-related information visit http://www.ws4d.org. The most recent
* version of the JMEDS framework can be obtained from
* http://sourceforge.net/projects/ws4d-javame.
******************************************************************************/
package org.ws4d.java.security;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.ws4d.java.communication.CommunicationBinding;
import org.ws4d.java.communication.HTTPBinding;
import org.ws4d.java.communication.HTTPSBinding;
import org.ws4d.java.communication.ProtocolData;
import org.ws4d.java.communication.TimeoutException;
import org.ws4d.java.communication.connection.ip.IPAddress;
import org.ws4d.java.communication.connection.tcp.SecureSocketFactorySE;
import org.ws4d.java.communication.connection.tcp.ServerSocket;
import org.ws4d.java.communication.connection.tcp.Socket;
import org.ws4d.java.configuration.SecurityProperties;
import org.ws4d.java.constants.HTTPConstants;
import org.ws4d.java.dispatch.DeviceServiceRegistry;
import org.ws4d.java.io.xml.XmlSerializer;
import org.ws4d.java.io.xml.canonicalization.CanonicalSerializer;
import org.ws4d.java.message.Message;
import org.ws4d.java.service.reference.DeviceReference;
import org.ws4d.java.service.reference.ServiceReference;
import org.ws4d.java.structures.HashMap;
import org.ws4d.java.types.EndpointReference;
import org.ws4d.java.types.URI;
import org.ws4d.java.util.Log;
import org.ws4d.java.util.StringUtil;
public class DPWSSecurityManagerSE implements DPWSSecurityManager {
private HashMap protocolDataToInputStream = new HashMap();
private static SecurityProperties secProp;
public DPWSSecurityManagerSE() {
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getBodySignature(org.ws4d.
* java.io.xml.XmlSerializer, org.ws4d.java.message.Message)
*/
public String getBodySignature(XmlSerializer serial, Message msg) {
CanonicalSerializer cs = (CanonicalSerializer) serial;
byte[] arry = cs.bodyPart();
byte[] signArray = getSignature(arry, (PrivateKey) msg.getPrivateKey());
return Base64Util.encodeBytes(signArray);
}
private byte[] getSignature(byte[] rawxml, PrivateKey pk) {
try {
// Compute signature
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(pk);
MessageDigest digest = MessageDigest.getInstance("sha1");
// calculate digest of the one and only part
rawxml = digest.digest(rawxml);
// generate SignedInfo part and calculate its digest
byte[][] digs = new byte[1][];
digs[0] = rawxml;
rawxml = generateSignedInfo(digs, new String[] { SecurityManager.bodyPartID });
rawxml = digest.digest(rawxml);
// sign the SignedInfo parts digest
instance.update(rawxml);
byte[] signature = instance.sign();
return signature;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
return null;
}
/*
* (non-Javadoc)
* @see org.ws4d.java.security.DPWSSecurityManager#validateMessage(byte[],
* org.ws4d.java.communication.ProtocolData,
* org.ws4d.java.types.EndpointReference, java.lang.String[])
*/
public boolean validateMessage(byte[] signature, ProtocolData pd, EndpointReference epr, String[] aliasCanditates) {
try {
IDawareInputStream bbis = ((IDawareInputStream) (protocolDataToInputStream.get(pd)));
byte[][] signedParts = bbis.getPartsByteArrays();
if (signedParts == null) {
Log.error("Message validation failed because the referred sections cound not be extracted!");
return false;
} else {
protocolDataToInputStream.remove(pd);
}
Certificate cert = null;
DeviceReference dRef = null;
ServiceReference sRef = null;
if ((dRef = DeviceServiceRegistry.getDeviceReference(epr, false)) != null) {
try {
cert = (Certificate) dRef.getDevice().getCertificate();
} catch (TimeoutException e) {
e.printStackTrace();
}
} else if ((sRef = DeviceServiceRegistry.getServiceReference(epr, pd.getCommunicationManagerId(), false)) != null) {
try {
cert = (Certificate) sRef.getService().getCertificate();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
for (int i = 0; cert == null && aliasCanditates != null && i < aliasCanditates.length; i++) {
cert = (Certificate) getCertificate(aliasCanditates[i]);
}
if (cert == null) {
cert = (Certificate) getCertificate(epr.getAddress().toString());
if (dRef != null) {
dRef.getDevice().setCertificate(cert);
} else if (sRef != null) {
sRef.getService().setCertificate(cert);
}
}
if (cert == null) {
Log.error("Security: device/service uuid '" + epr.getAddress() + "' not found in the specified keystore!");
return false;
}
// calculating digests over all parts
MessageDigest digest = MessageDigest.getInstance("sha1");
byte[][] digests = new byte[signedParts.length][];
for (int i = 0; i < signedParts.length; i++) {
digests[i] = digest.digest(signedParts[i]);
}
byte[] signedInfo = generateSignedInfo(digests, bbis.getIds());
// the digest of the signedInfo element
signedInfo = digest.digest(signedInfo);
// sign that digest
PublicKey pk = cert.getPublicKey();
Signature s = Signature.getInstance("SHA1withRSA");
s.initVerify(pk);
s.update(signedInfo);
if (s.verify(signature)) {
Log.info("Discovery-Message validated!");
return true;
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.warn("Discovery-Message could not be validated!");
return false;
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getMD5Hash(java.lang.String)
*/
public long getMD5Hash(String str) {
MessageDigest m = null;
try {
m = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
m.update(str.getBytes(), 0, str.length());
return new BigInteger(1, m.digest()).longValue();
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getPrivateKey(java.lang.String
* , java.lang.String)
*/
public Object getPrivateKey(String privKey, String pswd) {
try {
if (secProp == null) {
secProp = SecurityProperties.getInstance();
}
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream(secProp.getKeyStoreFilePath());
ks.load(fis, secProp.getKeyStorePswd().toCharArray());
fis.close();
PrivateKey pk = null;
if (pswd == null) {
pswd = SecurityProperties.getInstance().getKeyStorePswd();
}
if (pswd == null) throw new KeyStoreException("Could not fetch private key. Password not found.");
try {
pk = (PrivateKey) ks.getKey(privKey, pswd.toCharArray());
} catch (UnrecoverableKeyException e) {
// If there is no key the user should create one!
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return pk;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getCertificate(java.lang.String
* )
*/
public Object getCertificate(String certAlias) {
if (secProp == null) {
secProp = SecurityProperties.getInstance();
}
try {
Certificate cert = null;
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
if (SecurityProperties.getInstance().getTrustStorePath() == null && SecurityProperties.getInstance().getKeyStoreFilePath() == null) return null;
FileInputStream fis = (secProp.getTrustStorePath() != null ? new FileInputStream(secProp.getTrustStorePath()) : new FileInputStream(secProp.getKeyStoreFilePath()));
ks.load(fis, (secProp.getTrustStorePasswd() != null ? secProp.getTrustStorePasswd() : secProp.getKeyStorePswd()).toCharArray());
fis.close();
String nearestAlias = certAlias;
int lastIndex = -1;
while (nearestAlias.length() > 1) {
if ((cert = ks.getCertificate(nearestAlias.toLowerCase())) != null) break;
nearestAlias = (lastIndex = nearestAlias.indexOf('/')) < 0 ? "" : nearestAlias.substring(lastIndex + 1);
}
return cert;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
Log.error("Security: Could not get keystore!");
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/*
* (non-Javadoc)
* @see org.ws4d.java.security.DPWSSecurityManager#wrapInputStream(java.io.
* InputStream, org.ws4d.java.communication.ProtocolData)
*/
public InputStream wrapInputStream(InputStream in, ProtocolData pd) {
IDawareInputStream bbio = new IDawareInputStream(in, null);
protocolDataToInputStream.put(pd, bbio);
return bbio;
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getNewCanonicalSerializer()
*/
public XmlSerializer getNewCanonicalSerializer(String id) {
return new CanonicalSerializer(id);
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getSecureServerSocket(java
* .lang.String, int, java.lang.String)
*/
public ServerSocket getSecureServerSocket(IPAddress adr, int port, String alias) {
try {
return SecureSocketFactorySE.createServerSocket(adr, port, alias);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getSecureSocket(org.ws4d.java
* .types.URI)
*/
public Socket getSecureSocket(URI location) {
try {
return SecureSocketFactorySE.createSocket(new IPAddress(location.getHost()), location.getPort(), location.toString());
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getSecureSocket(java.lang.
* String, int, java.lang.String)
*/
public Socket getSecureSocket(IPAddress host, int port, String alias) {
try {
return SecureSocketFactorySE.createSocket(host, port, alias);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/*
* (non-Javadoc)
* @see org.ws4d.java.security.DPWSSecurityManager#decode(java.lang.String)
*/
public byte[] decode(String base64enc) {
return Base64Util.decode(base64enc);
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#isHTTPS(org.ws4d.java.types
* .URI)
*/
public boolean isHTTPS(URI uri) {
// return uri.getSchema().equalsIgnoreCase(HTTPConstants.HTTPS_SCHEMA);
return StringUtil.equalsIgnoreCase(uri.getSchema(), HTTPConstants.HTTPS_SCHEMA);
}
/*
* (non-Javadoc)
* @see org.ws4d.java.security.DPWSSecurityManager#getKeyManagers()
*/
public Object[] getKeyManagers() throws IOException, GeneralSecurityException {
String alg = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmFact = KeyManagerFactory.getInstance(alg);
if (SecurityProperties.getInstance().getKeyStoreFilePath() == null) return null;
FileInputStream fis = new FileInputStream(SecurityProperties.getInstance().getKeyStoreFilePath());
KeyStore ks = KeyStore.getInstance("jks");
ks.load(fis, SecurityProperties.getInstance().getKeyStorePswd().toCharArray());
fis.close();
kmFact.init(ks, SecurityProperties.getInstance().getKeyStorePswd().toCharArray());
KeyManager[] kms = kmFact.getKeyManagers();
return kms;
}
/*
* (non-Javadoc)
* @see org.ws4d.java.security.DPWSSecurityManager#getTrustManagers()
*/
public Object[] getTrustManagers() throws IOException, GeneralSecurityException {
String alg = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmFact = TrustManagerFactory.getInstance(alg);
if (SecurityProperties.getInstance().getTrustStorePath() == null) return null;
FileInputStream fis = new FileInputStream(SecurityProperties.getInstance().getTrustStorePath());
KeyStore ks = KeyStore.getInstance("jks");
ks.load(fis, SecurityProperties.getInstance().getTrustStorePasswd().toCharArray());
fis.close();
tmFact.init(ks);
TrustManager[] tms = tmFact.getTrustManagers();
return tms;
}
/*
* (non-Javadoc)
* @see
* org.ws4d.java.security.DPWSSecurityManager#getAliasFromBinding(org.ws4d
* .java.communication.HTTPBinding)
*/
public String getAliasFromBinding(CommunicationBinding binding) {
if (binding.getType() != HTTPBinding.HTTPS_BINDING)
return null;
else
return ((HTTPSBinding) binding).getAlias();
}
/**
* This element will only be used internally. It will be gennerated digested
* and this digest will be signed
*
* @param digs the signatures
* @param refs the reference ids of the digested parts of the xml message.
* @return the byte array of the signed info element. Ready to be signed.
*/
private static byte[] generateSignedInfo(byte[][] digs, String[] refs) {
String signatureInfo = "<SignedInfo><CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml_exc-c14n#\" /> " + "<SignatureMethod Algorithm=\"htt://www.w3.org/2000/09/xmldsig#rsa-sha1\" />";
for (int i = 0; i < digs.length; i++) {
signatureInfo += "<Reference URI=\"#" + refs[i] + "\" ><Transforms><Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\" />" + "</Transforms><DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\" />" + "<DigestValue>" + Base64Util.encodeBytes(digs[i]) + "</DigestValue></Reference>";
}
signatureInfo += "</SignedInfo>";
return signatureInfo.getBytes();
}
private String byteArrayToString(byte[] d) {
String g = "";
for (int i = 0; i < d.length; i++) {
g += (char) d[i];
}
return g;
}
/*
* (non-Javadoc)
* @see org.ws4d.java.security.DPWSSecurityManager#encode(byte[])
*/
public String encode(byte[] raw) {
return Base64Util.encodeBytes(raw);
}
}