/*
 * Class: RSAFormat
 *
 * Author: Alex Usun
 *
 * Date: 1/9/2005
 */
package com.jscape.inet.ssh.util.keyreader.putty;

import com.jscape.inet.ssh.util.keyreader.KeyPairSpec;
import com.jscape.util.Assert;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;

/**
 * Putty RSA key pair format.
 *
 * @author Alex Usun
 * @version 1.0
 * @see <a href="RSAFormat.java.html">Source code</a>
 */
public class RSAFormat
        extends PuttyFormat {

    private static final String ALGORITHM = "RSA";
    private static final String SSH_ALGORITHM = "ssh-rsa";

    protected KeyPair keyPairFor(byte[] publicKeyData, byte[] privateKeyData)
            throws Exception {
        RSAPublicKeySpec publicKeySpec = restorePublicKeySpec(publicKeyData);
        RSAPrivateKeySpec privateKeySpec = restorePrivateKeySpec(privateKeyData, publicKeySpec);

        return new KeyPairSpec(ALGORITHM, publicKeySpec, privateKeySpec).toKeyPair();
    }

    /**
     * @see PuttyFormat#sshAlgorithm()
     */
    protected String sshAlgorithm() {
        return SSH_ALGORITHM;
    }

    /**
     * Restores public key specification.
     *
     * @param data specification data
     *
     * @return restored specification
     *
     * @throws IOException if specification cannot be read
     */
    private RSAPublicKeySpec restorePublicKeySpec(byte[] data)
            throws IOException {
        DataReader reader = new DataReader(new ByteArrayInputStream(data));

        String algorithm = reader.readString();
        Assert.isTrue(SSH_ALGORITHM.equalsIgnoreCase(algorithm), "wrong key pair algorithm");

        BigInteger publicExponent = reader.readMpint();
        BigInteger modulus = reader.readMpint();

        return new RSAPublicKeySpec(modulus, publicExponent);
    }

    /**
     * Restores private key specification.
     *
     * @param data    specification data
     * @param publicKeySpec public key specification
     *
     * @return restored specification
     *
     * @throws IOException if specification cannot be read
     */
    private RSAPrivateKeySpec restorePrivateKeySpec(byte[] data, RSAPublicKeySpec publicKeySpec)
            throws IOException {
        DataReader reader = new DataReader(new ByteArrayInputStream(data));

        BigInteger privateExponent = reader.readMpint();

        reader.readMpint(); // skip p
        reader.readMpint(); // skip q
        reader.readMpint(); // skip invQ

//        byte[] dp = privateExponent.mod(p.subtract(BigInteger.ONE)).toByteArray();
//        byte[] dq = privateExponent.mod(q.subtract(BigInteger.ONE)).toByteArray();


        return new RSAPrivateKeySpec(publicKeySpec.getModulus(), privateExponent);
    }

}
