Example usage for java.security MessageDigest reset

List of usage examples for java.security MessageDigest reset

Introduction

In this page you can find the example usage for java.security MessageDigest reset.

Prototype

public void reset() 

Source Link

Document

Resets the digest for further use.

Usage

From source file:edu.mayo.informatics.lexgrid.convert.directConversions.UmlsCommon.UMLSBaseCode.java

/**
 * Adds qualification to concepts and associations in the LexGrid
 * repository.// w w  w .  ja  va  2s .  co m
 * 
 * @param aq
 *            Qualification information from the UMLS source.
 * @param constructHCD
 *            Indicates whether artificial context values should be
 *            constructed if not provided in the UMLS information.
 * @param rela
 *            The relationship attribute defined by UMLS (can be empty or
 *            null).
 * @param totalCount
 *            The total number of context links qualified previously.
 * @return The number of contextual links qualified in the repository for
 *         the given UMLS info.
 * @throws SQLException
 */
protected int loadContext(AssociationQualification aq, boolean constructHCD, String rela, int totalCount)
        throws SQLException {
    // If a context identifier was assigned, use it.
    // If a context identifier is not assigned and the option to construct
    // is enabled,
    // derive one based on the root concept code and path to root AUI
    // values.
    int contextLinks = 0;
    String hcd = aq.qualifierValue;
    if (constructHCD && StringUtils.isBlank(hcd) && StringUtils.isNotBlank(aq.pathToRoot)
            && StringUtils.isNotBlank(aq.sourceConceptCode)) {
        MessageDigest md = getSHA1();
        md.reset();
        md.update(aq.pathToRoot.getBytes());
        hcd = String.valueOf(md.digest(aq.sourceConceptCode.getBytes()));
    }
    if (StringUtils.isBlank(hcd))
        return 0;

    // Iterate through the path to root and determine the codes for
    // participating AUIs. We maintain a LRU cache of AUIs to codes to
    // assist.
    // If the associated code is not in the cache, find and cache it here.
    ListOrderedMap orderedPtrAUIToCode = new ListOrderedMap();

    // Break up the path to root into AUIs ...
    String[] auis = aq.pathToRoot.split("\\.");
    if (auis.length > 0) {
        // Check the cache for each. If not found, perform and cache the
        // AUI to code mapping.
        PreparedStatement getPTRCode = umlsConnection2_
                .prepareStatement("SELECT CODE FROM MRCONSO WHERE AUI = ?");
        try {
            String nextCode, nextAUI;
            for (int i = 0; i < auis.length; i++) {
                // Check for registered code in the cache.
                nextAUI = auis[i];
                nextCode = (String) auiToCodeCache_.get(nextAUI);

                // If not cached, perform lookup ...
                if (nextCode == null) {
                    getPTRCode.setString(1, nextAUI);
                    ResultSet ptrCodes = getPTRCode.executeQuery();
                    int count = 0;
                    try {
                        while (ptrCodes.next()) {
                            count++;
                            nextCode = ptrCodes.getString(1);
                        }
                    } finally {
                        ptrCodes.close();
                    }
                    // If one to one mapping (probably should always be, but
                    // doesn't
                    // hurt to check), add to the cache for quick lookup
                    // later...
                    if (count == 1)
                        auiToCodeCache_.put(nextAUI, nextCode);
                }

                // Was it resolved?
                if (nextCode != null)
                    orderedPtrAUIToCode.put(nextAUI, nextCode);
            }
        } finally {
            getPTRCode.close();
        }
    }
    // Ensure we have included the original AUI to code mapping from the
    // provided UMLS qualification info; inserted last as the root
    // of the path.
    orderedPtrAUIToCode.put(aq.sourceConceptAUI, aq.sourceConceptCode);

    // /////////////////////////////////////////////////////////////////////
    // We have all the participating codes and AUIs.
    // Add context qualifiers to the text presentation of each concept
    // based on code/AUI pairs.
    // /////////////////////////////////////////////////////////////////////
    for (OrderedMapIterator omi = orderedPtrAUIToCode.orderedMapIterator(); omi.hasNext();) {
        omi.next();
        String aui = (String) omi.getKey();
        String code = (String) omi.getValue();
        if (code != null)
            qualifyConceptPresentation(code, aui, aq.codingSchemeName, aq.qualifierName, hcd);
    }

    // /////////////////////////////////////////////////////////////////////
    // At this point we have taken care of all the concept qualifiers.
    // Now find and similarly tag each participating association link
    // between AUIs in the path to root chain.
    // /////////////////////////////////////////////////////////////////////

    // Statements to find LexGrid association to concept mappings.
    // Check source to target (parent association as processed)
    // or target to source (child association as processed).

    // Honor the association specified in the MRHIER entry, if provided.
    // For example, the UMLS 'inverse_isa' is mapped on load to 'hasSubtype'
    // association name; account for that here.
    String assoc = mapRela(rela);

    // If a specific relation attribute (rela) was not provided, consider
    // all relevant
    // hierarchical associations (including UMLS standard or source-specific
    // names).
    String assocParam = StringUtils.isNotBlank(assoc) ? '\'' + assoc + '\''
            : toCommaDelimitedWithQuotes(getHierAssocNames(aq.codingSchemeName));

    // Create statements to navigate both directions (up & down the
    // contextual chain).
    PreparedStatement getRelationship_1 = sqlConnection_.prepareStatement(new StringBuffer(
            "SELECT " + SQLTableConstants.TBLCOL_MULTIATTRIBUTESKEY + ", " + stc_.targetEntityCodeOrId + ", "
                    + stc_.entityCodeOrAssociationId + ", " + stc_.sourceEntityCodeOrId + " FROM ")
                            .append(stc_.getTableName(SQLTableConstants.ENTITY_ASSOCIATION_TO_ENTITY))
                            .append(" WHERE " + stc_.sourceEntityCodeOrId + " = ? AND "
                                    + stc_.targetEntityCodeOrId + " = ? AND")
                            .append(" " + stc_.codingSchemeNameOrId + " = ? AND "
                                    + stc_.entityCodeOrAssociationId + " IN (")
                            .append(assocParam).append(")").toString());

    PreparedStatement getRelationship_2 = sqlConnection_.prepareStatement(new StringBuffer(
            "SELECT " + SQLTableConstants.TBLCOL_MULTIATTRIBUTESKEY + ", " + stc_.targetEntityCodeOrId + ", "
                    + stc_.entityCodeOrAssociationId + ", " + stc_.sourceEntityCodeOrId + " FROM ")
                            .append(stc_.getTableName(SQLTableConstants.ENTITY_ASSOCIATION_TO_ENTITY))
                            .append(" WHERE " + stc_.targetEntityCodeOrId + " = ? AND "
                                    + stc_.sourceEntityCodeOrId + " = ? AND")
                            .append(" " + stc_.codingSchemeNameOrId + " = ? AND "
                                    + stc_.entityCodeOrAssociationId + " IN (")
                            .append(assocParam).append(")").toString());

    // Statement to update a multi-attributes key for an association
    // mapping.
    PreparedStatement updateMAK = sqlConnection_.prepareStatement(new StringBuffer("UPDATE ")
            .append(stc_.getTableName(SQLTableConstants.ENTITY_ASSOCIATION_TO_ENTITY))
            .append(" SET " + SQLTableConstants.TBLCOL_MULTIATTRIBUTESKEY + " = ? " + " WHERE "
                    + stc_.codingSchemeNameOrId + " = ?")
            .append(" AND " + stc_.sourceEntityCodeOrId + " = ? AND " + stc_.targetEntityCodeOrId + " = ?")
            .append(" AND " + stc_.entityCodeOrAssociationId + " = ?").toString());

    // Locate and qualify each affected association link with the context ID
    // ...
    try {
        PreparedStatement[] stmts = new PreparedStatement[] { getRelationship_1, getRelationship_2 };
        for (int s = 0; s < stmts.length; s++) {
            PreparedStatement stmt = stmts[s];
            for (int i = orderedPtrAUIToCode.size() - 1; i > 0; i--) {
                String code = (String) orderedPtrAUIToCode.getValue(i);
                String codePrev = (String) orderedPtrAUIToCode.getValue(i - 1);
                stmt.setString(1, code);
                stmt.setString(2, codePrev);
                stmt.setString(3, aq.codingSchemeName);

                ResultSet results = stmt.executeQuery();
                try {
                    // Iterate through all relevant association links ...
                    while (results.next()) {
                        String multiAttributesKey = results
                                .getString(SQLTableConstants.TBLCOL_MULTIATTRIBUTESKEY);
                        String targetConceptCode = results.getString(stc_.targetEntityCodeOrId);
                        String sourceConceptCode = results.getString(stc_.sourceEntityCodeOrId);
                        String association = results.getString(stc_.entityCodeOrAssociationId);

                        // If there is no key to correlate to the
                        // multi-attributes table,
                        // construct and add now.
                        if (multiAttributesKey == null) {
                            StringBuffer key = new StringBuffer().append(System.currentTimeMillis())
                                    .append((int) Math.floor((Math.random() * 100000))).append(totalCount);
                            multiAttributesKey = key.substring(0, Math.min(50, key.length()));
                            updateMAK.setString(1, multiAttributesKey);
                            updateMAK.setString(2, aq.codingSchemeName);
                            updateMAK.setString(3, sourceConceptCode);
                            updateMAK.setString(4, targetConceptCode);
                            updateMAK.setString(5, association);
                            updateMAK.execute();
                        }

                        // Add a context qualifier to the multi-attributes
                        // table.
                        try {
                            addEntityAssociationQualifierToEntityAssociation(aq.codingSchemeName,
                                    multiAttributesKey, aq.qualifierName, hcd);
                            contextLinks++;
                        } catch (SQLException e) {
                            // Because we qualify all relationships along
                            // the PTR and
                            // the HCD is identical for siblings at the same
                            // PTR some
                            // exceptions with regards to identical keys
                            // will come up.

                            // We try to bypass altogether if the message
                            // indicates duplication.
                            // However, message text can vary based on the
                            // database engine.
                            // Rather than exit in error, log the message
                            // and continue.
                            if (!e.getMessage().contains("Duplicate")) {
                                messages_.warn("Unable to add context qualifier to association.", e);
                            }
                        }
                    }
                } finally {
                    results.close();
                }
            }
        }
    } finally {
        updateMAK.close();
        getRelationship_1.close();
        getRelationship_2.close();
    }
    return contextLinks;
}

