public class IBEEngine {

    /**
     * 初始化一个IBE系统
     * Setup an IBE system
     * @param pairingIn 椭圆函数参数 the pairing
     * @return 系统参数 如果椭圆函数参数非法或者出现其它错误返回null
     * returns the IBESystemParameter object when success, otherwise return null
     */
    public static IBESystemParameter setup(byte[] pairingIn) {
        byte[] alphaOut = new byte[IBELibrary.PBC_ZR_SIZE];
        byte[] gOut = new byte[IBELibrary.PBC_G_SIZE];
        byte[] g1Out = new byte[IBELibrary.PBC_G_SIZE];
        byte[] hOut = new byte[IBELibrary.PBC_G_SIZE];
        int succ = IBELibrary.setup(alphaOut, gOut, g1Out, hOut, pairingIn);
        if (succ == 0) {
            IBEPublicParameter publicParameter = new IBEPublicParameter();
            publicParameter.setParamG(gOut);
            publicParameter.setParamG1(g1Out);
            publicParameter.setParamH(hOut);
            publicParameter.setPairing(pairingIn);
            IBESystemParameter parameter = new IBESystemParameter();
            parameter.setMasterKey(alphaOut);
            parameter.setPublicParameter(publicParameter);
            return parameter;
        }
        return null;
    }

