/* DigiDoc4J library * * This software is released under either the GNU Library General Public * License (see LICENSE.LGPL). * * Note that the only valid version of the LGPL license as far as this * project is concerned is the original GNU Library General Public License * Version 2.1, February 1999 */ package org.digidoc4j.impl.bdoc; import java.io.Serializable; import java.util.Arrays; import java.util.Date; import java.util.List; import org.apache.commons.codec.binary.Base64; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DLSequence; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.cert.ocsp.BasicOCSPResp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.esig.dss.DSSUtils; import eu.europa.esig.dss.DigestAlgorithm; import eu.europa.esig.dss.xades.validation.XAdESSignature; public class OcspNonceValidator implements Serializable { private static final Logger logger = LoggerFactory.getLogger(OcspNonceValidator.class); private XAdESSignature signature; private BasicOCSPResp ocspResponse; public OcspNonceValidator(XAdESSignature signature) { this.signature = signature; ocspResponse = getLatestOcspResponse(signature.getOCSPSource().getContainedOCSPResponses()); } public boolean isValid() { if (signature.getPolicyId() == null) { return true; } if (ocspResponse == null) { logger.debug("OCSP response was not found in signature: " + signature.getId()); return true; } return isOcspResponseValid(ocspResponse); } private BasicOCSPResp getLatestOcspResponse(List<BasicOCSPResp> ocspResponses) { if (ocspResponses.size() == 0) { return null; } BasicOCSPResp basicOCSPResp = ocspResponses.get(0); Date latestDate = basicOCSPResp.getProducedAt(); for (int i = 1; i < ocspResponses.size(); i++) { BasicOCSPResp ocspResponse = ocspResponses.get(i); if (ocspResponse.getProducedAt().after(latestDate)) { latestDate = ocspResponse.getProducedAt(); basicOCSPResp = ocspResponse; } } return basicOCSPResp; } private boolean isOcspResponseValid(BasicOCSPResp latestOcspResponse) { Extension extension = latestOcspResponse.getExtension(new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.2")); if (extension == null) { logger.error("No valid OCSP extension found in signature: " + signature.getId()); return false; } return isOcspExtensionValid(extension); } private boolean isOcspExtensionValid(Extension extension) { try { byte[] octets = extension.getExtnValue().getOctets(); byte[] signatureDigestValue = getSignatureDigestValue(octets); byte[] foundHash = ((DEROctetString) ASN1Sequence.getInstance(octets).getObjectAt(1)).getOctets(); boolean extensionHashMatchesSignatureHash = Arrays.equals(foundHash, signatureDigestValue); logger.debug("OCSP extension contains valid signature digest: " + extensionHashMatchesSignatureHash); return extensionHashMatchesSignatureHash; } catch (Exception e) { logger.error("Invalid nonce format: " + e.getMessage()); return false; } } private byte[] getSignatureDigestValue(byte[] octets) { DigestAlgorithm usedDigestAlgorithm = getExtensionDigestAlgorithm(octets); String signatureValueInBase64 = signature.getSignatureValue().getFirstChild().getNodeValue(); byte[] signatureValue = Base64.decodeBase64(signatureValueInBase64.getBytes()); return DSSUtils.digest(usedDigestAlgorithm, signatureValue); } private DigestAlgorithm getExtensionDigestAlgorithm(byte[] octets) { ASN1Encodable oid = ASN1Sequence.getInstance(octets).getObjectAt(0); String oidString = ((DLSequence) oid).getObjects().nextElement().toString(); return DigestAlgorithm.forOID(oidString); } }