예제 #1
1
  @Override
  protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
      throws ServletException, IOException {
    response.setHeader("Access-Control-Allow-Origin", "*");
    try {
      if (server == null) {
        LOG.error("server in servlet not configured");
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        response.setContentLength(0);
        return;
      }

      ResponderAndRelativeUri r = server.getResponderAndRelativeUri(request);
      if (r == null) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
      }

      if (StringUtil.isNotBlank(r.getRelativeUri())) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
      }

      Responder responder = r.getResponder();

      HealthCheckResult healthResult = server.healthCheck(responder);
      if (healthResult.isHealthy()) {
        response.setStatus(HttpServletResponse.SC_OK);
      } else {
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      }

      response.setContentType(HealthCheckServlet.CT_RESPONSE);
      byte[] respBytes = healthResult.toJsonMessage(true).getBytes();
      response.setContentLength(respBytes.length);
      response.getOutputStream().write(respBytes);
    } catch (EOFException e) {
      final String message = "connection reset by peer";
      if (LOG.isErrorEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);

      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.setContentLength(0);
    } catch (Throwable t) {
      final String message = "Throwable thrown, this should not happen";
      if (LOG.isErrorEnabled()) {
        LOG.error(LogUtil.buildExceptionLogFormat(message), t.getClass().getName(), t.getMessage());
      }
      LOG.debug(message, t);
      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.setContentLength(0);
    }

    response.flushBuffer();
  } // method doGet
예제 #2
0
  public static void addNamedCurves() {
    synchronized (EXECUTED) {
      if (!successful) {
        LOG.warn("could not initialize");
        return;
      }

      if (EXECUTED) {
        return;
      }
      EXECUTED = Boolean.TRUE;

      try {
        if (jdk18on) {
          addNamedCurves_jdk18on();
        } else {
          addNamedCurves_jdk17();
        }

      } catch (Throwable t) {
        final String message = "uncatched Error";
        if (LOG.isErrorEnabled()) {
          LOG.error(
              LogUtil.buildExceptionLogFormat(message), t.getClass().getName(), t.getMessage());
        }
        LOG.debug(message, t);
      }
    }
  }
예제 #3
0
  private static Field getField(final Class<?> clazz, final String fieldName) {
    String desc = "Field " + fieldName;
    try {
      Field field = clazz.getDeclaredField(fieldName);
      field.setAccessible(true);
      return field;
    } catch (NoSuchFieldException e) {
      final String message = "could not get " + desc;
      if (LOG.isWarnEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);
    }

    return null;
  }
예제 #4
0
  private static Method getMethod(
      final Class<?> clz, final String methodName, final Class<?>[] params) {
    Method serviceMethod = null;
    final String desc = "method " + clz.getName() + "." + methodName;

    try {
      if (params == null) {
        serviceMethod = clz.getDeclaredMethod(methodName);
      } else {
        serviceMethod = clz.getDeclaredMethod(methodName, params);
      }
      serviceMethod.setAccessible(true);
      return serviceMethod;
    } catch (SecurityException | NoSuchMethodException e) {
      final String message = "could not get " + desc;
      if (LOG.isWarnEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);
    }

    return null;
  }
