private void emsaPssVerify(byte[] m, byte[] em, int emBits) throws GeneralSecurityException {
    // Step 1. Length checking.
    // This step is unnecessary because Java's byte[] only supports up to 2^31 -1 bytes while the
    // input limitation for the hash function is far larger (2^61 - 1 for SHA-1).

    // Step 2. Compute hash.
    Validators.validateSignatureHash(sigHash);
    MessageDigest digest =
        EngineFactory.MESSAGE_DIGEST.getInstance(SubtleUtil.toDigestAlgo(this.sigHash));
    byte[] mHash = digest.digest(m);
    int hLen = digest.getDigestLength();

    int emLen = em.length;

    // Step 3. Check emLen.
    if (emLen < hLen + this.saltLength + 2) {
      throw new GeneralSecurityException("inconsistent");
    }

    // Step 4. Check right most byte of EM.
    if (em[em.length - 1] != (byte) 0xbc) {
      throw new GeneralSecurityException("inconsistent");
    }

    // Step 5. Extract maskedDb and H from EM.
    byte[] maskedDb = Arrays.copyOf(em, emLen - hLen - 1);
    byte[] h = Arrays.copyOfRange(em, maskedDb.length, maskedDb.length + hLen);

    // Step 6. Check whether the leftmost 8 * emLen - emBits bits of the leftmost octet in maskedDB
    // are all zeros.
    for (int i = 0; i < (long) emLen * 8 - emBits; i++) {
      int bytePos = i / 8;
      int bitPos = 7 - i % 8;
      if (((maskedDb[bytePos] >> bitPos) & 1) != 0) {
        throw new GeneralSecurityException("inconsistent");
      }
    }

    // Step 7. Compute dbMask.
    byte[] dbMask = SubtleUtil.mgf1(h, emLen - hLen - 1, mgf1Hash);

    // Step 8. Compute db.
    byte[] db = new byte[dbMask.length];
    for (int i = 0; i < db.length; i++) {
      db[i] = (byte) (dbMask[i] ^ maskedDb[i]);
    }

    // Step 9. Set the leftmost 8*emLen - emBits bits of the leftmost octet in DB to zero.
    for (int i = 0; i <= (long) emLen * 8 - emBits; i++) {
      int bytePos = i / 8;
      int bitPos = 7 - i % 8;
      db[bytePos] = (byte) (db[bytePos] & ~(1 << bitPos));
    }

    // Step 10. Check db.
    for (int i = 0; i < emLen - hLen - this.saltLength - 2; i++) {
      if (db[i] != 0) {
        throw new GeneralSecurityException("inconsistent");
      }
    }
    if (db[emLen - hLen - this.saltLength - 2] != (byte) 0x01) {
      throw new GeneralSecurityException("inconsistent");
    }

    // Step 11. Extract salt from db.
    byte[] salt = Arrays.copyOfRange(db, db.length - this.saltLength, db.length);

    // Step 12. Generate M'.
    byte[] mPrime = new byte[8 + hLen + this.saltLength];
    System.arraycopy(mHash, 0, mPrime, 8, mHash.length);
    System.arraycopy(salt, 0, mPrime, 8 + hLen, salt.length);

    // Step 13. Compute H'
    byte[] hPrime = digest.digest(mPrime);
    if (!Bytes.equal(hPrime, h)) {
      throw new GeneralSecurityException("inconsistent");
    }
  }
RsaSsaPss (i.e. RSA Signature Schemes with Appendix (SSA) using PSS encoding) verifying with JCE.
java_src/src/main/java/com/google/crypto/tink/subtle/RsaSsaPssVerifyJce.java