package org.bdware.sc.encrypt;

import org.bdware.sc.conn.ByteUtil;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;

public class RSA {
    public BigInteger n, e1, e2;

    private RSA() {
    }

    public static KeyPair buildKeyPair() {
        try {
            final int keySize = 2048;
            SecureRandom random = new SecureRandom();
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(keySize, random);
            return keyPairGenerator.genKeyPair();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static RSA generate() {
        KeyPair keyPair = buildKeyPair();
        return generate(keyPair);
    }

//    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    public static RSA generate(KeyPair pair) {
        if (null == pair) {
            pair = buildKeyPair();
        }
        if (null == pair) {
            throw new NullPointerException();
        }
        RSAPublicKey pubKey = (RSAPublicKey) pair.getPublic();
        RSA ret = new RSA();
        ret.n = pubKey.getModulus();
        ret.e1 = pubKey.getPublicExponent();
        RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) pair.getPrivate();
        ret.e2 = privateKey.getPrivateExponent();
        return ret;
    }

    //	public String sign(byte[] content) {
    //		try {
    //			KeyFactory keyf = KeyFactory.getInstance("RSA");
    //			PrivateKey priKey = keyf.generatePrivate(priPKCS8);
    //			java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
    //			signature.initSign(priKey);
    //			signature.update(content);
    //			byte[] signed = signature.sign();
    //			return new BASE64Encoder().encode(signed);
    //		} catch (Exception e) {
    //			e.printStackTrace();
    //		}
    //		return null;
    //	}

    //	public static boolean doCheck(byte[] content, String sign) {
    //		try {
    //			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    //			byte[] encodedKey = Base64.decode(publicKey);
    //			PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    //
    //			java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
    //
    //			signature.initVerify(pubKey);
    //			signature.update(content.getBytes());
    //
    //			boolean bverify = signature.verify(Base64.decode(sign));
    //			return bverify;
    //
    //		} catch (Exception e) {
    //			e.printStackTrace();
    //		}
    //
    //		return false;
    //	}

    public static RSA generateFromBase64(String base64) {
        try {
            byte[] bytes = ByteUtil.decodeBASE64(base64);
            RSA rsa = new RSA();
            if (null == bytes) {
                throw new NullPointerException();
            }
            String str = new String(bytes);
            String[] strArr = str.split(",");
            rsa.n = new BigInteger(strArr[0], 16);
            rsa.e1 = new BigInteger(strArr[1], 16);
            rsa.e2 = new BigInteger(strArr[2], 16);
            return rsa;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String str2Hex(String str) {
        if (str.length() % 2 == 1) str = "0" + str;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length() / 2; i++) {
            char c = (char) (16 * code(str.charAt(i * 2)) + code(str.charAt(i * 2 + 1)));
            sb.append(c);
        }
        return sb.toString();
    }

    private static int code(char c) {
        if (c >= '0' && c <= '9') return c - '0';
        else return c - 'a' + 10;
    }

    public String toBase64Pubkey() {
        String data = n.toString(16) + "," + e1.toString(16) + "," + 0;
        return ByteUtil.encodeBASE64(data.getBytes()).replaceAll("\n", "");
    }

    public String toBase64() {
        String data = n.toString(16) + "," + e1.toString(16) + "," + e2.toString(16);
        return ByteUtil.encodeBASE64(data.getBytes()).replaceAll("\n", "");
    }

    public BigInteger bytesToBI(byte[] bytes) {
        return new BigInteger(1, bytes);
    }

    public byte[] bIToBytes(BigInteger bi) {
        int len = (bi.bitLength() + 7) >> 3;
        byte[] b = bi.toByteArray();
        int n = b.length;
        if (n == len) {
            return b;
        }
        // BigInteger prefixed a 0x00 byte for 2's complement form, remove it
        if ((n == len + 1) && (b[0] == 0)) {
            byte[] t = new byte[len];
            System.arraycopy(b, 1, t, 0, len);
            return t;
        }
        // must be smaller
        assert (n < len);
        byte[] t = new byte[len];
        System.arraycopy(b, 0, t, (len - n), n);
        return t;
    }

    public byte[] encode(byte[] plain) {
        return bIToBytes(bytesToBI(plain).modPow(e1, n));
    }

    public byte[] decode(byte[] plain) {
        System.out.println("[RSA] decode bytearray:" + Arrays.hashCode(plain));
        return bIToBytes(bytesToBI(plain).modPow(e2, n));
    }

    public String encode(BigInteger bi) {
        String str = bi.modPow(e1, n).toString(16);
        System.out.println("[RSA.java] encodeStr:" + str);
        return str2Hex(str);
    }

    public String decode(BigInteger bi) {
        String str = bi.modPow(e2, n).toString(16);
        System.out.println("[RSA.java] decodeStr:" + str);
        return str2Hex(str);
    }
}