From source file:com.cloud.network.NetworkModelImpl.java

@Override
public List<String[]> generateVmData(String userData, String serviceOffering, String zoneName, String vmName,
        long vmId, String publicKey, String password, Boolean isWindows) {
    final List<String[]> vmData = new ArrayList<String[]>();

    if (userData != null) {
        vmData.add(new String[] { "userdata", "user-data",
                new String(Base64.decodeBase64(userData), StringUtils.getPreferredCharset()) });
    }//from w  w  w.java  2s  . c o  m
    vmData.add(new String[] { "metadata", "service-offering", StringUtils.unicodeEscape(serviceOffering) });
    vmData.add(new String[] { "metadata", "availability-zone", StringUtils.unicodeEscape(zoneName) });
    vmData.add(new String[] { "metadata", "local-hostname", StringUtils.unicodeEscape(vmName) });
    vmData.add(new String[] { "metadata", "instance-id", vmName });
    vmData.add(new String[] { "metadata", "vm-id", String.valueOf(vmId) });
    vmData.add(new String[] { "metadata", "public-keys", publicKey });

    String cloudIdentifier = _configDao.getValue("cloud.identifier");
    if (cloudIdentifier == null) {
        cloudIdentifier = "";
    } else {
        cloudIdentifier = "CloudStack-{" + cloudIdentifier + "}";
    }
    vmData.add(new String[] { "metadata", "cloud-identifier", cloudIdentifier });

    if (password != null && !password.isEmpty() && !password.equals("saved_password")) {

        // Here we are calculating MD5 checksum to reduce the over head of calculating MD5 checksum
        // in windows VM in password reset script.

        if (isWindows) {
            MessageDigest md5 = null;
            try {
                md5 = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                s_logger.error("Unexpected exception " + e.getMessage(), e);
                throw new CloudRuntimeException("Unable to get MD5 MessageDigest", e);
            }
            md5.reset();
            md5.update(password.getBytes(StringUtils.getPreferredCharset()));
            byte[] digest = md5.digest();
            BigInteger bigInt = new BigInteger(1, digest);
            String hashtext = bigInt.toString(16);

            vmData.add(new String[] { "password", "vm-password-md5checksum", hashtext });
        }

        vmData.add(new String[] { "password", "vm-password", password });
    }

    return vmData;
}