    /**
     * 为一个用户生成私钥
     * Generate a private key for a user
     * @param system IBE系统 the system parameter
     * @param user 私钥请求用户 the user
     * @return 用户的私钥 the generated private key
     */
    public static IBEPrivateKey keygen(IBESystemParameter system, String user) {
        byte[] hIDOut = new byte[IBELibrary.PBC_G_SIZE];
        byte[] rIDOut = new byte[IBELibrary.PBC_ZR_SIZE];
        try {
            int succ = IBELibrary.keygen(hIDOut, rIDOut, user.getBytes(IBEConstraints.USER_STRING_ENCODING), system.getMasterKey(),
                                         system.getPublicParameter().getParamG(), system.getPublicParameter().getParamH(), system.getPublicParameter().getPairing());
            if (succ != 0)
                throw new IOException("Cannot generate private key for user:" + user);
            IBEPrivateKey privateKey = new IBEPrivateKey();
            privateKey.sethID(hIDOut);
            privateKey.setrID(rIDOut);
            privateKey.setUserString(user);
            privateKey.setPairing(system.getPublicParameter().getPairing());
            return privateKey;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 对数据进行加密
     * encryption
     * @param publicParameter 接收方公共参数 the recipients' public key
     * @param plainText 明文 the plain text
     * @param receiver 接收着身份 the recipient
     * @return 密文 cipher text
     */
    public static IBECipherText encrypt(IBEPublicParameter publicParameter, IBEPlainText plainText, String receiver) {
        byte[] cipherBufferOut = new byte[IBELibrary.PBC_G_SIZE * 3];
        try {
            int succ = IBELibrary.encrypt(cipherBufferOut, plainText.getContent(), publicParameter.getParamG(), publicParameter.getParamG1(),
                                          publicParameter.getParamH(), receiver.getBytes(IBEConstraints.USER_STRING_ENCODING), publicParameter.getPairing());
            if (succ != 0)
                throw new IOException("Cannot encrypt messege for user:" + receiver);
            IBECipherText cipherText = new IBECipherText();
            cipherText.setUvw(cipherBufferOut);
            cipherText.setLength(plainText.getLength());
            return cipherText;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 对密文解密
     * decryption
     * @param cipherText 密文
     * @param privateKey 接收方私钥
     * @return 解密后明文 plain text
     */
    public static IBEPlainText decrypt(IBECipherText cipherText, IBEPrivateKey privateKey) {
        byte[] plainBufferOut = new byte[IBELibrary.PBC_G_SIZE];
        int i = IBELibrary.decrypt(plainBufferOut, cipherText.getUvw(), privateKey.getrID(), privateKey.gethID(), privateKey.getPairing());
        if (i == 0) {
            IBEPlainText plainText = new IBEPlainText() {
                private static final long serialVersionUID = -2705082103669151761L;
            };
            plainText.setContent(plainBufferOut);
            plainText.setLength(cipherText.getLength());
            return plainText;
        }
        return null;
    }

    /**
     * 生成证书
     * Generate certificate
     * @param user 证书所有者 certificate owner
     * @param root 签名用根证书 如果生成根证书 则传入一个只包含椭圆函数信息的证书对象
     * the root certificate, if this paramater only contains a pairing, then this method will generate a root certificate
     * @param validAfter 有效期开始日期 the certificate effective start date
     * @param period 有效时间 单位毫秒 the certificate valid time period, in millisecond.
     * @return 生成的证书 the certificate
     */
    public static IBSCertificate generateCertificate(String user, IBSCertificate root, Date validAfter, long period) {
        IBSCertificate root0 = root;
        IBESystemParameter parameter = setup(root0.getPublicParameter().getPairing());
        IBSCertificate certificate = new IBSCertificate();
        certificate.setMasterKey(parameter.getMasterKey());
        certificate.setPublicParameter(parameter.getPublicParameter());
        certificate.setOwnerString(user);
        certificate.setNoEarlyThan(validAfter);
        certificate.setNoLateThan(new Date(period + validAfter.getTime()));
        if (root0.getMasterKey() == null)
            root0 = certificate;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream writer = new ObjectOutputStream(out);
            writer.writeObject(certificate);
            writer.flush();
            writer.close();
            byte[] data = out.toByteArray();
            out.close();
            byte[] hash = Hash.sha256(data);
            IBSSignature signature = sign(root0, hash, "SHA-256");
            certificate.setSignature(signature);
            return certificate;
        } catch (IOException e) {
            return null;
        }
    }

    /**
     * 进行数字签名
     * Digital signing prcess
     * @param certificate 签名证书
     * @param digest 待签名数据摘要
     * @param hash 摘要方法 e.g. SHA1 or MD5
     * @return 数字签名
     */
    public static IBSSignature sign(IBSCertificate certificate, byte[] digest, String hash) {
        IBSSignature signature = new IBSSignature();
        signature.setSignatureParameter(certificate.getPublicParameter());
        signature.setDigest(digest);
        signature.setHashAlgorithm(hash);
        signature.setSigningDate(new Date());
        try {
            IBEPrivateKey privateKey = keygen(certificate, new String(digest, IBEConstraints.USER_STRING_ENCODING));
            signature.sethID(privateKey.gethID());
            signature.setrID(privateKey.getrID());
            signature.setUserString(privateKey.getUserString());
            return signature;
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    /**
     * 验证数字签名
     * Verify a digital signature
     * @param signature 数字签名
     * @param digest 待验证数据摘要
     * @return 当且仅当数据一致且签名合法时验证通过 only if the verification passes it returns true
     */
    public static boolean verify(IBSSignature signature, byte[] digest) {
        if (signature == null || digest == null)
            return false;
        boolean integrity = Arrays.equals(digest, signature.getDigest());
        if (!integrity)
            return false;
        byte[] significantBytes = new byte[120];
        new Random().nextBytes(significantBytes);
        IBEPlainText plainText = IBEPlainText.newIbePlainTextFormSignificantBytes(significantBytes);
        try {
            IBECipherText cipherText = encrypt(signature.getSignatureParameter(), plainText, new String(digest, IBEConstraints.USER_STRING_ENCODING));
            IBEPlainText decrypt = decrypt(cipherText, signature);
            return decrypt.equals(plainText);
        } catch (UnsupportedEncodingException e) {
            return false;
        }
    }
} public class IBSSignature extends IBEPrivateKey implements Serializable, IBEConstraints {
    private static final long serialVersionUID = -4862591033031464057L;

    /**
     * 签名者参数
     */
    protected IBEPublicParameter signatureParameter;

    /**
     * 被签名数据的摘要
     */
    protected byte[] digest;

    /**
     * 摘要算法
     */
    protected String hashAlgorithm;

    /**
     * 签名日期
     */
    protected Date signingDate;

    public IBSSignature() {
    }

    public IBEPublicParameter getSignatureParameter() {
        return signatureParameter;
    }

    public void setSignatureParameter(IBEPublicParameter signatureParameter) {
        this.signatureParameter = signatureParameter;
        setPairing(signatureParameter.getPairing());
    }

    public byte[] getDigest() {
        return digest;
    }

    public void setDigest(byte[] digest) {
        this.digest = digest;
    }

    public String getHashAlgorithm() {
        return hashAlgorithm;
    }

    public void setHashAlgorithm(String hashAlgorithm) {
        this.hashAlgorithm = hashAlgorithm;
    }

    public Date getSigningDate() {
        return signingDate;
    }

    public void setSigningDate(Date signingDate) {
        this.signingDate = signingDate;
    }

    /**
     * 序列化字段：<br>
     * rID 20字节<br>
     * hID 128字节<br>
     * 摘要长度 4字节<br>
     * 摘要内容<br>
     * 签名参数<br>
     * 签名日期 8字节<br>
     * 哈希算法长度 1字节<br>
     * 哈希算法字符串
     * @see hamaster.gradesgin.ibe.IBEConstraints#writeExternal(java.io.OutputStream)
     */
    @Override
    public void writeExternal(OutputStream out) throws IOException {
        byte[] rBuffer = new byte[IBE_ZR_SIZE];
        byte[] hBuffer = new byte[IBE_G_SIZE];
        Arrays.fill(rBuffer, (byte) 0);
        Arrays.fill(hBuffer, (byte) 0);
        if (rID != null)
            System.arraycopy(rID, 0, rBuffer, 0, rID.length > IBE_ZR_SIZE ? IBE_ZR_SIZE : rID.length);
        out.write(rBuffer);
        if (hID != null)
            System.arraycopy(hID, 0, hBuffer, 0, hID.length > IBE_G_SIZE ? IBE_G_SIZE : hID.length);
        out.write(hBuffer);
        if (digest == null) {
            byte[] zero = new byte[4];
            Arrays.fill(zero, (byte) 0);
            out.write(zero);
        } else {
            out.write(intToByte(digest.length));
            out.write(digest);
        }
        signatureParameter.writeExternal(out);
        if (signingDate == null) {
            byte[] zero = new byte[8];
            Arrays.fill(zero, (byte) 0);
            out.write(zero);
        } else {
            out.write(longToBytes(signingDate.getTime()));
        }
        int aSize = hashAlgorithm == null ? 0 : hashAlgorithm.length();
        out.write((byte) aSize);
        out.write(hashAlgorithm.getBytes(USER_STRING_ENCODING));
        out.flush();
    }

    /*
     * (non-Javadoc)
     * @see hamaster.gradesgin.ibe.IBEConstraints#readExternal(java.io.InputStream)
     */
    @Override
    public void readExternal(InputStream in) throws IOException, ClassNotFoundException {
        byte[] buffer = new byte[IBE_G_SIZE + IBE_ZR_SIZE];
        int size = in.read(buffer);
        if (size != buffer.length)
            throw new IOException("Not enough bytes for a Signature");
        this.rID = new byte[IBE_ZR_SIZE];
        this.hID = new byte[IBE_G_SIZE];
        System.arraycopy(buffer, 0, rID, 0, IBE_ZR_SIZE);
        System.arraycopy(buffer, IBE_ZR_SIZE, hID, 0, IBE_G_SIZE);
        byte[] dTmp = new byte[4];
        Arrays.fill(dTmp, (byte) 0);
        if (4 != in.read(dTmp))
            throw new IOException("Not enough bytes for a Signature");
        int dSize = bytesToInt(dTmp);
        this.digest = new byte[dSize];
        dSize = in.read(digest);
        if (dSize != digest.length)
            throw new IOException("Not enough bytes for a Signature");
        setUserString(new String(digest, USER_STRING_ENCODING));
        this.signatureParameter = new IBEPublicParameter();
        signatureParameter.readExternal(in);
        byte[] sTmp = new byte[8];
        Arrays.fill(sTmp, (byte) 0);
        if (8 != in.read(sTmp))
            throw new IOException("Not enough bytes for a Signature");
        this.signingDate = new Date(bytesToLong(sTmp));
        size = in.read();
        byte[] hash = new byte[size];
        size = in.read(hash);
        if (size != hash.length)
            throw new IOException("Not enough bytes for a Signature");
        this.hashAlgorithm = new String(hash, 0, size, USER_STRING_ENCODING);
        setPairing(signatureParameter.getPairing());
    }
} public final class Hash {

    private Hash() {}

    /**
     * 计算字符串的MD5摘要
     * @param str 要计算的字符串
     * @return MD5摘要
     */
    @Deprecated
    public final static byte[] md5(String str) {
        return digest(str, "MD5");
    }

    /**
     * 计算字符串的SHA-1摘要
     * @param str 要计算的字符串
     * @return SHA-1摘要
     */
    @Deprecated
    public final static byte[] sha1(String str) {
        return digest(str, "SHA-1");
    }

    /**
     * 计算字符串的SHA-256摘要
     * @param str 要计算的字符串
     * @return SHA-256摘要
     */
    public final static byte[] sha256(String str) {
        return digest(str, "SHA-256");
    }

    /**
     * 计算字符串的SHA-512摘要
     * @param str 要计算的字符串
     * @return SHA-512摘要
     */
    public final static byte[] sha512(String str) {
        return digest(str, "SHA-512");
    }

    /**
     * 计算字节数组的MD5摘要
     * @param bytes 要计算的字节数组
     * @return MD5摘要
     */
    // There is no Deprecated annotation as this method is used to generate 16 bytes
    // array from data of arbitrary length
    public final static byte[] md5(byte[] bytes) {
        return digest(bytes, "MD5");
    }

    /**
     * 计算字节数组的SHA-1摘要
     * @param bytes 要计算的字节数组
     * @return SHA-1摘要
     */
    @Deprecated
    public final static byte[] sha1(byte[] bytes) {
        return digest(bytes, "SHA-1");
    }

    /**
     * 计算字节数组的SHA-256摘要
     * @param bytes 要计算的字节数组
     * @return SHA-256摘要
     */
    public final static byte[] sha256(byte[] bytes) {
        return digest(bytes, "SHA-256");
    }

    /**
     * 计算字节数组的SHA-512摘要
     * @param bytes 要计算的字节数组
     * @return SHA-512摘要
     */
    public final static byte[] sha512(byte[] bytes) {
        return digest(bytes, "SHA-512");
    }

    /**
     * 计算文件的MD5摘要
     * @param file 要计算的文件
     * @return 文件的MD5摘要
     * @throws IOException IO异常 如文件不存在
     */
    public final static byte[] md5(File file) throws IOException {
        return digest(new FileInputStream(file), file.length(), "MD5");
    }

    /**
     * 计算文件的SHA-1摘要
     * @param file 要计算的文件
     * @return 文件的SHA-1摘要
     * @throws IOException IO异常 如文件不存在
     */
    public final static byte[] sha1(File file) throws IOException {
        return digest(new FileInputStream(file), file.length(), "SHA-1");
    }

    /**
     * 计算文件的SHA-256摘要
     * @param file 要计算的文件
     * @return 文件的SHA-256摘要
     * @throws IOException IO异常 如文件不存在
     */
    public final static byte[] sha256_(File file) throws IOException {
        return digest(new FileInputStream(file), file.length(), "SHA-256");
    }

    /**
     * 计算文件的SHA-512摘要
     * @param file 要计算的文件
     * @return 文件的SHA-512摘要
     * @throws IOException IO异常 如文件不存在
     */
    public final static byte[] sha512(File file) throws IOException {
        return digest(new FileInputStream(file), file.length(), "SHA-512");
    }

    private final static byte[] digest(String str, String algorithm) {
        return digest(str.getBytes(), algorithm);
    }

    private final static byte[] digest(byte[] bytes, String algorithm) {
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            md.update(bytes);
            return md.digest();
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    private final static byte[] digest(InputStream in, long length, String algorithm) throws IOException {
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] buffer = new byte[1024];
            int l;
            while ((l = in.read(buffer)) > 0) {
                md.update(buffer, 0, l);
            }
            return md.digest();
        } catch (NoSuchAlgorithmException e) {
            return null;
        } finally {
            in.close();
        }
    }
} @Component("base64Encoder")
public class Base64Encoder implements Encoder {

    public Base64Encoder() {
    }

    /*
     * (non-Javadoc)
     * @see hamaster.gradesign.client.Encoder#encode(byte[])
     */
    @Override
    public String encode(byte[] data) {
        return new String(Base64Coder.encode(data));
    }

    /*
     * (non-Javadoc)
     * @see hamaster.gradesign.client.Encoder#decode(java.lang.String)
     */
    @Override
    public byte[] decode(String code) {
        return Base64Coder.decode(code);
    }

    /*
     * (non-Javadoc)
     * @see hamaster.gradesign.client.Encoder#name()
     */
    @Override
    public String name() {
        return "Base64";
    }
} public interface Encoder {

    String encode(byte[] data);

    byte[] decode(String code);

    String name();
} @Service
public class KeyGenClient {

    @Value("${hamaster.gradesign.keydist.genserver}")
    private String keyGenServereURL;

    @Value("${hamaster.gradesign.keydist.system}")
    private String systemIDStr;

    @Value("${hamaster.gradesign.keydist.server_id}")
    private String serverID;

    @Value("${hamaster.gradesign.keydist.server_key_valid:1000000000000}")
    private Long serverKeyValidPeriod;

    @Value("${hamaster.gradesign.keydist.server_key_dir}")
    private String serverKeyLocation;

    private final static String SERVER_KEY_FILE = "ibedist.key";
    private final static String SERVER_KEY_FILE_CONTENT = "content";
    private final static String SERVER_KEY_FILE_CRYPT_KEY = "key";

    private Integer currentSystemID;
    private IBSCertificate serverCertificate;
    private IBEPrivateKey serverPrivateKey;
    private Encoder base64;
    private RestTemplate restTemplate;

    private Map<Integer, String> systemIDs;
    private Map<Integer, IBEPublicParameter> systemParameters;

    private final static Logger logger = LoggerFactory.getLogger(KeyGenClient.class);

    @Autowired
    public KeyGenClient(RestTemplateBuilder restTemplateBuilder, @Qualifier("base64Encoder") Encoder base64) {
        this.restTemplate = requireNonNull(restTemplateBuilder).build();
        this.base64 = requireNonNull(base64);
        currentSystemID = -1;
        systemIDs = new ConcurrentHashMap<Integer, String>();
        systemParameters = new ConcurrentHashMap<Integer, IBEPublicParameter>();
    }

    public void init() {
        SimpleRESTResponse resp = restTemplate.getForObject(String.format("%s/system/%s/number", keyGenServereURL, systemIDStr), SimpleRESTResponse.class);
        if (resp.getResultCode() == 0) {
            currentSystemID = (Integer) resp.getPayload();
            logger.info("Connected to keygen server, default system: {} ", currentSystemID);
        } else {
            logger.error("Failed to connect to key generation server: {}, error code: {}, message: {}", keyGenServereURL, resp.getResultCode(), resp.getMessage());
        }
        @SuppressWarnings("unchecked")
        Map<String, String> allSystem = restTemplate.getForObject(String.format("%s/system/all", keyGenServereURL), Map.class);
        for (String key : allSystem.keySet()) {
            try {
                systemIDs.put(Integer.parseInt(key), allSystem.get(key));
            } catch (NumberFormatException e) {
            }
        }

        @SuppressWarnings("unchecked")
        Map<String, Map<String, String>> allSystemParameters = restTemplate.getForObject(String.format("%s/system/allparam", keyGenServereURL), Map.class);
        for (String systemID : allSystemParameters.keySet()) {
            Map<String, String> base64EncodedParameter = allSystemParameters.get(systemID);
            IBEPublicParameter decodedParameter = new IBEPublicParameter();
            decodedParameter.setPairing(base64.decode(base64EncodedParameter.get("pairing")));
            decodedParameter.setParamG(base64.decode(base64EncodedParameter.get("paramG")));
            decodedParameter.setParamG1(base64.decode(base64EncodedParameter.get("paramG1")));
            decodedParameter.setParamH(base64.decode(base64EncodedParameter.get("paramH")));
            systemParameters.put(Integer.parseInt(systemID), decodedParameter);
        }
        prepareServerKeys();
        if (serverPrivateKey == null || serverCertificate == null)
            setupServerKeys();
    }

    public IBEPrivateKey serverPrivateKey() {
        if (serverPrivateKey != null)
            return serverPrivateKey;
        prepareServerKeys();
        return serverPrivateKey;
    }

    public IBSCertificate serverCertificate() {
        if (serverCertificate != null)
            return serverCertificate;
        prepareServerKeys();
        return serverCertificate;
    }

    private synchronized void prepareServerKeys() {
        File folder = new File(serverKeyLocation);
        if (checkFolerPermission(folder)) {
            Properties props = new Properties();
            InputStream key = null;
            try {
                key = new FileInputStream(new File(folder, SERVER_KEY_FILE));
                props.load(key);
            } catch (IOException e) {
                logger.error("Failed to read server private key", e);
                return;
            } finally {
                try {
                    if (key != null)
                        key.close();
                } catch (IOException e) {
                }
            }
            // both server key and its decryption key are stored in hex format
            String serverIdContent = props.getProperty(SERVER_KEY_FILE_CONTENT);
            String keyContent = props.getProperty(SERVER_KEY_FILE_CRYPT_KEY);
            byte[] raw = Hex.unhex(serverIdContent);
            ByteArrayInputStream in = new ByteArrayInputStream(raw);
            IBECapsule capsule = new IBECapsuleAESImpl();
            capsule.setKey(Hex.unhex(keyContent));
            IdentityDescription id = null;
            try {
                capsule.readExternal(in);
                id = (IdentityDescription) capsule.getDataAsObject();
            } catch (ClassNotFoundException | IOException e) {
                logger.error("Failed to decrypt server private key", e);
            } finally {
                capsule.close();
            }
            serverPrivateKey = id == null ? null : id.getPrivateKey();
            serverCertificate = id == null ? null : id.getCertificate();
            if (serverPrivateKey != null && serverCertificate != null)
                logger.info("Successfully loaded server private key");
        }
    }

    private boolean checkFolerPermission(File folder) {
        if (folder.isDirectory()) {
            final String folderPermission = "rwx------";
            if (SystemUtils.IS_OS_WINDOWS)
                return true;  // Windows does not support PosixFilePermissions
            if (folder.list().length == 0) {
                Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(folderPermission);
                try {
                    Files.setPosixFilePermissions(Paths.get(folder.getAbsolutePath()), permissions);
                    return true;
                } catch (IOException e) {
                    logger.error("Failed to set permission for new server private key directory", e);
                    return false;
                }
            }
            try {
                Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(Paths.get(folder.getAbsolutePath()));
                String currentPermission = PosixFilePermissions.toString(permissions);
                if (folderPermission.equalsIgnoreCase(currentPermission)) {
                    return true;
                } else {
                    logger.error("Incorrect permission settings for server key directory {}, expected {}, actual {}", folder.getAbsoluteFile(), folderPermission, currentPermission);
                    return false;
                }
            } catch (IOException e) {
                logger.error("Failed to read permission for existing server private key directory", e);
                return false;
            }
        }
        return false;
    }

    private File createNewKeyFile(File folder, String name, boolean overwrite) throws IOException {
        final String fildPermission = "rw-------";
        Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(fildPermission);
        File key = new File(folder, name);
        if (key.exists()) {
            if (overwrite) {
                logger.warn("Original key file {} will be overwritten", key.getAbsolutePath());
                if (!key.delete()) {
                    logger.error("Failed to overwrite existing key file");
                    return null;
                }
            } else {
                File backup = new File(folder, String.format("%s_%d", SERVER_KEY_FILE, System.currentTimeMillis()));
                if (key.renameTo(backup)) {
                    Files.setPosixFilePermissions(Paths.get(backup.getAbsolutePath()), permissions);
                    logger.warn("Creating server key backup file {} for {}", backup.getName(), key.getName());
                } else {
                    logger.error("Can't create server key backup file {} for {}", backup.getName(), key.getName());
                    return null;
                }
            }
        }
        key.createNewFile();
        Files.setPosixFilePermissions(Paths.get(key.getAbsolutePath()), permissions);
        return key;
    }

    public void setupServerKeys() {
        File folder = new File(serverKeyLocation);
        if (!folder.exists()) {
            folder.mkdirs();
            checkFolerPermission(folder);
        }
        byte[] sessionKey = randomKey(64);
        IBEPlainText plain = IBEPlainText.newIbePlainTextFormSignificantBytes(sessionKey);
        IBECipherText cipher = IBEEngine.encrypt(getKeyGenServerPublicParameter(currentSystemID), plain, getSystemIDStr(currentSystemID));
        IBECSR request = new IBECSR();
        request.setApplicationDate(new Date());
        request.setIdentityString(serverID);
        request.setIbeSystemId(currentSystemID);
        request.setPeriod(serverKeyValidPeriod);
        request.setPassword(cipher.toByteArray());
        ResponseEntity<SimpleRESTResponse> response = restTemplate.postForEntity(String.format("%s/singleid", keyGenServereURL), request, SimpleRESTResponse.class);
        if (response.hasBody()) {
            SimpleRESTResponse rest = response.getBody();
            if (rest.getResultCode() != 0) {
                logger.error("Failed to request server key: {}, deatil: {}", rest.getResultCode(), rest.getMessage());
                return;
            }
            Properties serverKey = new Properties();
            serverKey.setProperty(SERVER_KEY_FILE_CONTENT, response.getBody().getPayload().toString());
            serverKey.setProperty(SERVER_KEY_FILE_CRYPT_KEY, Hex.hex(sessionKey));
            OutputStream out = null;
            try {
                File key = createNewKeyFile(folder, SERVER_KEY_FILE, false);
                out = new FileOutputStream(key);
                serverKey.store(out, "server key");
                out.flush();
            } catch (IOException e) {
                logger.error("Error saving server key.", e);
            } finally {
                try {
                    if (out != null)
                        out.close();
                } catch (IOException e) {
                }
            }
        }
    }

    public IdentityDescriptionEntity getIdentityDescription(Integer system, String id) {
        // @GetMapping("/getid/{system}/{id}")
        // sign the get id request and send to key gen server
        String systemStr = systemIDs.get(system);
        StringBuilder content = new StringBuilder(systemStr);
        content.append('/');
        content.append(id);
        byte[] digest = Hash.sha512(content.toString().getBytes());
        IBSSignature signature = IBEEngine.sign(serverCertificate, digest, "SHA-512");
        ResponseEntity<SimpleRESTResponse> response = restTemplate.getForEntity(String.format("%s/getid/%s/%s?sig=%s", keyGenServereURL, systemStr, id, Hex.hex(signature.toByteArray())), SimpleRESTResponse.class);
        if (response.hasBody()) {
            SimpleRESTResponse resp = response.getBody();
            if (resp.getResultCode() == 0) {
                IdentityDescriptionEntity ide = new IdentityDescriptionEntity();
                @SuppressWarnings("unchecked")
                Map<String, String> result = (Map<String, String>) resp.getPayload();
                ide.setIbeId(Integer.parseInt(String.valueOf(result.get("ibeId"))));
                ide.setIdOwner(result.get("idOwner"));
                ide.setEncryptedIdentityDescription(base64.decode(result.get("encryptedIdentityDescription")));
                return ide;
            }
        }
        return null;
    }

    public byte[] encryptSessionKeyForSystem(byte[] idPwdBin, int ibeSystemId) {
        IBEPlainText plainText = IBEPlainText.newIbePlainTextFormSignificantBytes(idPwdBin);
        IBECipherText cipher = IBEEngine.encrypt(systemParameters.get(ibeSystemId), plainText , systemIDs.get(ibeSystemId));
        return cipher.toByteArray();
    }
    
    public byte[] encryptSessionKeyForSystem(byte[] bytes) {
        return encryptSessionKeyForSystem(bytes, currentSystemID);
    }

    public byte[] randomKey(int length) {
        byte[] bytes = new byte[length];
        Random rand = new SecureRandom();
        rand.nextBytes(bytes);
        return bytes;
    }

    public Map<Integer, String> getAvailableSystem() {
        return systemIDs;
    }

    public String getSystemIDStr(Integer systemID) {
        return systemIDs.get(systemID);
    }

    public IBEPublicParameter getKeyGenServerPublicParameter(Integer systemID) {
        return systemParameters.get(systemID);
    }

    public Integer getCurrentSystemID() {
        return currentSystemID;
    }

    public String getKeyGenServereURL() {
        return keyGenServereURL;
    }
} @Repository
public interface UserDAO extends JpaRepository<User, Integer> {

    /**
     * 获取注册完成但还未验证邮箱的用户
     * @param amount 用户数量
     * @return 新用户列表
     */
    default List<User> listNewRegisteredUsers(int amount) {
        return listNewRegisteredUsers(PageRequest.of(0, amount));
    }

    @Query("select u from User u where u.status = 0")
    List<User> listNewRegisteredUsers(Pageable page);

    Optional<User> findByEmail(String email);
    Optional<User> findByUsername(String username);
} @Entity
@Table(name = "IBE_ID_REQUEST")
public class IDRequest implements Serializable, Cloneable {
    private static final long serialVersionUID = -811075818715325346L;

    /**
     * 身份描述申请序号
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "REQUEST_ID")
    private Integer requestId;

    /**
     * 申请者
     */
    @ManyToOne(optional = false)
    @JoinColumn(name = "APPLICANT")
    private User applicant;

    /**
     * 身份字符串
     */
    @Column(nullable = false, name = "IDENTITY_STRING")
    private String identityString;

    /**
     * The SHA512 hash of the password used to encrypt identity description.
     * Its plain text form will be encrypted with key generation server
     * public key and stored to passwordToKeyGen.
     */
    @Column(nullable = false, name = "PASSWORD")
    private String password;

    @Lob
    @Column(nullable = false, name = "PASSWORD_KEYGEN")
    private byte[] passwordToKeyGen;

    /**
     * 所使用的系统
     */
    @Column(nullable = false, name = "IBE_SYSTEM_ID")
    private Integer ibeSystemId;

    /**
     * 申请日期
     */
    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false, name = "APPLICATION_DATE")
    private Date applicationDate;

    /**
     * 申请状态
     */
    @Column(nullable = false, name = "APPLICATION_STATUS")
    private Integer status;

    public IDRequest() {
    }

    /**
     * 返回字节形式表示的部分<code>IDRequest</code>内容
     * @return 包含内容：<ul><li>
     * 请求编号 4字节</li><li>
     * 身份字符串长度 1字节</li><li>
     * 身份字符串内容 最多255字节 UTF-8编码</li><li>
     * 所属IBE系统编号 4字节</li><li>
     * 申请日期 8字节</li><li>
     * 当前状态 1字节</li></ul>
     */
    public byte[] toBytes() {
        ByteArrayOutputStream out = new SecureByteArrayOutputStream();
        byte[] ret = null;
        try {
            out.write(Hex.intToByte(requestId));
            byte[] id = identityString.getBytes(IBEConstraints.USER_STRING_ENCODING);
            out.write(id.length);
            out.write(id);
            out.write(Hex.intToByte(ibeSystemId));
            out.write(Hex.longToBytes(applicationDate.getTime()));
            out.write(status);
            out.flush();
            ret = out.toByteArray();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            out.reset();
        }
        return ret;
    }

    public Integer getRequestId() {
        return requestId;
    }

    public void setRequestId(Integer requestId) {
        this.requestId = requestId;
    }

    @JsonIgnore
    public User getApplicant() {
        return applicant;
    }

    /**
     * for generate json use only
     * @return applicant.getEmail()
     */
    public String getOwner() {
        return applicant == null ? null : applicant.getEmail();
    }

    public void setApplicant(User applicant) {
        this.applicant = applicant;
    }

    public String getIdentityString() {
        return identityString;
    }

    public void setIdentityString(String identityString) {
        this.identityString = identityString;
    }

    @JsonIgnore
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getIbeSystemId() {
        return ibeSystemId;
    }

    public void setIbeSystemId(Integer ibeSystemId) {
        this.ibeSystemId = ibeSystemId;
    }

    public Date getApplicationDate() {
        return applicationDate;
    }

    public void setApplicationDate(Date applicationDate) {
        this.applicationDate = applicationDate;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public void setPasswordToKeyGen(byte[] passwordToKeyGen) {
        this.passwordToKeyGen = passwordToKeyGen;
    }

    @JsonIgnore
    public byte[] getPasswordToKeyGen() {
        return this.passwordToKeyGen;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#clone()
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        IDRequest copy = new IDRequest();
        copy.requestId = this.requestId;
        copy.applicant = this.applicant;
        copy.identityString = this.identityString;
        copy.password = this.password;
        copy.ibeSystemId = this.ibeSystemId;
        copy.applicationDate = this.applicationDate;
        copy.status = this.status;
        copy.passwordToKeyGen = this.passwordToKeyGen;
        return copy;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((applicant == null) ? 0 : applicant.hashCode());
        result = prime * result + ((applicationDate == null) ? 0 : applicationDate.hashCode());
        result = prime * result + ((ibeSystemId == null) ? 0 : ibeSystemId.hashCode());
        result = prime * result + ((identityString == null) ? 0 : identityString.hashCode());
        result = prime * result + ((password == null) ? 0 : password.hashCode());
        result = prime * result + Arrays.hashCode(passwordToKeyGen);
        result = prime * result + ((requestId == null) ? 0 : requestId.hashCode());
        result = prime * result + ((status == null) ? 0 : status.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        IDRequest other = (IDRequest) obj;
        if (applicant == null) {
            if (other.applicant != null)
                return false;
        } else if (!applicant.equals(other.applicant))
            return false;
        if (applicationDate == null) {
            if (other.applicationDate != null)
                return false;
        } else if (!applicationDate.equals(other.applicationDate))
            return false;
        if (ibeSystemId == null) {
            if (other.ibeSystemId != null)
                return false;
        } else if (!ibeSystemId.equals(other.ibeSystemId))
            return false;
        if (identityString == null) {
            if (other.identityString != null)
                return false;
        } else if (!identityString.equals(other.identityString))
            return false;
        if (password == null) {
            if (other.password != null)
                return false;
        } else if (!password.equals(other.password))
            return false;
        if (!Arrays.equals(passwordToKeyGen, other.passwordToKeyGen))
            return false;
        if (requestId == null) {
            if (other.requestId != null)
                return false;
        } else if (!requestId.equals(other.requestId))
            return false;
        if (status == null) {
            if (other.status != null)
                return false;
        } else if (!status.equals(other.status))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "IDRequest [requestId=" + requestId + ", applicant=" + applicant + ", identityString=" + identityString
                + ", password=" + password + ", passwordToKeyGen=" + Arrays.toString(passwordToKeyGen)
                + ", ibeSystemId=" + ibeSystemId + ", applicationDate=" + applicationDate + ", status=" + status + "]";
    }
} @Entity
@Table(name = "IBE_USER")
public class User implements Cloneable, UserDetails {
    private static final long serialVersionUID = -1101813432750693173L;

    /**
     * 用户刚刚注册
     */
    public final static int USER_REG = 0;

    /**
     * 已经发出激活信
     */
    public final static int USER_ACTIVE_LETTER_SENT = 1;

    /**
     * 用户已经被激活
     */
    public final static int USER_ACTIVE = 2;

    /**
     * 用户编号
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "USER_ID")
    private Integer userId;

    /**
     * 用户名
     */
    @Column(nullable = false, name = "USERNAME")
    private String username;

    /**
     * 用户电子邮件地址
     */
    @Column(nullable = false, name = "EMAIL")
    private String email;

    /**
     * 用户密码的SHA-512摘要
     */
    @Column(nullable = false, name = "PASSWORD")
    private String password;

    /**
     * 用户注册时间
     */
    @Temporal(TemporalType.TIMESTAMP)
    @Column(nullable = false, name = "REG_DATE")
    private Date regDate;

    /**
     * 用户当前状态
     */
    @Column(name = "STATUS")
    private Integer status;

    public User() {
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    @Override
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @JsonIgnore
    @Override
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getRegDate() {
        return regDate;
    }

    public void setRegDate(Date regDate) {
        this.regDate = regDate;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#clone()
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        User copy = new User();
        copy.userId = this.userId;
        copy.username = this.username;
        copy.email = this.email;
        copy.password = this.password;
        copy.regDate = this.regDate;
        copy.status = this.status;
        return copy;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((email == null) ? 0 : email.hashCode());
        result = prime * result
                 + ((password == null) ? 0 : password.hashCode());
        result = prime * result + ((regDate == null) ? 0 : regDate.hashCode());
        result = prime * result + ((status == null) ? 0 : status.hashCode());
        result = prime * result + ((userId == null) ? 0 : userId.hashCode());
        result = prime * result
                 + ((username == null) ? 0 : username.hashCode());
        return result;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (email == null) {
            if (other.email != null)
                return false;
        } else if (!email.equals(other.email))
            return false;
        if (password == null) {
            if (other.password != null)
                return false;
        } else if (!password.equals(other.password))
            return false;
        if (regDate == null) {
            if (other.regDate != null)
                return false;
        } else if (!regDate.equals(other.regDate))
            return false;
        if (status == null) {
            if (other.status != null)
                return false;
        } else if (!status.equals(other.status))
            return false;
        if (userId == null) {
            if (other.userId != null)
                return false;
        } else if (!userId.equals(other.userId))
            return false;
        if (username == null) {
            if (other.username != null)
                return false;
        } else if (!username.equals(other.username))
            return false;
        return true;
    }

    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "User [userId=" + userId + ", username=" + username + ", email="
               + email + ", password=" + password + ", regDate=" + regDate
               + ", status=" + status + "]";
    }

    public final static String formatDate(Date date) {
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd#HH:mm:ss");
        return format.format(date);
    }

    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.emptyList();
    }

    @Override
    public boolean isAccountNonExpired() {
        return status == USER_ACTIVE;
    }

    @Override
    public boolean isAccountNonLocked() {
        return status == USER_ACTIVE;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return status == USER_ACTIVE;
    }
} public interface IDRequestService {

    /**
     * 根据所有者和身份获取ID申请
     * @param owner 所有者
     * @param idString 身份字符串
     * @return ID申请或者null
     */
    IDRequest getByOwner(User owner, String idString);

    IDRequest getByIDString(String idString);

    /**
     * 获取用户新发起的私钥申请
     * @param amount 获取数量
     * @return 私钥申请列表
     */
    List<IDRequest> listNewRequests(int amount);

    /**
     * 获取用户的身份请求
     * @param owner 要获取的用户
     * @param page 分页页码 从0开始
     * @param amount 每一页数量
     * @param status 当前状态 默认为全部状态
     * @return 身份请求列表
     */
    List<IDRequest> list(User owner, int page, int amount, int status);

    default List<IDRequest> list(User owner, int page, int amount) {
        return list(owner, page, amount, IBECSR.APPLICATION_STATUS_ALL);
    }

    /**
     * 获取未处理的请求列表
     * @param amount 获取数量
     * @return 请求列表
     */
    List<IDRequest> listUnhandledRequests(int amount);

    /**
     * 将处理后的请求写回数据库
     * @param requests
     */
    void requestHandled(Map<String, Integer> results);

    /**
     * 获取某一用户所有身份请求的数量
     * @param owner 要获取的用户
     * @return 身份请求数量
     */
    long count(User owner);

    /**
     * 判断ID是否是某个用户所拥有的
     * @param id 要判断的ID
     * @param user 要判断的所有者
     * @param idPassword 用户申请ID时所设置的密码
     * @return
     * 当且仅当用户拥有该ID且密码正确时返回0
     * 密码错误返回1
     * ID不属于用户返回2
     */
    int doesIdBelongToUser(String id, User user, String idPassword);

    /**
     * 检查id是否被人申请过
     * @param id 要检查的id
     * @return 当切仅当没有是返回0
     */
    int doesIdRequestExist(String id);

    void save(IDRequest request);
} public class IBECSR implements Serializable {
    private static final long serialVersionUID = -7382866034793567072L;

    final public static int APPLICATION_STATUS_ALL = 0;
    final public static int APPLICATION_STARTED = 1;
    final public static int APPLICATION_APPROVED = 2;
    final public static int APPLICATION_DENIED = 3;
    final public static int APPLICATION_ERROR = 4;
    final public static int APPLICATION_NOT_VERIFIED = 5;
    final public static int APPLICATION_PROCESSING = 6;

    /**
     * 身份描述申请序号
     */
    private Integer requestId;

    /**
     * 身份字符串
     */
    private String identityString;

    /**
     * The IBE encrypted application password.
     * This password will be used to protect generated identity description.
     */
    private byte[] password;

    /**
     * The message digest for this request.
     * It's the SHA-512 checksum for the following contents
     * <table border="1">
     * <tr>
     * <td>identityString.toUTF8Bytes</td>
     * <td>password</td>
     * <td>ibeSystemId.toBytes</td>
     * <td>applicationDate.unixEpoch.toBytes</td>
     * </tr>
     * </table>
     */
    private byte[] digest;

    /**
     * The IBE signature from key distribution server
     */
    private byte[] signature;

    /**
     * 所使用的系统
     */
    private Integer ibeSystemId;

    /**
     * 申请日期
     */
    private Date applicationDate;

    /**
     * 有效期 单位毫秒
     */
    private long period;

    public IBECSR() {
    }

    public Integer getRequestId() {
        return requestId;
    }

    public void setRequestId(Integer requestId) {
        this.requestId = requestId;
    }

    public String getIdentityString() {
        return identityString;
    }

    public void setIdentityString(String identityString) {
        this.identityString = identityString;
    }

    public Integer getIbeSystemId() {
        return ibeSystemId;
    }

    public void setIbeSystemId(Integer ibeSystemId) {
        this.ibeSystemId = ibeSystemId;
    }

    public Date getApplicationDate() {
        return applicationDate;
    }

    public void setApplicationDate(Date applicationDate) {
        this.applicationDate = applicationDate;
    }

    public long getPeriod() {
        return period;
    }

    public void setPeriod(long period) {
        this.period = period;
    }

    public byte[] getPassword() {
        return password;
    }

    public void setPassword(byte[] password) {
        this.password = password;
    }

    public byte[] getSignature() {
        return signature;
    }

    public void setSignature(byte[] signature) {
        this.signature = signature;
    }

    public byte[] getDigest() {
        byte[] message = null;
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            if (identityString != null)
                out.write(identityString.getBytes("UTF-8"));
            if (password != null)
                out.write(password);
            if (ibeSystemId != null)
                out.write(Hex.intToByte(ibeSystemId.intValue()));
            if (applicationDate != null)
                out.write(Hex.longToBytes(applicationDate.getTime()));
            message = out.toByteArray();
        } catch (IOException e) {
        }
        if (message == null)
            return null;
        return Hash.sha512(message);
    }

    public boolean verifyDigest(byte[] digest) {
        byte[] thisDigest = getDigest();
        if (thisDigest == null && digest == null)
            return false;
        return Arrays.equals(thisDigest, digest);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((applicationDate == null) ? 0 : applicationDate.hashCode());
        result = prime * result + Arrays.hashCode(digest);
        result = prime * result + ((ibeSystemId == null) ? 0 : ibeSystemId.hashCode());
        result = prime * result + ((identityString == null) ? 0 : identityString.hashCode());
        result = prime * result + Arrays.hashCode(password);
        result = prime * result + (int) (period ^ (period >>> 32));
        result = prime * result + ((requestId == null) ? 0 : requestId.hashCode());
        result = prime * result + Arrays.hashCode(signature);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        IBECSR other = (IBECSR) obj;
        if (applicationDate == null) {
            if (other.applicationDate != null)
                return false;
        } else if (!applicationDate.equals(other.applicationDate))
            return false;
        if (!Arrays.equals(digest, other.digest))
            return false;
        if (ibeSystemId == null) {
            if (other.ibeSystemId != null)
                return false;
        } else if (!ibeSystemId.equals(other.ibeSystemId))
            return false;
        if (identityString == null) {
            if (other.identityString != null)
                return false;
        } else if (!identityString.equals(other.identityString))
            return false;
        if (!Arrays.equals(password, other.password))
            return false;
        if (period != other.period)
            return false;
        if (requestId == null) {
            if (other.requestId != null)
                return false;
        } else if (!requestId.equals(other.requestId))
            return false;
        if (!Arrays.equals(signature, other.signature))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "IBECSR [requestId=" + requestId + ", identityString=" + identityString + ", password="
                + Arrays.toString(password) + ", digest=" + Arrays.toString(digest) + ", signature="
                + Arrays.toString(signature) + ", ibeSystemId=" + ibeSystemId + ", applicationDate=" + applicationDate
                + ", period=" + period + "]";
    }
}
import static java.util.Objects.requireNonNull;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import hamaster.gradesgin.ibe.core.IBEEngine;
import hamaster.gradesgin.ibs.IBSSignature;
import hamaster.gradesgin.util.Hash;
import hamaster.gradesign.keydist.client.Base64Encoder;
import hamaster.gradesign.keydist.client.Encoder;
import hamaster.gradesign.keydist.daemon.KeyGenClient;
import hamaster.gradesign.keydist.dao.UserDAO;
import hamaster.gradesign.keydist.entity.IDRequest;
import hamaster.gradesign.keydist.entity.User;
import hamaster.gradesign.keydist.service.IDRequestService;
import hamaster.gradesign.keygen.IBECSR;
package hamaster.gradesign.keydist.mail;





@Component
public class IBEMailParameterGenerator {

    public final static String CONTENT_KEY = "c";// content
    public final static String SIGNATURE_KEY = "s";//signature

    final private static long oneweek = 604800000L;

    private UserDAO userDAO;
    private IDRequestService requestDAO;
    private KeyGenClient system;

    @Autowired
    public IBEMailParameterGenerator(UserDAO userDAO, IDRequestService requestDAO, KeyGenClient system) {
        this.userDAO = requireNonNull(userDAO);
        this.requestDAO = requireNonNull(requestDAO);
        this.system = requireNonNull(system);
    }

    public Properties sign(ActivationContent content) {
        Properties props = new Properties();
        byte[] bs = ActivationContent.toBytes(content);
        byte[] digest = Hash.sha512(bs);
        IBSSignature signature = IBEEngine.sign(system.serverCertificate(), digest, "SHA-512");
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            signature.writeExternal(out);
            out.flush();
        } catch (IOException e) {
        }
        byte[] sign = out.toByteArray();

        Encoder base64 = new Base64Encoder();
        String ct = base64.encode(bs);
        ct = ct.replace('+', '*');
        ct = ct.replace('/', '-');
        ct = StringUtils.replace(ct, "=", "%3D");

        String sg = base64.encode(sign);
        sg = sg.replace('+', '*');
        sg = sg.replace('/', '-');
        sg = StringUtils.replace(sg, "=", "%3D");
        props.setProperty(CONTENT_KEY, ct);
        props.setProperty(SIGNATURE_KEY, sg);
        return props;
    }

    public int verify(byte[] content, byte[] signature) {
        byte[] digest = Hash.sha512(content);
        IBSSignature sign = new IBSSignature();
        ByteArrayInputStream in = new ByteArrayInputStream(signature);
        try {
            sign.readExternal(in);
        } catch (IOException e) {
        } catch (ClassNotFoundException e) {
        }
        boolean b0 = IBEEngine.verify(sign, digest);
        if (!b0)
            return 1;// 错误
        ActivationContent activationContent = ActivationContent.fromBytes(content);
        long now = System.currentTimeMillis();
        long start = activationContent.getActiveDate().getTime();
        if (now - start < oneweek) {
            User applicant = userDAO.getOne(activationContent.getUserId());