package cz.abclinuxu.datoveschranky.impl;
import cz.abclinuxu.datoveschranky.common.entities.Hash;
import cz.abclinuxu.datoveschranky.common.entities.TimeStamp;
import cz.abclinuxu.datoveschranky.common.impl.DataBoxException;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.Principal;
import java.security.Provider;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.apache.log4j.Logger;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.tsp.TimeStampTokenInfo;
/**
*
* Pomocná třída pro validaci časového razítka a podpisu zprávy. Jen prototyp!
*
* @author Vaclav Rosecky <xrosecky 'at' gmail 'dot' com>
*/
public class Validator {
private static Map<String, String> OIDToAlgorithmName = new HashMap<String, String>();
static {
OIDToAlgorithmName.put("1.3.14.3.2.26", "SHA-1");
OIDToAlgorithmName.put("2.16.840.1.101.3.4.2.1", "SHA-256");
OIDToAlgorithmName.put("2.16.840.1.101.3.4.2.2", "SHA-384");
OIDToAlgorithmName.put("2.16.840.1.101.3.4.2.3", "SHA-512");
Provider provider = new BouncyCastleProvider();
Security.addProvider(provider);
}
private Collection<X509Certificate> certs = null;
private boolean isValidating = false;
private Logger logger = Logger.getLogger(Validator.class.getCanonicalName());
public Validator(Collection<X509Certificate> certs, boolean validating) {
this.certs = certs;
isValidating = validating;
}
public Validator() {
this.certs = new ArrayList<X509Certificate>();
isValidating = false;
}
public TimeStamp readTimeStamp(byte[] timeStamp) {
try {
CMSSignedData data = new CMSSignedData(timeStamp);
TimeStampToken tst = new TimeStampToken(data);
TimeStampTokenInfo tsti = tst.getTimeStampInfo();
X509Certificate cert = this.findCertificate(tst.getSID());
if (isValidating) {
try {
tst.validate(cert, "BC");
} catch (Exception ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
} else {
throw new DataBoxException(ex.toString(), ex);
}
}
}
String algo = OIDToAlgorithmName.get(tsti.getMessageImprintAlgOID());
byte[] hash = tsti.getMessageImprintDigest();
return new TimeStamp(new Hash(algo, hash), cert, tsti.getGenTime());
} catch (CMSException ex) {
throw new DataBoxException("Chyba pri cteni casoveho razitka.", ex);
} catch (TSPException ioe) {
throw new DataBoxException("Chyba pri cteni casoveho razitka.", ioe);
} catch (IOException ioe) {
throw new DataBoxException("IO chyba pri cteni casoveho razitka.", ioe);
}
}
/**
* Vrátí obsah po odstranění PKCS7 obálky.
*/
public byte[] readPKCS7(byte[] signedBytes) throws DataBoxException {
try {
CMSSignedData data = new CMSSignedData(signedBytes);
verifySignature(data);
CMSProcessable signedContent = data.getSignedContent();
return (byte[]) signedContent.getContent();
} catch (Exception ex) {
throw new DataBoxException("Nemohu otevrit PKCS#7 obalku.", ex);
}
}
/**
* Vrátí obsah po odstranění PKCS7 obálky.
*/
public InputStream readPKCS7(InputStream is) throws DataBoxException {
try {
CMSSignedData data = new CMSSignedData(is);
CMSProcessable signedContent = data.getSignedContent();
verifySignature(data);
return (InputStream) signedContent.getContent();
} catch (Exception ex) {
throw new DataBoxException("Nemohu otevrit PKCS#7 obalku.", ex);
}
}
public static Collection<X509Certificate> readX509Certificates(InputStream is) {
List<X509Certificate> certs = new ArrayList<X509Certificate>();
BufferedInputStream bis = new BufferedInputStream(is);
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
certs.add((X509Certificate) cf.generateCertificate(bis));
}
} catch (CertificateException ce) {
throw new DataBoxException("Nemohu precist X.509 certifikat.", ce);
} catch (IOException ioe) {
throw new DataBoxException("IO chyba pri cteni X.509 certifikatu.", ioe);
}
return certs;
}
private void verifySignature(CMSSignedData data) throws Exception {
if (isValidating) {
SignerInformationStore signerStore = data.getSignerInfos();
Collection<SignerInformation> signers = signerStore.getSigners();
for (SignerInformation signer : signers) {
X509Certificate cert = this.findCertificate(signer.getSID());
if (cert == null) {
throw new DataBoxException("Nemohu najit certifikat.");
}
if (!signer.verify(cert, "BC")) {
throw new DataBoxException("Nemohu overit oproti certifikatu stazenou zpravu.");
}
}
}
}
private X509Certificate findCertificate(SignerId signer) {
return this.findCertificate(signer.getIssuer(), signer.getSerialNumber());
}
private X509Certificate findCertificate(Principal issuer, BigInteger serNumber) {
for (X509Certificate cert : certs) {
if (cert.getIssuerX500Principal().getName().equals(issuer.getName()) && cert.getSerialNumber().equals(serNumber)) {
return cert;
}
}
logger.info(String.format("Nemohu najit certifikat, vydavatel je %s "
+", seriove cislo je %d.", issuer.getName(), serNumber));
return null;
}
}