From source file:Hash.Hash.java

public static String getHash(File file) throws FileNotFoundException, IOException {
    MessageDigest md5Digest = null;
    try {//from   w ww.  j  a  va 2  s .c  o  m
        md5Digest = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException ex) {
        return EMPTYHASH;
    }
    byte[] digestDef = md5Digest.digest();
    byte[] digest = null;
    String ext = FilenameUtils.getExtension(file.getName().toLowerCase());
    if (ext.equals("arw") || ext.equals("nef") || ext.equals("dng") || ext.equals("tif")
            || ext.equals("tiff")) {
        // <editor-fold defaultstate="collapsed" desc="raw">
        try (BufferedInputStream rawIn = new BufferedInputStream(new FileInputStream(file.toString()));) {
            digest = startOfScanTiff(file, rawIn);
        } catch (IOException e) {
            errorOut("Hash", e);
        }
        // </editor-fold>            
    } else {
        try (FileInputStream fileInStream = new FileInputStream(file.toString());
                BufferedInputStream fileStream = new BufferedInputStream(fileInStream);
                DigestInputStream in = new DigestInputStream(fileStream, md5Digest);) {
            byte[] buffer = new byte[4096];
            long length;
            switch (ext) {
            // <editor-fold defaultstate="collapsed" desc="mp4">
            case "mp4":
                in.on(false);
                boolean EOF = false;
                do {
                    length = readEndianValue(in, 4, false) - 8;
                    //                        byte boxLength[] = readBytes(in, 4);
                    String desc = new String(readBytes(in, 4));
                    //                        ByteBuffer wrapped = ByteBuffer.wrap(boxLength); // big-endian by default
                    //                        length = wrapped.getInt() - 8;
                    if (length == -7) {
                        //largesize
                        length = readEndianValue(in, 8, false) - 16;
                    } else if (length == -8) {
                        // until eof
                        EOF = true;
                    } else if (length == in.available()) {
                        EOF = true;
                    }
                    if (desc.equals("mdat")) {
                        in.on(true);
                        while (buffer.length <= length) {
                            int read = in.read(buffer);
                            length -= read;
                        }
                        while (length > 0) {
                            int read = in.read(buffer, 0, (int) length);
                            if (read == -1) {
                                digest = null;
                                break;
                            }
                            length -= read;
                        }
                        //                            break;
                    } else {
                        if (!skipBytes(in, length))
                            break;
                    }
                } while (!EOF);
                digest = md5Digest.digest();
                break;
            // </editor-fold>
            // <editor-fold defaultstate="collapsed" desc="JPG">
            case "jpg":
            case "jpeg":
                int scanLength;
                int scanLengthOld = 0;
                do {
                    in.on(false);
                    md5Digest.reset();
                    scanLength = 0;
                    if (startOfScanJPG(fileStream) == -1)
                        break;
                    in.on(true);
                    int c = 0;
                    int oldc;
                    do {
                        oldc = c;
                        c = in.read();
                        if (c == -1) {
                            return EMPTYHASH;
                        }
                        scanLength++;
                    } while (!(oldc == 0xFF && c == 0xD9/*217*/));
                    if (scanLength > scanLengthOld) {
                        digest = md5Digest.digest();
                        scanLengthOld = scanLength;
                    }
                } while (scanLengthOld < in.available());
                break;
            // </editor-fold>
            default:
                in.on(true);
                while (in.read(buffer) != -1) {
                }
                digest = md5Digest.digest();
                break;
            }
        } catch (IOException e) {
            errorOut("Hash", e);
        }
    }
    if (digest == null) {
        return EMPTYHASH;
    }
    if (Arrays.equals(digest, digestDef)) {
        return EMPTYHASH;
    }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < digest.length; ++i) {
        sb.append(Integer.toHexString((digest[i] & 0xFF) | 0x100).substring(1, 3));
    }
    return sb.toString();
}

