Package com.atlassian.aquatic

Source Code of com.atlassian.aquatic.AquaticPrimeLicenseGeneratorImpl

package com.atlassian.aquatic;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.spec.RSAPrivateKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
* Implementation of the AquaticPrime license generator.  This is based on the Python/PHP versions,
* however it uses the Java-provided RSA implementation rather then their roll-your-own.
*
* See AquaticPrime.py and AquaticPrimeLicenseGeneratorTest in the test module.

* Copyright Atlassian: 17/11/11
*/
public class AquaticPrimeLicenseGeneratorImpl implements AquaticPrimeLicenseGenerator
{
    private final BigInteger privateKey;
    private final BigInteger publicKey;

    public AquaticPrimeLicenseGeneratorImpl(String privateKey, String publicKey)
    {
        this.privateKey = new BigInteger(privateKey, 16);
        this.publicKey = new BigInteger(publicKey, 16);
    }

    public AquaticPrimeLicense generateLicense(Map<String, String> properties)
        throws LicenseGenerationException
    {
        // It's not clear whether these implementations are thread-safe so generate here...
        MessageDigest sha1Digest;
        Cipher rsaCipher;
        try {
            sha1Digest = MessageDigest.getInstance("SHA1");

            RSAPrivateKeySpec privkey = new RSAPrivateKeySpec(publicKey, privateKey);
            KeyFactory factory = KeyFactory.getInstance("RSA");
            rsaCipher = Cipher.getInstance("RSA");
            rsaCipher.init(Cipher.ENCRYPT_MODE, factory.generatePrivate(privkey));

        } catch (Exception e) { // javax.security.* throws a lot of exceptions
            throw new LicenseGenerationException(e);
        }


        /*
         * The AquaticPrime algorithm is as follows:
         *
         * - Concatenate the properties values in key order
         * - Convert to UTF-8 byte stream
         * - Hash with unsalted SHA1
         * - Encrypt with RSA
         *
         * NOTE: Although the Python and PHP versions profess to escape
         * apostrophes as '\'', in practice this is wrong. The XML document
         * includes the unescaped string, and thus the decrypted hash won't
         * match on verification. In practice the PHP version doesn't use the
         * escaped string *when the compiled signer is missing*, which is
         * presumably always the case.
         */
        List<String> keys = new ArrayList<String>(properties.keySet());
        Collections.sort(keys);

        // Concatenate the properties together in key-order
        StringBuilder str = new StringBuilder();
        for (String key : keys) {
            str.append(properties.get(key));
        }

        try {
            // Encode as UTF-8
            byte[] data = str.toString()
                // .replace("'", "'\\''")  // Not actually needed, see above
                .getBytes("UTF-8");

            // Hash, encrypt and base64 encode
            data = sha1Digest.digest(data);
            data = rsaCipher.doFinal(data);
            data = Base64.encodeBase64Chunked(data);

            return new AquaticPrimeLicense(properties, data);

        } catch (Exception e) {
            throw new LicenseGenerationException(e);
        }
    }
}
TOP

Related Classes of com.atlassian.aquatic.AquaticPrimeLicenseGeneratorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.