//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
//

package com.cloud.utils.crypt;

import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class RSAHelper {
    protected static Logger LOGGER = LogManager.getLogger(RSAHelper.class);

    static {
        BouncyCastleProvider provider = new BouncyCastleProvider();
        if (Security.getProvider(provider.getName()) == null)
            Security.addProvider(provider);
    }

    private static RSAPublicKey readKey(String key) throws Exception {
        byte[] encKey = Base64.decodeBase64(key.split(" ")[1]);
        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(encKey));

        byte[] header = readElement(dis);
        String pubKeyFormat = new String(header);
        if (!pubKeyFormat.equals("ssh-rsa"))
            throw new RuntimeException("Unsupported format");

        byte[] publicExponent = readElement(dis);
        byte[] modulus = readElement(dis);

        KeySpec spec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME);
        RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(spec);

        return pubKey;
    }

    private static byte[] readElement(DataInput dis) throws IOException {
        int len = dis.readInt();
        byte[] buf = new byte[len];
        dis.readFully(buf);
        return buf;
    }

    public static String encryptWithSSHPublicKey(String sshPublicKey, String content) {
        String returnString = null;
        try {
            RSAPublicKey publicKey = readKey(sshPublicKey);
            Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", BouncyCastleProvider.PROVIDER_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey, new SecureRandom());
            byte[] encrypted = cipher.doFinal(content.getBytes());
            returnString = Base64.encodeBase64String(encrypted);
        } catch (Exception e) {
            LOGGER.info("[ignored]"
                    + "error during public key encryption: " + e.getLocalizedMessage());
        }

        return returnString;
    }
}