예제 #5
0
  private void initPkcs11ModuleConf() {
    if (p11Control != null) {
      return;
    }

    if (StringUtil.isBlank(pkcs11ConfFile)) {
      throw new IllegalStateException("pkcs11ConfFile is not set");
    }

    try {
      JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
      Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
      SchemaFactory schemaFact =
          SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
      Schema schema = schemaFact.newSchema(getClass().getResource("/xsd/pkcs11-conf.xsd"));
      unmarshaller.setSchema(schema);
      @SuppressWarnings("unchecked")
      JAXBElement<PKCS11ConfType> rootElement =
          (JAXBElement<PKCS11ConfType>) unmarshaller.unmarshal(new File(pkcs11ConfFile));
      PKCS11ConfType pkcs11Conf = rootElement.getValue();
      ModulesType modulesType = pkcs11Conf.getModules();

      Map<String, P11ModuleConf> confs = new HashMap<>();
      for (ModuleType moduleType : modulesType.getModule()) {
        String name = moduleType.getName();
        if (DEFAULT_P11MODULE_NAME.equals(name)) {
          throw new ConfigurationException(
              "invald module name " + DEFAULT_P11MODULE_NAME + ", it is reserved");
        }

        if (confs.containsKey(name)) {
          throw new ConfigurationException(
              "multiple modules with the same module name is not permitted");
        }

        P11PasswordRetriever pwdRetriever;

        PasswordsType passwordsType = moduleType.getPasswords();
        if (passwordsType == null || CollectionUtil.isEmpty(passwordsType.getPassword())) {
          pwdRetriever = P11NullPasswordRetriever.INSTANCE;
        } else {
          pwdRetriever = new P11PasswordRetrieverImpl();
          ((P11PasswordRetrieverImpl) pwdRetriever).setPasswordResolver(passwordResolver);

          for (PasswordType passwordType : passwordsType.getPassword()) {
            Set<P11SlotIdentifier> slots = getSlots(passwordType.getSlots());
            ((P11PasswordRetrieverImpl) pwdRetriever)
                .addPasswordEntry(slots, new ArrayList<>(passwordType.getSinglePassword()));
          }
        }

        Set<P11SlotIdentifier> includeSlots = getSlots(moduleType.getIncludeSlots());
        Set<P11SlotIdentifier> excludeSlots = getSlots(moduleType.getExcludeSlots());

        final String osName = System.getProperty("os.name").toLowerCase();
        String nativeLibraryPath = null;
        for (NativeLibraryType library : moduleType.getNativeLibraries().getNativeLibrary()) {
          List<String> osNames = library.getOs();
          if (CollectionUtil.isEmpty(osNames)) {
            nativeLibraryPath = library.getPath();
          } else {
            for (String entry : osNames) {
              if (osName.contains(entry.toLowerCase())) {
                nativeLibraryPath = library.getPath();
                break;
              }
            }
          }

          if (nativeLibraryPath != null) {
            break;
          }
        }

        if (nativeLibraryPath == null) {
          throw new ConfigurationException("could not find PKCS#11 library for OS " + osName);
        }

        P11ModuleConf conf =
            new P11ModuleConf(name, nativeLibraryPath, pwdRetriever, includeSlots, excludeSlots);
        confs.put(name, conf);
      }

      final String defaultModuleName = modulesType.getDefaultModule();
      if (confs.containsKey(defaultModuleName) == false) {
        throw new ConfigurationException("default module " + defaultModuleName + " is not defined");
      }

      this.p11Control = new P11Control(defaultModuleName, new HashSet<>(confs.values()));
    } catch (JAXBException | SAXException | ConfigurationException e) {
      final String message = "invalid configuration file " + pkcs11ConfFile;
      if (LOG.isErrorEnabled()) {
        final String exceptionMessage;
        if (e instanceof JAXBException) {
          exceptionMessage = XMLUtil.getMessage((JAXBException) e);
        } else {
          exceptionMessage = e.getMessage();
        }
        LOG.error(
            LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), exceptionMessage);
      }
      LOG.debug(message, e);

      throw new RuntimeException(message);
    }
  }