From source file:org.zywx.wbpalmstar.engine.universalex.EUExWindow.java

public void openAdMsg(String[] parm) {
    EBrowserWindow curWind = mBrwView.getBrowserWindow();
    if (null == curWind) {
        return;//from   ww  w  . jav  a 2s.  co m
    }
    WWidgetData wd = mBrwView.getCurrentWidget();
    boolean b1 = mBrwView.checkType(EBrwViewEntry.VIEW_TYPE_MAIN);
    boolean b2 = 0 == wd.m_widgetAdStatus;
    boolean b3 = parm.length < 4;
    if (!b1 || b2 || b3) {
        // 0 means do not show ad
        return;
    }
    String inType = parm[0];
    String inDTime = parm[1];
    String inInterval = parm[2];
    String inFlag = parm[3];
    int type = 0, flag = 0, dtime = 0, interval = 0, w = RelativeLayout.LayoutParams.FILL_PARENT, h = 50;
    MessageDigest md = null;
    int density = ESystemInfo.getIntence().mDensityDpi;
    switch (density) {
    case DisplayMetrics.DENSITY_LOW:
        h = 40;
        break;
    case DisplayMetrics.DENSITY_MEDIUM:
        h = 50;
        break;
    case DisplayMetrics.DENSITY_HIGH:
        h = 60;
        break;
    case 320: // DisplayMetrics.DENSITY_XHIGH from 2.3.3
        h = 70;
        break;
    }
    try {
        if (null != inType && inType.length() != 0) {
            type = Integer.parseInt(inType);
        }

        if (null != inDTime && inDTime.length() != 0) {
            dtime = Integer.parseInt(inDTime);
        }

        if (null != inInterval && inInterval.length() != 0) {
            interval = Integer.parseInt(inInterval);
        }

        if (null != inFlag && inFlag.length() != 0) {
            flag = Integer.parseInt(inFlag);
        }
        md = MessageDigest.getInstance("MD5");
    } catch (Exception e) {
        errorCallback(0, EUExCallback.F_E_UEXWINDOW_EVAL, "Illegal parameter");
        return;
    }
    StringBuffer sb = new StringBuffer(m_AdUrl);
    sb.append("?appid=");
    sb.append(wd.m_appId);
    sb.append("&pt=1");
    sb.append("&dw=");
    sb.append(ESystemInfo.getIntence().mWidthPixels);
    sb.append("&dh=");
    sb.append(ESystemInfo.getIntence().mHeightPixels);
    sb.append("&md5=");
    if (null == md) {
        return;
    }
    String jid = wd.m_appId + "BD7463CD-D608-BEB4-C633-EF3574213060";
    md.reset();
    md.update(jid.getBytes());
    byte[] md5Bytes = md.digest();
    StringBuffer hexValue = new StringBuffer();
    for (int i = 0; i < md5Bytes.length; i++) {
        int val = ((int) md5Bytes[i]) & 0xff;
        if (val < 16)
            hexValue.append("0");
        hexValue.append(Integer.toHexString(val));
    }
    sb.append(hexValue);
    sb.append("&type=");
    if (type == 1) {
        sb.append(1);
        h = w;
    } else {
        sb.append(0);
    }
    String url = sb.toString();
    curWind.openAd(type, url, dtime * 1000, h, w, interval * 1000, flag);
}

