/** {@inheritDoc} */
  @Override
  protected void makeReply() throws KrbException {
    Ticket ticket = getTicket();

    TgsRep reply = new TgsRep();

    if (getClientEntry() == null) {
      reply.setCname(ticket.getEncPart().getCname());
    } else {
      reply.setCname(getClientEntry().getPrincipal());
    }
    reply.setCrealm(getKdcContext().getKdcRealm());
    reply.setTicket(ticket);

    EncKdcRepPart encKdcRepPart = makeEncKdcRepPart();
    reply.setEncPart(encKdcRepPart);

    EncryptionKey sessionKey;
    if (getToken() != null) {
      sessionKey = getSessionKey();
    } else {
      sessionKey = getTgtSessionKey();
    }
    EncryptedData encryptedData =
        EncryptionUtil.seal(encKdcRepPart, sessionKey, KeyUsage.TGS_REP_ENCPART_SESSKEY);
    reply.setEncryptedEncPart(encryptedData);

    setReply(reply);
  }
  /**
   * Verify authenticator.
   *
   * @throws org.apache.kerby.kerberos.kerb.KrbException e
   * @param paDataEntry preauthentication data entry
   */
  public void verifyAuthenticator(PaDataEntry paDataEntry) throws KrbException {
    ApReq apReq = KrbCodec.decode(paDataEntry.getPaDataValue(), ApReq.class);

    if (apReq.getPvno() != KrbConstant.KRB_V5) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_BADVERSION);
    }

    if (apReq.getMsgType() != KrbMessageType.AP_REQ) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_MSG_TYPE);
    }

    tgtTicket = apReq.getTicket();
    EncryptionType encType = tgtTicket.getEncryptedEncPart().getEType();
    EncryptionKey tgsKey = getTgsEntry().getKeys().get(encType);
    if (tgtTicket.getTktvno() != KrbConstant.KRB_V5) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_BADVERSION);
    }

    EncTicketPart encPart =
        EncryptionUtil.unseal(
            tgtTicket.getEncryptedEncPart(), tgsKey, KeyUsage.KDC_REP_TICKET, EncTicketPart.class);
    tgtTicket.setEncPart(encPart);

    EncryptionKey encKey = null;
    // if (apReq.getApOptions().isFlagSet(ApOptions.USE_SESSION_KEY)) {
    encKey = tgtTicket.getEncPart().getKey();

    if (encKey == null) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_NOKEY);
    }
    Authenticator authenticator =
        EncryptionUtil.unseal(
            apReq.getEncryptedAuthenticator(), encKey, KeyUsage.TGS_REQ_AUTH, Authenticator.class);

    if (!authenticator.getCname().equals(tgtTicket.getEncPart().getCname())) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_BADMATCH);
    }

    HostAddresses hostAddresses = tgtTicket.getEncPart().getClientAddresses();
    if (hostAddresses == null || hostAddresses.isEmpty()) {
      if (!getKdcContext().getConfig().isEmptyAddressesAllowed()) {
        throw new KrbException(KrbErrorCode.KRB_AP_ERR_BADADDR);
      }
    } else if (!hostAddresses.contains(getClientAddress())) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_BADADDR);
    }

    PrincipalName serverPrincipal = tgtTicket.getSname();
    serverPrincipal.setRealm(tgtTicket.getRealm());
    PrincipalName clientPrincipal = authenticator.getCname();
    clientPrincipal.setRealm(authenticator.getCrealm());
    KrbIdentity clientEntry = getEntry(clientPrincipal.getName());
    setClientEntry(clientEntry);

    if (!authenticator
        .getCtime()
        .isInClockSkew(getKdcContext().getConfig().getAllowableClockSkew() * 1000)) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_SKEW);
    }

    KerberosTime now = KerberosTime.now();
    KerberosTime startTime = tgtTicket.getEncPart().getStartTime();
    if (startTime == null) {
      startTime = tgtTicket.getEncPart().getAuthTime();
    }
    if (!startTime.lessThan(now)) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_TKT_NYV);
    }

    KerberosTime endTime = tgtTicket.getEncPart().getEndTime();
    if (!endTime.greaterThan(now)) {
      throw new KrbException(KrbErrorCode.KRB_AP_ERR_TKT_EXPIRED);
    }

    apReq.getApOptions().setFlag(ApOption.MUTUAL_REQUIRED);

    setTgtSessionKey(tgtTicket.getEncPart().getKey());
  }