private void noPrefixVerify(final byte[] signature, final byte[] data)
      throws GeneralSecurityException {
    // The algorithm is described at (https://tools.ietf.org/html/rfc8017#section-8.1.2). As
    // signature verification is a public operation,  throwing different exception messages doesn't
    // give attacker any useful information.
    BigInteger e = publicKey.getPublicExponent();
    BigInteger n = publicKey.getModulus();
    int nLengthInBytes = (n.bitLength() + 7) / 8;
    int mLen = (n.bitLength() - 1 + 7) / 8;

    // Step 1. Length checking.
    if (nLengthInBytes != signature.length) {
      throw new GeneralSecurityException("invalid signature's length");
    }

    // Step 2. RSA verification.
    BigInteger s = SubtleUtil.bytes2Integer(signature);
    if (s.compareTo(n) >= 0) {
      throw new GeneralSecurityException("signature out of range");
    }
    BigInteger m = s.modPow(e, n);
    byte[] em = SubtleUtil.integer2Bytes(m, mLen);

    // Step 3. PSS encoding verification.
    emsaPssVerify(data, em, n.bitLength() - 1);
  }
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