From source file:org.opends.server.extensions.Sha2Crypt.java

/**
 * Generates a libc6 crypt() compatible "$5$" or "$6$" SHA2 based hash value.
 * <p>//w  ww  . j a v  a  2  s .c  om
 * This is a nearly line by line conversion of the original C function. The numbered comments are from the algorithm
 * description, the short C-style ones from the original C code and the ones with "Remark" from me.
 * <p>
 * See {@link Crypt#crypt(String, String)} for details.
 *
 * @param keyBytes
 *            plaintext to hash
 * @param salt
 *            real salt value without prefix or "rounds="
 * @param saltPrefix
 *            either $5$ or $6$
 * @param blocksize
 *            a value that differs between $5$ and $6$
 * @param algorithm
 *            {@link MessageDigest} algorithm identifier string
 * @return complete hash value including prefix and salt
 * @throws IllegalArgumentException
 *             if the given salt is <code>null</code> or does not match the allowed pattern
 * @throws IllegalArgumentException
 *             when a {@link NoSuchAlgorithmException} is caught
 * @see MessageDigestAlgorithms
 */
private static String sha2Crypt(final byte[] keyBytes, final String salt, final String saltPrefix,
        final int blocksize, final String algorithm) {

    final int keyLen = keyBytes.length;

    // Extracts effective salt and the number of rounds from the given salt.
    int rounds = ROUNDS_DEFAULT;
    boolean roundsCustom = false;
    if (salt == null) {
        throw new IllegalArgumentException("Salt must not be null");
    }

    final Matcher m = SALT_PATTERN.matcher(salt);
    if (!m.find()) {
        throw new IllegalArgumentException("Invalid salt value: " + salt);
    }
    if (m.group(3) != null) {
        rounds = Integer.parseInt(m.group(3));
        rounds = Math.max(ROUNDS_MIN, Math.min(ROUNDS_MAX, rounds));
        roundsCustom = true;
    }
    final String saltString = m.group(4);
    final byte[] saltBytes = saltString.getBytes(StandardCharsets.UTF_8);
    final int saltLen = saltBytes.length;

    // 1. start digest A
    // Prepare for the real work.
    MessageDigest ctx = getDigest(algorithm);

    // 2. the password string is added to digest A
    /*
     * Add the key string.
     */
    ctx.update(keyBytes);

    // 3. the salt string is added to digest A. This is just the salt string
    // itself without the enclosing '$', without the magic salt_prefix $5$ and
    // $6$ respectively and without the rounds=<N> specification.
    //
    // NB: the MD5 algorithm did add the $1$ salt_prefix. This is not deemed
    // necessary since it is a constant string and does not add security
    // and /possibly/ allows a plain text attack. Since the rounds=<N>
    // specification should never be added this would also create an
    // inconsistency.
    /*
     * The last part is the salt string. This must be at most 16 characters and it ends at the first `$' character
     * (for compatibility with existing implementations).
     */
    ctx.update(saltBytes);

    // 4. start digest B
    /*
     * Compute alternate sha512 sum with input KEY, SALT, and KEY. The final result will be added to the first
     * context.
     */
    MessageDigest altCtx = getDigest(algorithm);

    // 5. add the password to digest B
    /*
     * Add key.
     */
    altCtx.update(keyBytes);

    // 6. add the salt string to digest B
    /*
     * Add salt.
     */
    altCtx.update(saltBytes);

    // 7. add the password again to digest B
    /*
     * Add key again.
     */
    altCtx.update(keyBytes);

    // 8. finish digest B
    /*
     * Now get result of this (32 bytes) and add it to the other context.
     */
    byte[] altResult = altCtx.digest();

    // 9. For each block of 32 or 64 bytes in the password string (excluding
    // the terminating NUL in the C representation), add digest B to digest A
    /*
     * Add for any character in the key one byte of the alternate sum.
     */
    /*
     * (Remark: the C code comment seems wrong for key length > 32!)
     */
    int cnt = keyBytes.length;
    while (cnt > blocksize) {
        ctx.update(altResult, 0, blocksize);
        cnt -= blocksize;
    }

    // 10. For the remaining N bytes of the password string add the first
    // N bytes of digest B to digest A
    ctx.update(altResult, 0, cnt);

    // 11. For each bit of the binary representation of the length of the
    // password string up to and including the highest 1-digit, starting
    // from to lowest bit position (numeric value 1):
    //
    // a) for a 1-digit add digest B to digest A
    //
    // b) for a 0-digit add the password string
    //
    // NB: this step differs significantly from the MD5 algorithm. It
    // adds more randomness.
    /*
     * Take the binary representation of the length of the key and for every 1 add the alternate sum, for every 0
     * the key.
     */
    cnt = keyBytes.length;
    while (cnt > 0) {
        if ((cnt & 1) != 0) {
            ctx.update(altResult, 0, blocksize);
        } else {
            ctx.update(keyBytes);
        }
        cnt >>= 1;
    }

    // 12. finish digest A
    /*
     * Create intermediate result.
     */
    altResult = ctx.digest();

    // 13. start digest DP
    /*
     * Start computation of P byte sequence.
     */
    altCtx = getDigest(algorithm);

    // 14. for every byte in the password (excluding the terminating NUL byte
    // in the C representation of the string)
    //
    // add the password to digest DP
    /*
     * For every character in the password add the entire password.
     */
    for (int i = 1; i <= keyLen; i++) {
        altCtx.update(keyBytes);
    }

    // 15. finish digest DP
    /*
     * Finish the digest.
     */
    byte[] tempResult = altCtx.digest();

    // 16. produce byte sequence P of the same length as the password where
    //
    // a) for each block of 32 or 64 bytes of length of the password string
    // the entire digest DP is used
    //
    // b) for the remaining N (up to 31 or 63) bytes use the first N
    // bytes of digest DP
    /*
     * Create byte sequence P.
     */
    final byte[] pBytes = new byte[keyLen];
    int cp = 0;
    while (cp < keyLen - blocksize) {
        System.arraycopy(tempResult, 0, pBytes, cp, blocksize);
        cp += blocksize;
    }
    System.arraycopy(tempResult, 0, pBytes, cp, keyLen - cp);

    // 17. start digest DS
    /*
     * Start computation of S byte sequence.
     */
    altCtx = getDigest(algorithm);

    // 18. repeast the following 16+A[0] times, where A[0] represents the first
    // byte in digest A interpreted as an 8-bit unsigned value
    //
    // add the salt to digest DS
    /*
     * For every character in the password add the entire password.
     */
    for (int i = 1; i <= 16 + (altResult[0] & 0xff); i++) {
        altCtx.update(saltBytes);
    }

    // 19. finish digest DS
    /*
     * Finish the digest.
     */
    tempResult = altCtx.digest();

    // 20. produce byte sequence S of the same length as the salt string where
    //
    // a) for each block of 32 or 64 bytes of length of the salt string
    // the entire digest DS is used
    //
    // b) for the remaining N (up to 31 or 63) bytes use the first N
    // bytes of digest DS
    /*
     * Create byte sequence S.
     */
    // Remark: The salt is limited to 16 chars, how does this make sense?
    final byte[] sBytes = new byte[saltLen];
    cp = 0;
    while (cp < saltLen - blocksize) {
        System.arraycopy(tempResult, 0, sBytes, cp, blocksize);
        cp += blocksize;
    }
    System.arraycopy(tempResult, 0, sBytes, cp, saltLen - cp);

    // 21. repeat a loop according to the number specified in the rounds=<N>
    // specification in the salt (or the default value if none is
    // present). Each round is numbered, starting with 0 and up to N-1.
    //
    // The loop uses a digest as input. In the first round it is the
    // digest produced in step 12. In the latter steps it is the digest
    // produced in step 21.h. The following text uses the notation
    // "digest A/C" to describe this behavior.
    /*
     * Repeatedly run the collected hash value through sha512 to burn CPU cycles.
     */
    for (int i = 0; i <= rounds - 1; i++) {
        // a) start digest C
        /*
         * New context.
         */
        ctx = getDigest(algorithm);

        // b) for odd round numbers add the byte sequence P to digest C
        // c) for even round numbers add digest A/C
        /*
         * Add key or last result.
         */
        if ((i & 1) != 0) {
            ctx.update(pBytes, 0, keyLen);
        } else {
            ctx.update(altResult, 0, blocksize);
        }

        // d) for all round numbers not divisible by 3 add the byte sequence S
        /*
         * Add salt for numbers not divisible by 3.
         */
        if (i % 3 != 0) {
            ctx.update(sBytes, 0, saltLen);
        }

        // e) for all round numbers not divisible by 7 add the byte sequence P
        /*
         * Add key for numbers not divisible by 7.
         */
        if (i % 7 != 0) {
            ctx.update(pBytes, 0, keyLen);
        }

        // f) for odd round numbers add digest A/C
        // g) for even round numbers add the byte sequence P
        /*
         * Add key or last result.
         */
        if ((i & 1) != 0) {
            ctx.update(altResult, 0, blocksize);
        } else {
            ctx.update(pBytes, 0, keyLen);
        }

        // h) finish digest C.
        /*
         * Create intermediate result.
         */
        altResult = ctx.digest();
    }

    // 22. Produce the output string. This is an ASCII string of the maximum
    // size specified above, consisting of multiple pieces:
    //
    // a) the salt salt_prefix, $5$ or $6$ respectively
    //
    // b) the rounds=<N> specification, if one was present in the input
    // salt string. A trailing '$' is added in this case to separate
    // the rounds specification from the following text.
    //
    // c) the salt string truncated to 16 characters
    //
    // d) a '$' character
    /*
     * Now we can construct the result string. It consists of three parts.
     */
    final StringBuilder buffer = new StringBuilder(saltPrefix);
    if (roundsCustom) {
        buffer.append(ROUNDS_PREFIX);
        buffer.append(rounds);
        buffer.append("$");
    }
    buffer.append(saltString);
    buffer.append("$");

    // e) the base-64 encoded final C digest. The encoding used is as
    // follows:
    // [...]
    //
    // Each group of three bytes from the digest produces four
    // characters as output:
    //
    // 1. character: the six low bits of the first byte
    // 2. character: the two high bits of the first byte and the
    // four low bytes from the second byte
    // 3. character: the four high bytes from the second byte and
    // the two low bits from the third byte
    // 4. character: the six high bits from the third byte
    //
    // The groups of three bytes are as follows (in this sequence).
    // These are the indices into the byte array containing the
    // digest, starting with index 0. For the last group there are
    // not enough bytes left in the digest and the value zero is used
    // in its place. This group also produces only three or two
    // characters as output for SHA-512 and SHA-512 respectively.

    // This was just a safeguard in the C implementation:
    // int buflen = salt_prefix.length() - 1 + ROUNDS_PREFIX.length() + 9 + 1 + salt_string.length() + 1 + 86 + 1;

    if (blocksize == 32) {
        B64.b64from24bit(altResult[0], altResult[10], altResult[20], 4, buffer);
        B64.b64from24bit(altResult[21], altResult[1], altResult[11], 4, buffer);
        B64.b64from24bit(altResult[12], altResult[22], altResult[2], 4, buffer);
        B64.b64from24bit(altResult[3], altResult[13], altResult[23], 4, buffer);
        B64.b64from24bit(altResult[24], altResult[4], altResult[14], 4, buffer);
        B64.b64from24bit(altResult[15], altResult[25], altResult[5], 4, buffer);
        B64.b64from24bit(altResult[6], altResult[16], altResult[26], 4, buffer);
        B64.b64from24bit(altResult[27], altResult[7], altResult[17], 4, buffer);
        B64.b64from24bit(altResult[18], altResult[28], altResult[8], 4, buffer);
        B64.b64from24bit(altResult[9], altResult[19], altResult[29], 4, buffer);
        B64.b64from24bit((byte) 0, altResult[31], altResult[30], 3, buffer);
    } else {
        B64.b64from24bit(altResult[0], altResult[21], altResult[42], 4, buffer);
        B64.b64from24bit(altResult[22], altResult[43], altResult[1], 4, buffer);
        B64.b64from24bit(altResult[44], altResult[2], altResult[23], 4, buffer);
        B64.b64from24bit(altResult[3], altResult[24], altResult[45], 4, buffer);
        B64.b64from24bit(altResult[25], altResult[46], altResult[4], 4, buffer);
        B64.b64from24bit(altResult[47], altResult[5], altResult[26], 4, buffer);
        B64.b64from24bit(altResult[6], altResult[27], altResult[48], 4, buffer);
        B64.b64from24bit(altResult[28], altResult[49], altResult[7], 4, buffer);
        B64.b64from24bit(altResult[50], altResult[8], altResult[29], 4, buffer);
        B64.b64from24bit(altResult[9], altResult[30], altResult[51], 4, buffer);
        B64.b64from24bit(altResult[31], altResult[52], altResult[10], 4, buffer);
        B64.b64from24bit(altResult[53], altResult[11], altResult[32], 4, buffer);
        B64.b64from24bit(altResult[12], altResult[33], altResult[54], 4, buffer);
        B64.b64from24bit(altResult[34], altResult[55], altResult[13], 4, buffer);
        B64.b64from24bit(altResult[56], altResult[14], altResult[35], 4, buffer);
        B64.b64from24bit(altResult[15], altResult[36], altResult[57], 4, buffer);
        B64.b64from24bit(altResult[37], altResult[58], altResult[16], 4, buffer);
        B64.b64from24bit(altResult[59], altResult[17], altResult[38], 4, buffer);
        B64.b64from24bit(altResult[18], altResult[39], altResult[60], 4, buffer);
        B64.b64from24bit(altResult[40], altResult[61], altResult[19], 4, buffer);
        B64.b64from24bit(altResult[62], altResult[20], altResult[41], 4, buffer);
        B64.b64from24bit((byte) 0, (byte) 0, altResult[63], 2, buffer);
    }

    /*
     * Clear the buffer for the intermediate result so that people attaching to processes or reading core dumps
     * cannot get any information.
     */
    // Is there a better way to do this with the JVM?
    Arrays.fill(tempResult, (byte) 0);
    Arrays.fill(pBytes, (byte) 0);
    Arrays.fill(sBytes, (byte) 0);
    ctx.reset();
    altCtx.reset();
    Arrays.fill(keyBytes, (byte) 0);
    Arrays.fill(saltBytes, (byte) 0);

    return buffer.toString();
}