예제 #6
0
  private static void addNamedCurves_jdk18on() {
    final Class<?>[] Param_CurveDB_add =
        new Class[] {
          String.class,
          String.class,
          int.class,
          String.class,
          String.class,
          String.class,
          String.class,
          String.class,
          String.class,
          int.class,
          Pattern.class
        };
    final Class<?>[] Param_getCurve = new Class[] {String.class};

    Method method_add = getMethod(class_CurveDB, "add", Param_CurveDB_add);
    if (method_add == null) {
      return;
    }

    Method method_getCurve = getMethod(class_CurveDB, "lookup", Param_getCurve);
    if (method_getCurve == null) {
      return;
    }

    Field field_oidMap = getField(class_CurveDB, "oidMap");
    if (field_oidMap == null) {
      return;
    }

    Field field_specCollection = getField(class_CurveDB, "specCollection");
    if (field_specCollection == null) {
      return;
    }

    Set<String> processedCurveOids = new HashSet<>();
    Map<String, String> addedCurves = new HashMap<>();

    Enumeration<?> curveNames = ECNamedCurveTable.getNames();
    while (curveNames.hasMoreElements()) {
      String curveName = (String) curveNames.nextElement();
      ASN1ObjectIdentifier curveId = getCurveId(curveName);
      if (curveId == null) {
        LOG.debug("cound not find curve OID for curve {}, ignore it", curveName);
        continue;
      }

      String curveDesc = "named curve " + curveName + " (" + curveId + ")";

      if (processedCurveOids.contains(curveId.getId())) {
        LOG.debug("{} is already processed, ignore it", curveDesc);
        continue;
      }

      processedCurveOids.add(curveId.getId());

      if (curve_isRegistered(method_getCurve, curveId)) {
        LOG.info("{} is already registered, ignore it", curveDesc);
        continue;
      }

      X9ECParameters params = ECNamedCurveTable.getByOID(curveId);
      ECCurve curve = params.getCurve();
      if (curve instanceof ECCurve.Fp || curve instanceof ECCurve.F2m) {
        CurveData c = new CurveData(params);
        boolean added =
            CurveDB_add(
                method_add,
                curveName,
                curveId.getId(),
                c.type,
                c.sfield,
                c.a,
                c.b,
                c.x,
                c.y,
                c.n,
                c.h);

        if (added) {
          LOG.debug("added {}", curveDesc);
          addedCurves.put(curveName, curveId.getId());
        } else {
          LOG.warn("could not add {}", curveDesc);
        }
      } else {
        LOG.info("unknown curve type {}", curve.getClass().getName());
      }
    }

    try {
      Map<?, ?> oidMap = (Map<?, ?>) field_oidMap.get(null);
      Collection<?> namedCurves = Collections.unmodifiableCollection(oidMap.values());

      field_specCollection.set(null, namedCurves);
    } catch (IllegalArgumentException | IllegalAccessException | ClassCastException e) {
      final String message = "could not update change the value of field CurveDB.specCollection.";
      if (LOG.isWarnEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);
    }

    logAddedCurves(addedCurves);
  }
예제 #7
0
  private static void addNamedCurves_jdk17() {
    final Class<?>[] Param_NamedCurve_add =
        new Class[] {
          String.class,
          String.class,
          int.class,
          String.class,
          String.class,
          String.class,
          String.class,
          String.class,
          String.class,
          int.class
        };
    final Class<?>[] Param_getCurve = new Class[] {String.class};

    Method method_add = getMethod(class_NamedCurve, "add", Param_NamedCurve_add);
    if (method_add == null) {
      return;
    }

    Method method_getCurve = getMethod(class_NamedCurve, "getECParameterSpec", Param_getCurve);
    if (method_getCurve == null) {
      return;
    }

    Field field_SPLIT_PATTERN = getField(class_NamedCurve, "SPLIT_PATTERN");
    if (field_SPLIT_PATTERN == null) {
      return;
    }

    try {
      field_SPLIT_PATTERN.set(null, SPLIT_PATTERN);
    } catch (IllegalArgumentException | IllegalAccessException e) {
      final String message = "could not set Field SPLIT_PATTERN";
      if (LOG.isWarnEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);
      return;
    }

    Set<String> processedCurveOids = new HashSet<>();
    Map<String, String> addedCurves = new HashMap<>();

    Enumeration<?> curveNames = ECNamedCurveTable.getNames();
    while (curveNames.hasMoreElements()) {
      String curveName = (String) curveNames.nextElement();
      ASN1ObjectIdentifier curveId = getCurveId(curveName);

      if (curveId == null) {
        LOG.info("cound not find curve OID for curve {}, ignore it", curveName);
        continue;
      }

      String curveDesc = "named curve " + curveName + " (" + curveId + ")";

      if (processedCurveOids.contains(curveId.getId())) {
        LOG.debug("{} is already processed, ignore it", curveDesc);
        continue;
      }

      processedCurveOids.add(curveId.getId());

      if (curve_isRegistered(method_getCurve, curveId)) {
        LOG.debug("{} is already registered, ignore it", curveDesc);
        continue;
      }

      X9ECParameters params = ECNamedCurveTable.getByOID(curveId);
      ECCurve curve = params.getCurve();

      if (curve instanceof ECCurve.Fp || curve instanceof ECCurve.F2m) {
        CurveData c = new CurveData(params);
        boolean added =
            NamedCurve_add(
                method_add,
                curveName,
                curveId.getId(),
                c.type,
                c.sfield,
                c.a,
                c.b,
                c.x,
                c.y,
                c.n,
                c.h);

        if (added) {
          LOG.debug("added {}", curveDesc);
          addedCurves.put(curveName, curveId.getId());
        } else {
          LOG.warn("could not add {}", curveDesc);
        }
      } else {
        LOG.info("unknown curve type {}", curve.getClass().getName());
      }
    }

    try {
      field_SPLIT_PATTERN.set(null, null);
    } catch (IllegalArgumentException | IllegalAccessException e) {
      final String message = "could not set Field SPLIT_PATTERN";
      if (LOG.isWarnEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);
      return;
    }

    logAddedCurves(addedCurves);
  }
