001: /*************************************************************************
002: * *
003: * EJBCA: The OpenSource Certificate Authority *
004: * *
005: * This software is free software; you can redistribute it and/or *
006: * modify it under the terms of the GNU Lesser General Public *
007: * License as published by the Free Software Foundation; either *
008: * version 2.1 of the License, or any later version. *
009: * *
010: * See terms of license at gnu.org. *
011: * *
012: *************************************************************************/package org.ejbca.core.protocol.xkms;
013:
014: import java.security.cert.CertPath;
015: import java.security.cert.CertPathValidator;
016: import java.security.cert.CertPathValidatorException;
017: import java.security.cert.CertStore;
018: import java.security.cert.CertificateFactory;
019: import java.security.cert.CollectionCertStoreParameters;
020: import java.security.cert.PKIXParameters;
021: import java.security.cert.TrustAnchor;
022: import java.security.cert.X509Certificate;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.Date;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Set;
030:
031: import javax.annotation.Resource;
032: import javax.ejb.CreateException;
033: import javax.naming.NamingException;
034: import javax.servlet.http.HttpServletRequest;
035: import javax.xml.bind.JAXBContext;
036: import javax.xml.bind.JAXBElement;
037: import javax.xml.bind.JAXBException;
038: import javax.xml.bind.Marshaller;
039: import javax.xml.bind.PropertyException;
040: import javax.xml.bind.Unmarshaller;
041: import javax.xml.parsers.DocumentBuilderFactory;
042: import javax.xml.parsers.ParserConfigurationException;
043: import javax.xml.transform.Source;
044: import javax.xml.transform.Transformer;
045: import javax.xml.transform.TransformerConfigurationException;
046: import javax.xml.transform.TransformerException;
047: import javax.xml.transform.TransformerFactory;
048: import javax.xml.transform.TransformerFactoryConfigurationError;
049: import javax.xml.transform.dom.DOMResult;
050: import javax.xml.transform.dom.DOMSource;
051: import javax.xml.ws.Provider;
052: import javax.xml.ws.Service;
053: import javax.xml.ws.ServiceMode;
054: import javax.xml.ws.WebServiceContext;
055: import javax.xml.ws.WebServiceProvider;
056: import javax.xml.ws.handler.MessageContext;
057:
058: import org.apache.log4j.Logger;
059: import org.ejbca.core.ejb.ServiceLocator;
060: import org.ejbca.core.ejb.ca.caadmin.ICAAdminSessionLocal;
061: import org.ejbca.core.ejb.ca.caadmin.ICAAdminSessionLocalHome;
062: import org.ejbca.core.ejb.ca.sign.ISignSessionLocal;
063: import org.ejbca.core.ejb.ca.sign.ISignSessionLocalHome;
064: import org.ejbca.core.ejb.ca.store.ICertificateStoreSessionLocal;
065: import org.ejbca.core.ejb.ca.store.ICertificateStoreSessionLocalHome;
066: import org.ejbca.core.model.InternalResources;
067: import org.ejbca.core.model.ca.caadmin.CAInfo;
068: import org.ejbca.core.model.ca.caadmin.extendedcaservices.XKMSCAServiceRequest;
069: import org.ejbca.core.model.ca.caadmin.extendedcaservices.XKMSCAServiceResponse;
070: import org.ejbca.core.model.ca.crl.RevokedCertInfo;
071: import org.ejbca.core.model.log.Admin;
072: import org.ejbca.core.protocol.xkms.common.XKMSConstants;
073: import org.ejbca.core.protocol.xkms.common.XKMSNamespacePrefixMapper;
074: import org.ejbca.core.protocol.xkms.generators.LocateResponseGenerator;
075: import org.ejbca.core.protocol.xkms.generators.RecoverResponseGenerator;
076: import org.ejbca.core.protocol.xkms.generators.RegisterResponseGenerator;
077: import org.ejbca.core.protocol.xkms.generators.ReissueResponseGenerator;
078: import org.ejbca.core.protocol.xkms.generators.RevokeResponseGenerator;
079: import org.ejbca.core.protocol.xkms.generators.ValidateResponseGenerator;
080: import org.ejbca.core.protocol.xkms.generators.XKMSConfig;
081: import org.ejbca.util.CertTools;
082: import org.w3._2002._03.xkms_.LocateRequestType;
083: import org.w3._2002._03.xkms_.LocateResultType;
084: import org.w3._2002._03.xkms_.MessageAbstractType;
085: import org.w3._2002._03.xkms_.ObjectFactory;
086: import org.w3._2002._03.xkms_.RecoverRequestType;
087: import org.w3._2002._03.xkms_.RecoverResultType;
088: import org.w3._2002._03.xkms_.RegisterRequestType;
089: import org.w3._2002._03.xkms_.RegisterResultType;
090: import org.w3._2002._03.xkms_.ReissueRequestType;
091: import org.w3._2002._03.xkms_.ReissueResultType;
092: import org.w3._2002._03.xkms_.RequestAbstractType;
093: import org.w3._2002._03.xkms_.RevokeRequestType;
094: import org.w3._2002._03.xkms_.RevokeResultType;
095: import org.w3._2002._03.xkms_.ValidateRequestType;
096: import org.w3._2002._03.xkms_.ValidateResultType;
097: import org.w3c.dom.Document;
098: import org.w3c.dom.Node;
099: import org.w3c.dom.NodeList;
100:
101: /**
102: * The XKMS Web Service in provider form
103: *
104: * This is used as a workaround for the namespace prefix handling
105: * in the JAX-WS
106: *
107: *
108: * @author Philip Vendil 2006 dec 18
109: *
110: * @version $Id: XKMSProvider.java,v 1.5 2007/11/20 08:44:48 anatom Exp $
111: */
112:
113: @ServiceMode(value=Service.Mode.PAYLOAD)
114: @WebServiceProvider(serviceName="XKMSService",targetNamespace="http://www.w3.org/2002/03/xkms#wsdl",portName="XKMSPort")
115: public class XKMSProvider implements Provider<Source> {
116: @Resource
117: private WebServiceContext wsContext;
118:
119: private static Logger log = Logger.getLogger(XKMSPortType.class);
120:
121: private static final InternalResources intres = InternalResources
122: .getInstance();
123:
124: protected Admin intAdmin = new Admin(Admin.TYPE_INTERNALUSER);
125:
126: private ObjectFactory xKMSObjectFactory = new ObjectFactory();
127:
128: private static JAXBContext jAXBContext = null;
129: private static Marshaller marshaller = null;
130: private static Unmarshaller unmarshaller = null;
131: private static DocumentBuilderFactory dbf = null;
132:
133: static {
134: try {
135: org.apache.xml.security.Init.init();
136: jAXBContext = JAXBContext
137: .newInstance("org.w3._2002._03.xkms_:org.w3._2001._04.xmlenc_:org.w3._2000._09.xmldsig_");
138: marshaller = jAXBContext.createMarshaller();
139: try {
140: marshaller.setProperty(
141: "com.sun.xml.bind.namespacePrefixMapper",
142: new XKMSNamespacePrefixMapper());
143: } catch (PropertyException e) {
144: log
145: .error(
146: intres
147: .getLocalizedMessage("xkms.errorregisteringnamespace"),
148: e);
149: }
150: dbf = DocumentBuilderFactory.newInstance();
151: dbf.setNamespaceAware(true);
152: unmarshaller = jAXBContext.createUnmarshaller();
153:
154: } catch (JAXBException e) {
155: log
156: .error(
157: intres
158: .getLocalizedMessage("xkms.errorinitializinggenerator"),
159: e);
160: }
161:
162: }
163:
164: /**
165: * The main method performing the actual calls
166: */
167: public Source invoke(Source request) {
168: Source response = null;
169:
170: MessageContext msgContext = wsContext.getMessageContext();
171: HttpServletRequest httpreq = (HttpServletRequest) msgContext
172: .get(MessageContext.SERVLET_REQUEST);
173: String remoteIP = httpreq.getRemoteAddr();
174:
175: Document requestDoc = null;
176: try {
177: DOMResult dom = new DOMResult();
178: Transformer trans = TransformerFactory.newInstance()
179: .newTransformer();
180: trans.transform(request, dom);
181: requestDoc = (Document) dom.getNode();
182: } catch (TransformerConfigurationException e) {
183: log.error(intres
184: .getLocalizedMessage("xkms.errorparsingdomreq"), e);
185: } catch (TransformerFactoryConfigurationError e) {
186: log.error(intres
187: .getLocalizedMessage("xkms.errorparsingdomreq"), e);
188: } catch (TransformerException e) {
189: log.error(intres
190: .getLocalizedMessage("xkms.errorparsingdomreq"), e);
191: }
192:
193: boolean respMecSign = false;
194: try {
195: JAXBElement jAXBRequest = (JAXBElement) unmarshaller
196: .unmarshal(request);
197:
198: JAXBElement jAXBResult = null;
199: if (jAXBRequest.getValue() instanceof RequestAbstractType) {
200: respMecSign = ((RequestAbstractType) jAXBRequest
201: .getValue()).getResponseMechanism().contains(
202: XKMSConstants.RESPONSMEC_REQUESTSIGNATUREVALUE);
203: }
204: if (jAXBRequest.getValue() instanceof ValidateRequestType) {
205: boolean requestVerifies = verifyRequest(requestDoc);
206: jAXBResult = validate(remoteIP,
207: (ValidateRequestType) jAXBRequest.getValue(),
208: requestVerifies);
209: }
210: if (jAXBRequest.getValue() instanceof LocateRequestType) {
211: boolean requestVerifies = verifyRequest(requestDoc);
212: jAXBResult = locate(remoteIP,
213: (LocateRequestType) jAXBRequest.getValue(),
214: requestVerifies);
215: }
216: if (jAXBRequest.getValue() instanceof RegisterRequestType) {
217: boolean requestVerifies = verifyRequest(requestDoc);
218: jAXBResult = register(remoteIP,
219: (RegisterRequestType) jAXBRequest.getValue(),
220: requestVerifies, requestDoc);
221: }
222: if (jAXBRequest.getValue() instanceof ReissueRequestType) {
223: boolean requestVerifies = verifyRequest(requestDoc);
224: jAXBResult = reissue(remoteIP,
225: (ReissueRequestType) jAXBRequest.getValue(),
226: requestVerifies, requestDoc);
227: }
228: if (jAXBRequest.getValue() instanceof RecoverRequestType) {
229: boolean requestVerifies = verifyRequest(requestDoc);
230: jAXBResult = recover(remoteIP,
231: (RecoverRequestType) jAXBRequest.getValue(),
232: requestVerifies, requestDoc);
233: }
234:
235: if (jAXBRequest.getValue() instanceof RevokeRequestType) {
236: boolean requestVerifies = verifyRequest(requestDoc);
237: jAXBResult = revoke(remoteIP,
238: (RevokeRequestType) jAXBRequest.getValue(),
239: requestVerifies, requestDoc);
240: }
241:
242: String responseId = ((MessageAbstractType) jAXBResult
243: .getValue()).getId();
244:
245: Document doc = dbf.newDocumentBuilder().newDocument();
246: marshaller.marshal(jAXBResult, doc);
247: doc = signResponseIfNeeded(doc, responseId, respMecSign,
248: intAdmin);
249:
250: response = new DOMSource(doc);
251:
252: } catch (JAXBException e) {
253: log.error(intres
254: .getLocalizedMessage("xkms.errorunmarshallingreq"),
255: e);
256: } catch (ParserConfigurationException e) {
257: log.error(intres
258: .getLocalizedMessage("xkms.errorparsingresp"), e);
259: }
260:
261: return response;
262: }
263:
264: private JAXBElement validate(String remoteIP,
265: ValidateRequestType value, boolean requestVerifies) {
266: ValidateResponseGenerator gen = new ValidateResponseGenerator(
267: remoteIP, value);
268:
269: JAXBElement<ValidateResultType> validateresult = xKMSObjectFactory
270: .createValidateResult(gen.getResponse(requestVerifies));
271: return validateresult;
272: }
273:
274: private JAXBElement locate(String remoteIP,
275: LocateRequestType value, boolean requestVerifies) {
276: LocateResponseGenerator gen = new LocateResponseGenerator(
277: remoteIP, value);
278:
279: JAXBElement<LocateResultType> locateresult = xKMSObjectFactory
280: .createLocateResult(gen.getResponse(requestVerifies));
281: return locateresult;
282: }
283:
284: private JAXBElement register(String remoteIP,
285: RegisterRequestType value, boolean requestVerifies,
286: Document requestDoc) {
287: RegisterResponseGenerator gen = new RegisterResponseGenerator(
288: remoteIP, value, requestDoc);
289:
290: JAXBElement<RegisterResultType> registerresult = xKMSObjectFactory
291: .createRegisterResult(gen.getResponse(requestVerifies));
292: return registerresult;
293: }
294:
295: private JAXBElement reissue(String remoteIP,
296: ReissueRequestType value, boolean requestVerifies,
297: Document requestDoc) {
298: ReissueResponseGenerator gen = new ReissueResponseGenerator(
299: remoteIP, value, requestDoc);
300:
301: JAXBElement<ReissueResultType> reissueresult = xKMSObjectFactory
302: .createReissueResult(gen.getResponse(requestVerifies));
303: return reissueresult;
304: }
305:
306: private JAXBElement recover(String remoteIP,
307: RecoverRequestType value, boolean requestVerifies,
308: Document requestDoc) {
309: RecoverResponseGenerator gen = new RecoverResponseGenerator(
310: remoteIP, value, requestDoc);
311:
312: JAXBElement<RecoverResultType> recoverresult = xKMSObjectFactory
313: .createRecoverResult(gen.getResponse(requestVerifies));
314: return recoverresult;
315: }
316:
317: private JAXBElement revoke(String remoteIP,
318: RevokeRequestType value, boolean requestVerifies,
319: Document requestDoc) {
320: RevokeResponseGenerator gen = new RevokeResponseGenerator(
321: remoteIP, value, requestDoc);
322:
323: JAXBElement<RevokeResultType> recoverresult = xKMSObjectFactory
324: .createRevokeResult(gen.getResponse(requestVerifies));
325: return recoverresult;
326: }
327:
328: /**
329: * Method that verifies the content of the requests against the
330: * configured trusted CA.
331: *
332: * @param kISSRequest if the caller is a kISSRequest
333: *
334: */
335: private boolean verifyRequest(Document requestDoc) {
336: boolean signatureExists = false;
337:
338: Node xmlSig = null;
339: NodeList nodeList = requestDoc.getChildNodes().item(0)
340: .getChildNodes();
341: for (int i = 0; i < nodeList.getLength(); i++) {
342: if (nodeList.item(i).getLocalName().equalsIgnoreCase(
343: "Signature")) {
344: xmlSig = nodeList.item(i);
345: }
346: }
347:
348: signatureExists = xmlSig != null;
349:
350: // Check that signature exists and if it's required
351: boolean sigRequired = XKMSConfig.isSignedRequestRequired();
352:
353: if (sigRequired && !signatureExists) {
354: log.error(intres
355: .getLocalizedMessage("xkms.recievedreqwithoutsig"));
356: return false;
357: } else {
358: if (signatureExists) {
359:
360: try {
361: org.w3c.dom.Element xmlSigElement = (org.w3c.dom.Element) xmlSig;
362: org.apache.xml.security.signature.XMLSignature xmlVerifySig = new org.apache.xml.security.signature.XMLSignature(
363: xmlSigElement, null);
364:
365: org.apache.xml.security.keys.KeyInfo keyInfo = xmlVerifySig
366: .getKeyInfo();
367: java.security.cert.X509Certificate verCert = keyInfo
368: .getX509Certificate();
369:
370: // Check signature
371: if (xmlVerifySig.checkSignatureValue(verCert)) {
372: // Check that the issuer is among accepted issuers
373: int cAId = CertTools.getIssuerDN(verCert)
374: .hashCode();
375:
376: Collection acceptedCAIds = XKMSConfig
377: .getAcceptedCA(intAdmin,
378: getCAAdminSession());
379: if (!acceptedCAIds.contains(new Integer(cAId))) {
380: throw new Exception(
381: "Error XKMS request signature certificate isn't among the list of accepted CA certificates");
382: }
383:
384: CAInfo cAInfo = getCAAdminSession().getCAInfo(
385: intAdmin, cAId);
386: Collection cACertChain = cAInfo
387: .getCertificateChain();
388: // Check issuer and validity
389: X509Certificate rootCert = null;
390: Iterator iter = cACertChain.iterator();
391: while (iter.hasNext()) {
392: X509Certificate cert = (X509Certificate) iter
393: .next();
394: if (cert.getIssuerDN().equals(
395: cert.getSubjectDN())) {
396: rootCert = cert;
397: break;
398: }
399: }
400:
401: if (rootCert == null) {
402: throw new CertPathValidatorException(
403: "Error Root CA cert not found in cACertChain");
404: }
405:
406: List list = new ArrayList();
407: list.add(verCert);
408: list.add(cACertChain);
409:
410: CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(
411: list);
412: CertStore store = CertStore.getInstance(
413: "Collection", ccsp);
414:
415: //validating path
416: List certchain = new ArrayList();
417: certchain.addAll(cACertChain);
418: certchain.add(verCert);
419: CertPath cp = CertificateFactory.getInstance(
420: "X.509", "BC").generateCertPath(
421: certchain);
422:
423: Set trust = new HashSet();
424: trust.add(new TrustAnchor(rootCert, null));
425:
426: CertPathValidator cpv = CertPathValidator
427: .getInstance("PKIX", "BC");
428: PKIXParameters param = new PKIXParameters(trust);
429: param.addCertStore(store);
430: param.setDate(new Date());
431: param.setRevocationEnabled(false);
432:
433: cpv.validate(cp, param);
434:
435: // Check revokation status
436: RevokedCertInfo revCertInfo = getCertStoreSession()
437: .isRevoked(intAdmin,
438: CertTools.getIssuerDN(verCert),
439: verCert.getSerialNumber());
440: if (revCertInfo.getReason() != RevokedCertInfo.NOT_REVOKED) {
441: return false;
442: }
443: } else {
444: log
445: .error(intres
446: .getLocalizedMessage("xkms.errorreqsigdoesntverify"));
447: return false;
448: }
449: } catch (Exception e) {
450: log
451: .error(intres
452: .getLocalizedMessage("xkms.errorwhenverifyingreq"));
453: return false;
454: }
455: }
456: }
457:
458: return true;
459: }
460:
461: /**
462: * Method that checks if signing is required by
463: * checking the service configuration and the request,
464: * It then signs the request, othervise it isn't
465: * @param admin
466: * @return the document signed or null of the signature failed;
467: */
468: private Document signResponseIfNeeded(Document result, String id,
469: boolean respMecSign, Admin admin) {
470: Document retval = result;
471:
472: if (XKMSConfig.alwaysSignResponses()
473: || (XKMSConfig.acceptSignRequests() && respMecSign)) {
474: try {
475:
476: XKMSCAServiceRequest cAReq = new XKMSCAServiceRequest(
477: result, id, true, false);
478:
479: XKMSCAServiceResponse resp = (XKMSCAServiceResponse) getSignSession()
480: .extendedService(
481: admin,
482: XKMSConfig.cAIdUsedForSigning(admin,
483: getCAAdminSession()), cAReq);
484:
485: retval = resp.getSignedDocument();
486: } catch (Exception e) {
487: log
488: .error(
489: intres
490: .getLocalizedMessage("xkms.errorgenrespsig"),
491: e);
492: retval = null;
493: }
494: }
495:
496: return retval;
497: }
498:
499: //
500: // Methods for fetching ejb session bean interfaces
501: //
502:
503: /**
504: * return the environment entries locator
505: * @return return the environment entries locator
506: */
507: protected ServiceLocator getLocator() {
508: return ServiceLocator.getInstance();
509: }
510:
511: private ICertificateStoreSessionLocal certificatestoresession = null;
512:
513: protected ICertificateStoreSessionLocal getCertStoreSession()
514: throws ClassCastException, CreateException, NamingException {
515: if (certificatestoresession == null) {
516: certificatestoresession = ((ICertificateStoreSessionLocalHome) getLocator()
517: .getLocalHome(
518: ICertificateStoreSessionLocalHome.COMP_NAME))
519: .create();
520: }
521: return certificatestoresession;
522: }
523:
524: private ICAAdminSessionLocal caadminsession = null;
525:
526: protected ICAAdminSessionLocal getCAAdminSession()
527: throws ClassCastException, CreateException, NamingException {
528: if (caadminsession == null) {
529: caadminsession = ((ICAAdminSessionLocalHome) getLocator()
530: .getLocalHome(ICAAdminSessionLocalHome.COMP_NAME))
531: .create();
532: }
533: return caadminsession;
534: }
535:
536: private ISignSessionLocal signsession = null;
537:
538: protected ISignSessionLocal getSignSession()
539: throws ClassCastException, CreateException, NamingException {
540: if (signsession == null) {
541: signsession = ((ISignSessionLocalHome) getLocator()
542: .getLocalHome(ISignSessionLocalHome.COMP_NAME))
543: .create();
544: }
545: return signsession;
546: }
547:
548: }
|