/*
* Copyright 2012 Research Studios Austria Forschungsges.m.b.H.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package won.protocol.message.processor.impl;
import org.apache.jena.query.Dataset;
import org.apache.jena.riot.Lang;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import won.cryptography.rdfsign.SignatureVerificationState;
import won.cryptography.rdfsign.WonKeysReaderWriter;
import won.protocol.message.WonMessage;
import won.protocol.message.processor.WonMessageProcessor;
import won.protocol.message.processor.exception.WonMessageProcessingException;
import won.protocol.util.RdfUtils;
import won.protocol.util.linkeddata.LinkedDataSource;
import java.net.URI;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.Map;
import java.util.Set;
/**
* Checks all signatures found in a WonMessage. It is assumed that the message is well-formed.
*/
public class SignatureCheckingWonMessageProcessor implements WonMessageProcessor
{
private final Logger logger = LoggerFactory.getLogger(getClass());
public SignatureCheckingWonMessageProcessor() {
}
public void setLinkedDataSource(final LinkedDataSource linkedDataSource) {
this.linkedDataSource = linkedDataSource;
}
@Autowired
private LinkedDataSource linkedDataSource;
@Override
public WonMessage process(final WonMessage message) throws WonMessageProcessingException {
SignatureVerificationState result = null;
try {
// obtain public keys
Map<String,PublicKey> keys = getRequiredPublicKeys(message.getCompleteDataset());
// verify with those public keys
result = WonMessageSignerVerifier.verify(keys, message);
logger.debug("VERIFIED=" + result.isVerificationPassed() + " with keys: " + keys.values() + " for\n"
+ RdfUtils
.writeDatasetToString(message.getCompleteDataset(), Lang.TRIG));
} catch (Exception e) {
//TODO SignatureProcessingException?
throw new WonMessageProcessingException("Could not verify message "
+ message.getMessageURI(), e);
}
// throw exception if the verification fails:
if (!result.isVerificationPassed()) {
//TODO SignatureProcessingException?
throw new WonMessageProcessingException(
new SignatureException("Could not verify message " + message.getMessageURI()
+ ": " + result.getMessage()));
}
return message;
}
// TODO If public key extracted from the message itself, there is a security flaw -
// no guarantee that that public key really belongs to original generator.
// in this case integration with webid-tls when the certificate has to be provided via tls might solve the problem
// TODO what about owner key?
// TODO maybe already known public keys can be taken from own cache/store?
// TODO proper exceptions
/**
* Extract public keys from content and from referenced in signature key uris data
* @param msgDataset
* @return
* @throws NoSuchAlgorithmException
* @throws NoSuchProviderException
* @throws InvalidKeySpecException
*/
private Map<String, PublicKey> getRequiredPublicKeys(final Dataset msgDataset)
throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
//extracted and then
WonKeysReaderWriter keyReader = new WonKeysReaderWriter();
// extract keys if directly provided in the message content:
Map<String,PublicKey> keys = keyReader.readFromDataset(msgDataset);
// extract referenced key by dereferencing a (kind of) webid of a signer
Set<String> refKeys = keyReader.readKeyReferences(msgDataset);
for (String refKey : refKeys) {
if (!keys.containsKey(refKey)) {
Dataset keyDataset = linkedDataSource.getDataForResource(URI.create(refKey));
// TODO replace the WonKeysReaderWriter methods with WonRDFUtils methods and use the WonKeysReaderWriter
// itself internally there in those methods
Set<PublicKey> resolvedKeys = keyReader.readFromDataset(keyDataset, refKey);
for (PublicKey resolvedKey : resolvedKeys) {
keys.put(refKey, resolvedKey);
// TODO now we only expect one key but in future there could be several keys for one WebID
break;
}
}
}
return keys;
}
}