예제 #8
0
  private void processRequest(
      final HttpServletRequest request,
      final HttpServletResponse response,
      final ResponderAndRelativeUri r,
      final boolean getMethod)
      throws ServletException, IOException {
    Responder responder = r.getResponder();
    AuditEvent auditEvent = null;

    AuditLevel auditLevel = AuditLevel.INFO;
    AuditStatus auditStatus = AuditStatus.SUCCESSFUL;
    String auditMessage = null;

    long start = 0;

    AuditService auditService =
        (auditServiceRegister == null) ? null : auditServiceRegister.getAuditService();

    if (auditService != null && responder.getAuditOption() != null) {
      start = System.currentTimeMillis();
      auditEvent = new AuditEvent(new Date());
      auditEvent.setApplicationName("OCSP");
      auditEvent.setName("PERF");
    }

    try {
      if (server == null) {
        String message = "responder in servlet not configured";
        LOG.error(message);
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        response.setContentLength(0);

        auditLevel = AuditLevel.ERROR;
        auditStatus = AuditStatus.FAILED;
        auditMessage = message;
        return;
      }

      InputStream requestStream;
      if (getMethod) {
        String relativeUri = r.getRelativeUri();

        // RFC2560 A.1.1 specifies that request longer than 255 bytes SHOULD be sent by
        // POST, we support GET for longer requests anyway.
        if (relativeUri.length() > responder.getRequestOption().getMaxRequestSize()) {
          response.setContentLength(0);
          response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);

          auditStatus = AuditStatus.FAILED;
          auditMessage = "request too large";
          return;
        }

        requestStream = new ByteArrayInputStream(Base64.decode(relativeUri));
      } else {
        // accept only "application/ocsp-request" as content type
        if (!CT_REQUEST.equalsIgnoreCase(request.getContentType())) {
          response.setContentLength(0);
          response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);

          auditStatus = AuditStatus.FAILED;
          auditMessage = "unsupporte media type " + request.getContentType();
          return;
        }

        // request too long
        if (request.getContentLength() > responder.getRequestOption().getMaxRequestSize()) {
          response.setContentLength(0);
          response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);

          auditStatus = AuditStatus.FAILED;
          auditMessage = "request too large";
          return;
        } // if (CT_REQUEST)

        requestStream = request.getInputStream();
      } // end if (getMethod)

      OCSPRequest ocspRequest;
      try {
        ASN1StreamParser parser = new ASN1StreamParser(requestStream);
        ocspRequest = OCSPRequest.getInstance(parser.readObject());
      } catch (Exception e) {
        response.setContentLength(0);
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);

        auditStatus = AuditStatus.FAILED;
        auditMessage = "bad request";

        final String message = "could not parse the request (OCSPRequest)";
        if (LOG.isErrorEnabled()) {
          LOG.error(
              LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
        }
        LOG.debug(message, e);

        return;
      }

      OCSPReq ocspReq = new OCSPReq(ocspRequest);

      response.setContentType(HttpOcspServlet.CT_RESPONSE);

      OcspRespWithCacheInfo ocspRespWithCacheInfo =
          server.answer(responder, ocspReq, auditEvent, getMethod);
      if (ocspRespWithCacheInfo == null) {
        auditMessage = "processRequest returned null, this should not happen";
        LOG.error(auditMessage);
        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        response.setContentLength(0);

        auditLevel = AuditLevel.ERROR;
        auditStatus = AuditStatus.FAILED;
      } else {
        OCSPResp resp = ocspRespWithCacheInfo.getResponse();
        byte[] encodedOcspResp = null;
        response.setStatus(HttpServletResponse.SC_OK);

        ResponseCacheInfo cacheInfo = ocspRespWithCacheInfo.getCacheInfo();
        if (getMethod && cacheInfo != null) {
          encodedOcspResp = resp.getEncoded();
          long now = System.currentTimeMillis();
          // RFC 5019 6.2: Date: The date and time at which the OCSP server generated
          // the HTTP response.
          response.setDateHeader("Date", now);
          // RFC 5019 6.2: Last-Modified: date and time at which the OCSP responder
          // last modified the response.
          response.setDateHeader("Last-Modified", cacheInfo.getThisUpdate());
          // RFC 5019 6.2: Expires: This date and time will be the same as the
          // nextUpdate time-stamp in the OCSP
          // response itself.
          // This is overridden by max-age on HTTP/1.1 compatible components
          if (cacheInfo.getNextUpdate() != null) {
            response.setDateHeader("Expires", cacheInfo.getNextUpdate());
          }
          // RFC 5019 6.2: This profile RECOMMENDS that the ETag value be the ASCII
          // HEX representation of the SHA1 hash of the OCSPResponse structure.
          response.setHeader(
              "ETag",
              new StringBuilder(42)
                  .append('\\')
                  .append(HashCalculator.hexSha1(encodedOcspResp))
                  .append('\\')
                  .toString());

          // Max age must be in seconds in the cache-control header
          long maxAge;
          if (responder.getResponseOption().getCacheMaxAge() != null) {
            maxAge = responder.getResponseOption().getCacheMaxAge().longValue();
          } else {
            maxAge = OcspServer.defaultCacheMaxAge;
          }

          if (cacheInfo.getNextUpdate() != null) {
            maxAge =
                Math.min(maxAge, (cacheInfo.getNextUpdate() - cacheInfo.getThisUpdate()) / 1000);
          }

          response.setHeader(
              "Cache-Control",
              new StringBuilder(55)
                  .append("max-age=")
                  .append(maxAge)
                  .append(",public,no-transform,must-revalidate")
                  .toString());
        } // end if (getMethod && cacheInfo != null)

        if (encodedOcspResp != null) {
          response.getOutputStream().write(encodedOcspResp);
        } else {
          ASN1OutputStream asn1Out = new ASN1OutputStream(response.getOutputStream());
          asn1Out.writeObject(resp.toASN1Structure());
          asn1Out.flush();
        }
      } // end if (ocspRespWithCacheInfo)
    } catch (EOFException e) {
      final String message = "Connection reset by peer";
      if (LOG.isErrorEnabled()) {
        LOG.warn(LogUtil.buildExceptionLogFormat(message), e.getClass().getName(), e.getMessage());
      }
      LOG.debug(message, e);

      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.setContentLength(0);
    } catch (Throwable t) {
      final String message = "Throwable thrown, this should not happen!";
      LOG.error(message, t);

      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.setContentLength(0);

      auditLevel = AuditLevel.ERROR;
      auditStatus = AuditStatus.FAILED;
      auditMessage = "internal error";
    } finally {
      try {
        response.flushBuffer();
      } finally {
        if (auditEvent != null) {
          if (auditLevel != null) {
            auditEvent.setLevel(auditLevel);
          }

          if (auditStatus != null) {
            auditEvent.setStatus(auditStatus);
          }

          if (auditMessage != null) {
            auditEvent.addEventData(new AuditEventData("message", auditMessage));
          }

          auditEvent.setDuration(System.currentTimeMillis() - start);

          if (!auditEvent.containsChildAuditEvents()) {
            auditService.logEvent(auditEvent);
          } else {
            List<AuditEvent> expandedAuditEvents = auditEvent.expandAuditEvents();
            for (AuditEvent event : expandedAuditEvents) {
              auditService.logEvent(event);
            }
          }
        } // end if (auditEvent != null)
      } // end inner try
    } // end external try
  } // method processRequest