/** * Validate signature value. * * @throws throws exception if signature value did not match. */ void digidoc::SignatureBES::checkSignatureValue() const throw(SignatureException) { DEBUG("SignatureBES::checkSignatureValue()"); try { // Initialize RSA crypter. X509* cert = getSigningCertificate().getX509(); X509_scope certScope(&cert); RSACrypt rsa(cert); // Calculate SHA1 digest of the Signature->SignedInfo node. std::auto_ptr<Digest> calc = Digest::create(NID_sha1); std::vector<unsigned char> sha1 = calcDigestOnNode(calc.get(), DSIG_NAMESPACE, "SignedInfo"); DEBUGMEM("Digest", &sha1[0], sha1.size()); // Get signature value. std::vector<unsigned char> signatureSha1Rsa = getSignatureValue(); // Verify signature value with public RSA key. bool valid = rsa.verify(calc->getMethod(), sha1, signatureSha1Rsa); // Check that signature matched. if(!valid) { THROW_SIGNATUREEXCEPTION("Signature is not valid."); } } catch(const IOException& e) { THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to validate signature."); } }
/** * Check if document digest matches with what reference claims */ void digidoc::SignatureBES::checkDocumentRefDigest(Document& doc, const std::string& documentFileName, const digidoc::dsig::ReferenceType& refType) const throw(SignatureException) { // get digest of the referred document const dsig::DigestMethodType& digestMethod = refType.digestMethod(); const dsig::DigestMethodType::AlgorithmType& algorithmType = digestMethod.algorithm(); if ( !Digest::isSupported(algorithmType) ) { THROW_SIGNATUREEXCEPTION("Document reference '%s' has an unsupported digest algorithm '%s'" , documentFileName.c_str(), algorithmType.c_str()); } std::auto_ptr< Digest > docDigest; // only this scope try { docDigest = Digest::create(algorithmType); } catch (IOException&) { // according to Digest contract: THROW_SIGNATUREEXCEPTION("Document reference '%s' algorithm '%s' is not implemented" , documentFileName.c_str(), algorithmType.c_str()); } std::vector<unsigned char> docDigestBuf = doc.calcDigest(docDigest.get()); // get the claimed digest const dsig::DigestValueType& digestValueType = refType.digestValue(); const unsigned char* refDigest = reinterpret_cast<const unsigned char* >(digestValueType.data()); if ( docDigestBuf.size() != digestValueType.size() || memcmp(&docDigestBuf[0], refDigest, docDigestBuf.size()) != 0 ) { DEBUGMEM("Claimed digest", refDigest, digestValueType.size()); DEBUGMEM("Calculated digest", &docDigestBuf[0], docDigestBuf.size()); THROW_SIGNATUREEXCEPTION("Document '%s' digest does not match with digest in signature" , documentFileName.c_str()); } }
/** * Prepares SignedInfo * * @param signer signer that signs the signature object. * @throws SignatureException exception is throws if signing failed. */ std::vector<unsigned char> digidoc::SignatureBES::prepareSignedInfo(Signer* signer) throw(SignatureException, SignException) { // Calculate digests for the documents and add these to the signature reference. for(unsigned int i = 0; i < bdoc.documentCount(); ++i) { digidoc::Document doc = bdoc.getDocument(i); try { DEBUG("Adding document '%s', '%s' to the signature references.", doc.getFileName().c_str(), doc.getMediaType().c_str()); // URIs must encode non-ASCII characters in the format %HH where HH is the hex representation of the character std::string uri = std::string("/") + digidoc::util::File::toUri(doc.getFileName()); std::auto_ptr<Digest> calc(new Digest()); std::vector<unsigned char> digest = doc.calcDigest(calc.get()); DEBUGMEM("digest", &digest[0], digest.size()); addReference(uri, calc->getUri(), digest); } catch(const Exception& e) { delete signature; THROW_SIGNEXCEPTION_CAUSE(e, "Failed to calculate digests for document '%s'.", doc.getFileName().c_str()); } } // Set required signature fields. try { setSigningCertificate(signer->getCert()); setSignatureProductionPlace(signer->getSignatureProductionPlace()); setSignerRole(signer->getSignerRole()); setSigningTime(util::date::currentTime()); } catch( const IOException &e ) { THROW_SIGNEXCEPTION_CAUSE( e, "Failed to sign document" ); } xml_schema::Uri uri(URI_ID_RSA_SHA1); switch(signer->type()) { case NID_sha224: uri = xml_schema::Uri(URI_ID_RSA_SHA224); break; case NID_sha256: uri = xml_schema::Uri(URI_ID_RSA_SHA256); break; case NID_sha384: uri = xml_schema::Uri(URI_ID_RSA_SHA384); break; case NID_sha512: uri = xml_schema::Uri(URI_ID_RSA_SHA512); break; default: break; } signature->signedInfo().signatureMethod(dsig::SignatureMethodType(uri)); // Calculate digest of the Signature->Object->SignedProperties node. std::auto_ptr<Digest> calc(new Digest()); std::vector<unsigned char> digest = calcDigestOnNode(calc.get(), XADES_NAMESPACE, "SignedProperties"); addReference("#" + getId() +"-SignedProperties", calc->getUri(), digest, "http://uri.etsi.org/01903#SignedProperties"); // Calculate SHA digest of the Signature->SignedInfo node. calc.reset(new Digest(signer->type())); return calcDigestOnNode(calc.get(), URI_ID_DSIG, "SignedInfo"); }
/** * Do TM offline validations. * <ul> * <li>Validate BES offline</li> * <li>Check OCSP response (RevocationValues) was signed by trusted OCSP server</li> * <li>Check that nonce field in OCSP response is same as CompleteRevocationRefs->DigestValue</li> * <li>Recalculate hash of signature and compare with nonce</li> * </ul> * @throws SignatureException if signature is not valid */ void digidoc::SignatureTM::validateOffline() const throw(SignatureException) { SignatureBES::validateOffline(); // 1. Check OCSP response (RevocationValues) was signed by OCSP server // 2. OCSP server certificate is trusted? // 3. Check that nonce field in OCSP response is same as CompleteRevocationRefs->DigestValue // 4. Recalculate hash of signature and compare with nonce #if 0 Conf* conf = Conf::getInstance(); Conf::OCSPConf ocspConf = conf->getOCSP(getSigningCertificate().getIssuerName()); if(ocspConf.issuer.empty()) { SignatureException e(__FILE__, __LINE__, "Failed to find ocsp responder."); e.setCode( Exception::OCSPResponderMissing ); throw e; } OCSP ocsp(ocspConf.url); STACK_OF(X509)* ocspCerts = 0; try { ocspCerts = X509Cert::loadX509Stack(ocspConf.cert); } catch( const Exception &e ) { SignatureException exception(__FILE__, __LINE__, "OCSP certificate loading failed", e); exception.setCode( Exception::OCSPCertMissing ); throw exception; } X509Stack_scope x509StackScope(&ocspCerts); ocsp.setOCSPCerts(ocspCerts); #else OCSP ocsp; ocsp.setCertStore(digidoc::X509CertStore::getInstance()->getCertStore()); ocsp.setOCSPCerts(digidoc::X509CertStore::getInstance()->getCerts()); #endif std::vector<unsigned char> respBuf; try { getOCSPResponseValue(respBuf); ocsp.verifyResponse(respBuf); } catch( const Exception &e ) { THROW_SIGNATUREEXCEPTION_CAUSE( e, "OCSP response verfiy failed" ); } DEBUG("OCSP response was signed by trusted OCSP responder"); std::vector<unsigned char> respNonce = ocsp.getNonce(respBuf); xml_schema::Uri method = unsignedSignatureProperties()->completeRevocationRefs()[0].oCSPRefs() ->oCSPRef()[0].digestAlgAndValue()->digestMethod().algorithm(); std::auto_ptr<Digest> calc = Digest::create(std::string(method)); calc->update(getSignatureValue()); std::vector<unsigned char> nonce = calc->getDigest(); if(nonce != respNonce) { DEBUGMEM("Calculated signature HASH", &nonce[0], nonce.size()); DEBUGMEM("Response nonce", &respNonce[0], respNonce.size()); THROW_SIGNATUREEXCEPTION("Calculated signature hash doesn't match to OCSP responder nonce field"); } std::vector<unsigned char> revocationOCSPRefValue(0); std::string ocspResponseHashUri; getRevocationOCSPRef(revocationOCSPRefValue, ocspResponseHashUri); std::auto_ptr<Digest> ocspResponseCalc = Digest::create(ocspResponseHashUri); DEBUG("Calculating digest on %d bytes", respBuf.size()); ocspResponseCalc->update(respBuf); std::vector<unsigned char> ocspResponseHash = ocspResponseCalc->getDigest(); if(ocspResponseHash != revocationOCSPRefValue) { DEBUGMEM("Document ocspResponse HASH:", &revocationOCSPRefValue[0], revocationOCSPRefValue.size()); DEBUGMEM("Calculated ocspResponse HASH:", &ocspResponseHash[0], ocspResponseHash.size()); THROW_SIGNATUREEXCEPTION("OCSPRef value doesn't match with hash of OCSP response"); } else { DEBUG("TM signature valid"); } }
/** * * @param signer * @throws SignatureException */ void digidoc::SignatureTM::sign(Signer* signer) throw(SignatureException, SignException) { DEBUG("SignatureTM::sign()"); // Sign with BES profile. SignatureBES::sign(signer); DEBUG("BES signature successful."); // Calculate NONCE value. std::auto_ptr<Digest> calc = Digest::create(); calc->update(getSignatureValue()); std::vector<unsigned char> nonce = calc->getDigest(); DEBUGMEM("Calculated signature HASH (nonce):", &nonce[0], nonce.size()); // Get issuer certificate from certificate store. X509* cert = signer->getCert(); X509Cert cert_(cert); X509* issuer = X509CertStore::getInstance()->getCert(*(cert_.getIssuerNameAsn1())); X509_scope issuerScope(&issuer); if(issuer == NULL) { THROW_SIGNATUREEXCEPTION("Could not find certificate '%s' issuer '%s' in certificate store.", cert_.getSubject().c_str(), cert_.getIssuerName().c_str()); } DEBUG("Signing with X.509 cert {serial=%ld, subject=%s, issuer=%s})", cert_.getSerial(), cert_.getSubject().c_str(), cert_.getIssuerName().c_str()); // Initialize OCSP. DEBUG("Making OCSP request."); Conf* conf = Conf::getInstance(); Conf::OCSPConf ocspConf = conf->getOCSP(cert_.getIssuerName()); if(ocspConf.issuer.empty()) { SignatureException e(__FILE__, __LINE__, "Failed to find ocsp responder."); e.setCode( Exception::OCSPResponderMissing ); throw e; } STACK_OF(X509)* ocspCerts = 0; try { ocspCerts = X509Cert::loadX509Stack(ocspConf.cert); } catch(const IOException& e) { THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to load OCSP certificate"); } X509Stack_scope ocspCertsScope(&ocspCerts); OCSP::CertStatus status = OCSP::UNKNOWN; std::vector<unsigned char> ocspResponse; struct tm producedAt; try { OCSP ocsp; ocsp.setOCSPCerts(ocspCerts); ocsp.setUrl(ocspConf.url); ocsp.setMaxAge(2*60); // FIXME: remove or move to conf ocsp.setSkew(15*60); // FIXME: remove or move to conf status = ocsp.checkCert(cert, issuer, nonce, ocspResponse, producedAt); } catch(const IOException& e) { THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to get OCSP response"); } catch(const OCSPException& e) { THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to get OCSP response"); } switch(status) { case digidoc::OCSP::GOOD: DEBUG("OCSP status: GOOD"); break; case digidoc::OCSP::REVOKED: { DEBUG("OCSP status: REVOKED"); SignatureException e( __FILE__, __LINE__, "Certificate status: revoked" ); e.setCode( Exception::CertificateRevoked ); throw e; break; } case digidoc::OCSP::UNKNOWN: { DEBUG("OCSP status: UNKNOWN"); SignatureException e( __FILE__, __LINE__, "Certificate status: unknown" ); e.setCode( Exception::CertificateUnknown ); throw e; break; } } DEBUG("OCSP response size %d", ocspResponse.size()); // FIXME: get from ocsp instead // FIXME: This file can contain multiple certs. X509Cert class supports only one cert per file // loadX509Stack loads multiple certs from one file // X509* ocspCert = X509Cert::loadX509(Conf::getInstance()->getOCSPCertPath()); X509_scope ocspCertScope(&ocspCert); if(sk_X509_num(ocspCerts) > 1) { ERR("More than one OCSP cert in file."); } X509Cert ocspCert_(sk_X509_value(ocspCerts, 0)); std::auto_ptr<Digest> ocspResponseCalc = Digest::create(); ocspResponseCalc->update(ocspResponse); std::vector<unsigned char> ocspResponseHash = ocspResponseCalc->getDigest(); DEBUGMEM("Calculated ocspResponse HASH:", &ocspResponseHash[0], ocspResponseHash.size()); // Set TM profile signature parameters. createTMProperties(); setOCSPCertificate(ocspCert_); setCACertificate(X509Cert(issuer)); setCompleteRevocationRefs(ocspCert_.getIssuerName(), calc->getUri(), ocspResponseHash, producedAt); setOCSPResponseValue(ocspResponse); }
/// TODO: comment /// /// @throws SignatureException on a problem in signature void digidoc::SignatureBES::checkReferenceToSigProps(const digidoc::dsig::ReferenceType& refType) const throw(SignatureException) { // check attribute URI (e.g. "#SigId-SignedProperties") const dsig::ReferenceType::URIOptional& uriOpt = refType.uRI(); if ( !uriOpt.present() ) { THROW_SIGNATUREEXCEPTION("SignedInfo reference to SignedProperties does not have attribute 'URI'"); } /* This check is pointless. It might make sense to check the syntax of the * URL, but this is better left for the resolvers to handle. std::string foundUri = uriOpt.get(); std::string expectedUri = std::string("#") + id() + "-SignedProperties"; if ( foundUri != expectedUri ) { THROW_SIGNATUREEXCEPTION("SignedInfo reference to SignedProperties attribute 'URI' is invalid"); } */ // check DigestMethod const dsig::DigestMethodType& digestMethod = refType.digestMethod(); const dsig::DigestMethodType::AlgorithmType& algorithm = digestMethod.algorithm(); if ( !Digest::isSupported( algorithm ) ) { THROW_SIGNATUREEXCEPTION("reference to SignedProperties digest method algorithm '%s' is not supported", algorithm.c_str()); } // check DigestValue const dsig::DigestValueType& digestValue = refType.digestValue(); // TODO: do it nicely. //xml_schema::dom::auto_ptr<xercesc::DOMDocument> dom = createDom(); //xercesc::DOMNode* signedPropsNode = dom->getFirstChild()->getLastChild()->getFirstChild()->getFirstChild(); // xercesc::DOMNode* idNode(NULL); // FIXME: Äkki oleks parem kasutada olemasolevat signature puud, mitte Xercese oma? // if (!signedPropsNode->hasAttributes() // || (idNode = signedPropsNode->getAttributes()->getNamedItem(xercesc::XMLString::transcode("Id"))) == NULL ) // { // THROW_SIGNATUREEXCEPTION("SignedProperties does not have attribute 'Id'"); // } std::auto_ptr<Digest> calc = Digest::create(refType.digestMethod().algorithm()); //std::vector<unsigned char> calculatedDigestValue = calcDigestOnNode(calc.get(), signedPropsNode); std::vector<unsigned char> calculatedDigestValue = calcDigestOnNode(calc.get(), XADES_NAMESPACE, "SignedProperties"); if ( digestValue.begin() + calculatedDigestValue.size() != digestValue.end() ) { THROW_SIGNATUREEXCEPTION("SignedProperties digest lengths do not match"); } for ( size_t i = 0; i < calculatedDigestValue.size(); i++ ) { const char* dv = digestValue.begin() + i; if ( *dv != static_cast<char>(calculatedDigestValue[i]) ) { DEBUGMEM("Document digest:", &digestValue.data()[0], digestValue.size()); DEBUGMEM("Calculated digest:", &calculatedDigestValue[0], calculatedDigestValue.size()); THROW_SIGNATUREEXCEPTION("SignedProperties digest values do not match"); } } }
/// TODO: comment /// /// @throws SignatureException on a problem in signature void digidoc::SignatureBES::checkKeyInfo() const throw(SignatureException) { X509Cert x509 = getSigningCertificate(); dsig::SignatureType::ObjectSequence const& objs = signature->object(); if ( objs.size() != 1 ) { THROW_SIGNATUREEXCEPTION("Number of Objects is %d, must be 1", objs.size()); } dsig::ObjectType::QualifyingPropertiesSequence const& qProps = objs[0].qualifyingProperties(); if ( qProps.size() != 1 ) { THROW_SIGNATUREEXCEPTION("Number of QualifyingProperties is %d, must be 1", qProps.size()); } xades::QualifyingPropertiesType::SignedPropertiesOptional const& sigProps = qProps[0].signedProperties(); if ( !sigProps.present() ) { THROW_SIGNATUREEXCEPTION("SignedProperties not found"); } xades::SignedSignaturePropertiesType::SigningCertificateOptional const& sigCertOpt = sigProps->signedSignatureProperties().signingCertificate(); if ( !sigCertOpt.present() ) { THROW_SIGNATUREEXCEPTION("SigningCertificate not found"); } xades::CertIDListType::CertSequence const& certs = sigCertOpt->cert(); if ( certs.size() != 1 ) { THROW_SIGNATUREEXCEPTION("Number of SigningCertificates is %d, must be 1", certs.size()); } dsig::DigestMethodType::AlgorithmType const& certDigestMethodAlgorithm = certs[0].certDigest().digestMethod().algorithm(); if ( !Digest::isSupported(certDigestMethodAlgorithm) ) { THROW_SIGNATUREEXCEPTION("Unsupported digest algorithm %s for signing certificate", certDigestMethodAlgorithm.c_str()); } dsig::X509IssuerSerialType::X509IssuerNameType certIssuerName = certs[0].issuerSerial().x509IssuerName(); dsig::X509IssuerSerialType::X509SerialNumberType certSerialNumber = certs[0].issuerSerial().x509SerialNumber(); try { if ( x509.compareIssuerToString(certIssuerName) || x509.getSerial() != certSerialNumber ) { DEBUG("certIssuerName: \"%s\"", certIssuerName.c_str()); DEBUG("x509.getCertIssuerName() \"%s\"", x509.getIssuerName().c_str()); DEBUG("sertCerials = %ld %ld", x509.getSerial(), (long)certSerialNumber); THROW_SIGNATUREEXCEPTION("Signing certificate issuer information does not match"); } } catch( const IOException &e ) { THROW_SIGNATUREEXCEPTION_CAUSE(e, "Signing certificate issuer information not valid"); } xades::DigestAlgAndValueType::DigestValueType const& certDigestValue = certs[0].certDigest().digestValue(); std::auto_ptr<Digest> certDigestCalc = Digest::create(certDigestMethodAlgorithm); // lets check digest with x509 that was in keyInfo std::vector<unsigned char> derEncodedX509 = x509.encodeDER(); certDigestCalc->update(&derEncodedX509[0], derEncodedX509.size()); std::vector<unsigned char> calcDigest = certDigestCalc->getDigest(); if ( certDigestValue.size() != static_cast<size_t>( certDigestCalc->getSize() ) ) { THROW_SIGNATUREEXCEPTION("Wrong length for signing certificate digest"); } for ( size_t i = 0; i < static_cast<size_t>( certDigestCalc->getSize() ); ++i ) { if ( calcDigest[i] != static_cast<unsigned char>(certDigestValue.data()[i]) ) { DEBUGMEM("Document cert digest", &(certDigestValue.data())[0], certDigestValue.size()); DEBUGMEM("Calculated cert digest", &calcDigest[0], calcDigest.size()); THROW_SIGNATUREEXCEPTION("Signing certificate digest does not match"); } } }
/** * Signs all documents in container. * * @param signer signer implementation. * @param profile signature profile (e.g. BES, TM). * @throws BDocException exception is throws if signing the BDCO container failed. */ void digidoc::BDoc::sign(Signer* signer, Signature::Type profile) throw(BDocException) { if (signer == NULL) { THROW_BDOCEXCEPTION("Null pointer in digidoc::BDoc::sign"); } DEBUG("sign(signer = 0x%X, profile=%d)", (unsigned int)signer, profile); // Create signature by type. Signature* signature = NULL; if(profile == Signature::BES) { signature = new SignatureBES(*this); } else if(profile == Signature::TM) { signature = new SignatureTM(*this); } else if(profile == Signature::MOBILE) { try { signature = new SignatureMobile( signer->signaturePath(), *this); } catch(const Exception& e) { THROW_BDOCEXCEPTION_CAUSE(e, "MobileSignature"); } addSignature( signature ); return; } else { THROW_BDOCEXCEPTION("Unknown signature profile: %d", profile); } // Calculate digests for the documents and add these to the signature reference. for(std::vector<Document>::iterator iter = documents.begin(); iter != documents.end(); iter++) { try { DEBUG("Adding document '%s', '%s' to the signature references.", iter->getPath().c_str(), iter->getMediaType().c_str()); // URIs must encode non-ASCII characters in the format %HH where HH is the hex representation of the character std::string uri = std::string("/") + digidoc::util::String::toUriFormat(digidoc::util::File::fileName(iter->getPath())); std::auto_ptr<Digest> calc = Digest::create(); std::vector<unsigned char> digest = iter->calcDigest(calc.get()); DEBUGMEM("digest", &digest[0], digest.size()); signature->addReference(uri, calc->getUri(), digest); } catch(const Exception& e) { delete signature; THROW_BDOCEXCEPTION_CAUSE(e, "Failed to calculate digests for document '%s'.", iter->getPath().c_str()); } } try { // Finalize the signature by calculating signature. signature->sign(signer); } catch(const SignatureException& e) { delete signature; THROW_BDOCEXCEPTION_CAUSE(e, "Failed to sign BDOC container."); } catch(const SignException& e) { delete signature; THROW_BDOCEXCEPTION_CAUSE(e, "Failed to sign BDOC container."); } // Add the created signature to the signatures list. addSignature(signature); }