view src/ch/ethz/ssh2/signature/RSASHA1Verify.java @ 347:bb7d8a7babbe

compensate for SecureRandom bug on older devices
author Carl Byington <carl@five-ten-sg.com>
date Thu, 31 Jul 2014 18:57:57 -0700
parents db9b028016de
children
line wrap: on
line source


package ch.ethz.ssh2.signature;

import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;

import ch.ethz.ssh2.log.Logger;
import ch.ethz.ssh2.packets.TypesReader;
import ch.ethz.ssh2.packets.TypesWriter;


/**
 * RSASHA1Verify.
 *
 * @author Christian Plattner, plattner@trilead.com
 * @version $Id: RSASHA1Verify.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
 */
public class RSASHA1Verify {
    private static final Logger log = Logger.getLogger(RSASHA1Verify.class);

    public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException {
        TypesReader tr = new TypesReader(key);
        String key_format = tr.readString();

        if (key_format.equals("ssh-rsa") == false)
            throw new IllegalArgumentException("This is not a ssh-rsa public key");

        BigInteger e = tr.readMPINT();
        BigInteger n = tr.readMPINT();

        if (tr.remain() != 0)
            throw new IOException("Padding in RSA public key!");

        KeySpec keySpec = new RSAPublicKeySpec(n, e);

        try {
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return (RSAPublicKey) kf.generatePublic(keySpec);
        }
        catch (NoSuchAlgorithmException nsae) {
            IOException ioe = new IOException("No RSA KeyFactory available");
            ioe.initCause(nsae);
            throw ioe;
        }
        catch (InvalidKeySpecException ikse) {
            IOException ioe = new IOException("No RSA KeyFactory available");
            ioe.initCause(ikse);
            throw ioe;
        }
    }

    public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException {
        TypesWriter tw = new TypesWriter();
        tw.writeString("ssh-rsa");
        tw.writeMPInt(pk.getPublicExponent());
        tw.writeMPInt(pk.getModulus());
        return tw.getBytes();
    }

    public static byte[] decodeSSHRSASignature(byte[] sig) throws IOException {
        TypesReader tr = new TypesReader(sig);
        String sig_format = tr.readString();

        if (sig_format.equals("ssh-rsa") == false)
            throw new IOException("Peer sent wrong signature format");

        /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
         * containing s (which is an integer, without lengths or padding, unsigned and in
         * network byte order)." See also below.
         */
        byte[] s = tr.readByteString();

        if (s.length == 0)
            throw new IOException("Error in RSA signature, S is empty.");

        if (log.isEnabled()) {
            log.info("Decoding ssh-rsa signature string (length: " + s.length + ")");
        }

        if (tr.remain() != 0)
            throw new IOException("Padding in RSA signature!");

        if (s[0] == 0 && s[1] == 0 && s[2] == 0) {
            int i = 0;
            int j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
                    | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
            i += j;
            j = ((s[i++] << 24) & 0xff000000) | ((s[i++] << 16) & 0x00ff0000)
                | ((s[i++] << 8) & 0x0000ff00) | ((s[i++]) & 0x000000ff);
            byte[] tmp = new byte[j];
            System.arraycopy(s, i, tmp, 0, j);
            sig = tmp;
        }

        return s;
    }

    public static byte[] encodeSSHRSASignature(byte[] s) throws IOException {
        TypesWriter tw = new TypesWriter();
        tw.writeString("ssh-rsa");

        /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
         * containing s (which is an integer, without lengths or padding, unsigned and in
         * network byte order)."
         */

        /* Remove first zero sign byte, if present */

        if ((s.length > 1) && (s[0] == 0x00))
            tw.writeString(s, 1, s.length - 1);
        else
            tw.writeString(s, 0, s.length);

        return tw.getBytes();
    }

    public static byte[] generateSignature(byte[] message, RSAPrivateKey pk) throws IOException {
        try {
            Signature s = Signature.getInstance("SHA1withRSA");
            s.initSign(pk);
            s.update(message);
            return s.sign();
        }
        catch (NoSuchAlgorithmException e) {
            IOException ex = new IOException();
            ex.initCause(e);
            throw ex;
        }
        catch (InvalidKeyException e) {
            IOException ex =  new IOException();
            ex.initCause(e);
            throw ex;
        }
        catch (SignatureException e) {
            IOException ex =  new IOException();
            ex.initCause(e);
            throw ex;
        }
    }

    public static boolean verifySignature(byte[] message, byte[] ds, RSAPublicKey dpk) throws IOException {
        try {
            Signature s = Signature.getInstance("SHA1withRSA");
            s.initVerify(dpk);
            s.update(message);
            return s.verify(ds);
        }
        catch (NoSuchAlgorithmException e) {
            IOException ex = new IOException();
            ex.initCause(e);
            throw ex;
        }
        catch (InvalidKeyException e) {
            IOException ex = new IOException();
            ex.initCause(e);
            throw ex;
        }
        catch (SignatureException e) {
            IOException ex = new IOException();
            ex.initCause(e);
            throw ex;
        }
    }
}