/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */

package com.sun.satsa.crypto;

import com.sun.satsa.util.TLV;
import com.sun.satsa.util.TLVException;
import com.sun.satsa.util.Utils;

import java.security.PublicKey;
import java.security.spec.KeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

/**
 * This class represents an RSA public key.
 */
public class RSAPublicKey implements PublicKey {

    /** OID for RSA crypto algorithm, 1.2.840.113549.1.1.1. */
    private static byte[] RSA_OID = {0x2A, (byte) 0x86, 0x48, (byte)0x86,
                                     (byte)0xF7, 0x0D, 0x01, 0x01, 0x01};
    /** Key object. */
    private com.sun.midp.crypto.RSAPublicKey key;

    /** Key length in bytes. */
    private int keyLen;

    /** Specification of the key material. */
    private X509EncodedKeySpec keySpec;

    /**
     * Constructs an RSAPublicKey object.
     * @param keySpec specification of the key material
     * @throws InvalidKeySpecException if key specification is invalid
     */
    public RSAPublicKey(KeySpec keySpec) throws InvalidKeySpecException {

        if (! (keySpec instanceof X509EncodedKeySpec)) {
            throw new InvalidKeySpecException();
        }

        this.keySpec = (X509EncodedKeySpec) keySpec;

        byte[] data = getEncoded();

        /*
         * SubjectPublicKeyInfo { ALGORITHM : IOSet} ::= SEQUENCE {
         *       algorithm        AlgorithmIdentifier {{IOSet}},
         *       subjectPublicKey BIT STRING
         *  }
         *
         *  AlgorithmIdentifier  ::=  SEQUENCE  {
         *       algorithm               OBJECT IDENTIFIER,
         *       parameters              ANY DEFINED BY algorithm OPTIONAL
         *  }
         *
         */

        try {
            TLV t = new TLV(data, 0);

            t = t.child;    // AlgorithmIdentifier
            if (! Utils.byteMatch(data, t.child.valueOffset, t.child.length,
                                 RSA_OID, 0, RSA_OID.length)) {
                throw new InvalidKeySpecException(
                        "Invalid algorithm identifier");
            }

            t = t.next;     // subjectPublicKey

            /*
             *  RSAPublicKey ::= SEQUENCE {
             *      modulus            INTEGER, -- n
             *      publicExponent     INTEGER  -- e --
             *  }
             */

            // the first byte of value in BIT STRING is the number of
            // unused bits
            t = new TLV(data, t.valueOffset + 1);

            t = t.child;    // modulus

            int offset = t.valueOffset;
            int len = t.length;
            while (data[offset] == 0) {
                offset++;
                len--;
            }

            keyLen = ((len + 7) / 8) * 8;

            t = t.next;

            key = new com.sun.midp.crypto.RSAPublicKey(data, offset, len,
                      data, t.valueOffset, t.length);
        } catch (InvalidKeySpecException ikse) {
            throw ikse;
        } catch (NullPointerException npe) {
            throw new InvalidKeySpecException();
        } catch (TLVException tlve) {
            throw new InvalidKeySpecException();
        }
    }

    /**
     * Returns key object.
     * @return key object
     */
    public com.sun.midp.crypto.RSAPublicKey getKey() {
        return key;
    }

    /**
     * Returns the size of key in bytes.
     * @return size of key in bytes
     */
    public int getKeySize() {
        return keyLen;
    }

    /**
     * Returns the standard algorithm name for this key.
     * @return the name of the algorithm associated with this key.
     */
    public String getAlgorithm() {
        return "RSA";
    }

    /**
     * Returns the name of the primary encoding format of this key.
     * @return the primary encoding format of the key.
     */
    public String getFormat() {
        return keySpec.getFormat();
    }

    /**
     * Returns the key in its primary encoding format, or null
     * if this key does not support encoding.
     * @return the encoded key.
     */
    public byte[] getEncoded() {
        return keySpec.getEncoded();
    }
}
