summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java
blob: 96038d3283ab04e7e5a99b223e0b75018162e924 (plain) (tree)



























































































































































































































































































                                                                                                                                
package org.uic.barcode.utils;

import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.Provider;
import java.security.PublicKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.EllipticCurve;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;


public class ECKeyEncoder {
	

    private static final byte X962_UNCOMPRESSED_POINT_INDICATOR = 0x04;
    private static final byte X962_ODD = 0x02;
    private static final byte X962_EVEN = 0x03;
    
    public static final String ENCODING_X509 = "X509";
    public static final String ENCODING_X962_UNCOMPESSED = "X962_UNCOMPRESSED";
    public static final String ENCODING_X962_COMPRESSED = "X962_COMPRESSED";
    
    public static PublicKey fromEncoded (byte[] keyBytes, String oid, Provider provider) {
    	
    	PublicKey key = null;
    	
    	
		String keyAlgName = null;
		try {
			keyAlgName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, oid,provider);
		} catch (Exception e1) {
			throw new IllegalArgumentException("algorithm unknown in: " + provider.getName());
		}

		if (keyAlgName == null || keyAlgName.length() == 0) {
			throw new IllegalArgumentException("algorithm unknown in: " + provider.getName());
		}
    	
    	//try standard X.509 encoding first
    	
    	try {
    		
    		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
    		    		
    		if (keyAlgName != null && keyAlgName.length() > 0) {
    		
    			KeyFactory keyFactory = KeyFactory.getInstance(keyAlgName,provider);

    			key = keyFactory.generatePublic(keySpec);
    			
    		}
			
    	} catch (Exception e) {
    		
    		//try next option
    		
    	}
    	
    	if (key != null) return key;
    	
    	if (keyBytes[0] ==  X962_UNCOMPRESSED_POINT_INDICATOR) {
    		
    		
    		
    	}
    	
    	//maybe a compressed  X9.62 eliptic key
    	if (keyBytes[0] == X962_ODD || keyBytes[0] == X962_EVEN) {
    		
    		
    		try {
		    	
		    	//we need to know the curve!
				String curveName = EllipticCurveNames.getInstance().getName(oid);
				
				//get the curve parameters
				AlgorithmParameters parameters = AlgorithmParameters.getInstance(keyAlgName, provider);
				parameters.init(new ECGenParameterSpec(curveName));
				ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
				EllipticCurve curve = ecParameters.getCurve();
				
				//reconstruct the uncompressed version with the curve
				byte[] uncompressed = decompressPubkey(keyBytes, curve);
				
				//decode the uncompressed key
				return fromUncompressedPoint(uncompressed, ecParameters);

			} catch (Exception e) {
				key = null;
				// failed
			}
 
		
    	}
    	
    	//try X962 uncompressed
    	if (keyBytes[0] == X962_UNCOMPRESSED_POINT_INDICATOR) {
    		
    		try {
    			//we need to know the curve!
    			String curveName = EllipticCurveNames.getInstance().getName(oid);
			
    			//get the curve parameters
    			AlgorithmParameters parameters = AlgorithmParameters.getInstance(keyAlgName, provider);
    			parameters.init(new ECGenParameterSpec(curveName));
    			ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
			
    			//decode the uncompressed key
				return fromUncompressedPoint(keyBytes, ecParameters);
    		
    		} catch(Exception e) {
    			//failed
    		}
    	}
    	
    	
    	throw new IllegalArgumentException("public key format unknown");
    }

    
    
    	public static byte[] getEncoded(PublicKey key, String encoding){
    		
    		if (encoding.equals(ENCODING_X509)) {
    			return key.getEncoded();
    		} else if (encoding.equals(ENCODING_X962_UNCOMPESSED)) {
    			
    			if (key instanceof ECPublicKey) {
    			
    				return toUncompressedPoint((ECPublicKey) key);
    				
    			}
    			
    		} else if (encoding.equals(ENCODING_X962_COMPRESSED)) {
    			
    			if (key instanceof ECPublicKey) {
    			
	        		ECPoint point = ((ECPublicKey) key).getW();
	   		   		
	        		byte[] x = toUnsignedBytes(point.getAffineX());
	        		
	        		BigInteger y = point.getAffineY();
	        		
	                byte[] compressed = new byte[x.length + 1];
	                
	                //compression indicator
	        		if (y.testBit(0)) {
	        			compressed[0] = 0x03;
	        		} else {
	        			compressed[0] = 0x02;
	        		}
	                System.arraycopy(x, 0, compressed, 1, x.length);
	                
	                return compressed;
    			}
    		}
    		
    		throw new IllegalArgumentException("unknown encoding");

    		
    	}
    	
 
	    private static ECPublicKey fromUncompressedPoint(
	            final byte[] uncompressedPoint, final ECParameterSpec params)
	            throws Exception {

	        int offset = 0;
	        if (uncompressedPoint[offset++] != X962_UNCOMPRESSED_POINT_INDICATOR) {
	            throw new IllegalArgumentException(
	                    "Invalid uncompressedPoint encoding, no uncompressed point indicator");
	        }

	        int keySizeBytes = (params.getOrder().bitLength() + Byte.SIZE - 1)
	                / Byte.SIZE;

	        if (uncompressedPoint.length != 1 + 2 * keySizeBytes) {
	            throw new IllegalArgumentException(
	                    "Invalid uncompressedPoint encoding, not the correct size");
	        }

	        final BigInteger x = new BigInteger(1, Arrays.copyOfRange(
	                uncompressedPoint, offset, offset + keySizeBytes));
	        offset += keySizeBytes;
	        final BigInteger y = new BigInteger(1, Arrays.copyOfRange(
	                uncompressedPoint, offset, offset + keySizeBytes));
	        final ECPoint w = new ECPoint(x, y);
	        final ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(w, params);
	        final KeyFactory keyFactory = KeyFactory.getInstance("EC");
	        return (ECPublicKey) keyFactory.generatePublic(ecPublicKeySpec);
	    }

	    private static byte[] toUncompressedPoint(final ECPublicKey publicKey) {

	        int keySizeBytes = (publicKey.getParams().getOrder().bitLength() + Byte.SIZE - 1)
	                / Byte.SIZE;

	        final byte[] uncompressedPoint = new byte[1 + 2 * keySizeBytes];
	        int offset = 0;
	        uncompressedPoint[offset++] = 0x04;

	        final byte[] x = publicKey.getW().getAffineX().toByteArray();
	        if (x.length <= keySizeBytes) {
	            System.arraycopy(x, 0, uncompressedPoint, offset + keySizeBytes
	                    - x.length, x.length);
	        } else if (x.length == keySizeBytes + 1 && x[0] == 0) {
	            System.arraycopy(x, 1, uncompressedPoint, offset, keySizeBytes);
	        } else {
	            throw new IllegalStateException("x value is too large");
	        }
	        offset += keySizeBytes;

	        final byte[] y = publicKey.getW().getAffineY().toByteArray();
	        if (y.length <= keySizeBytes) {
	            System.arraycopy(y, 0, uncompressedPoint, offset + keySizeBytes
	                    - y.length, y.length);
	        } else if (y.length == keySizeBytes + 1 && y[0] == 0) {
	            System.arraycopy(y, 1, uncompressedPoint, offset, keySizeBytes);
	        } else {
	            throw new IllegalStateException("y value is too large");
	        }

	        return uncompressedPoint;
	    }
	    
	    static final BigInteger MODULUS =
	    	    new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
	    
	    static final BigInteger POW = MODULUS.add(BigInteger.ONE).shiftRight(2);

	    // Given a 33-byte compressed public key, this returns a 65-byte uncompressed key.
	    private static byte[] decompressPubkey(byte[] compressedKey, EllipticCurve curve ) {
	    		    		
	    	    // Check array length and type indicator byte
	    	    if (compressedKey.length != 33 || compressedKey[0] != 2 && compressedKey[0] != 3)
	    	        throw new IllegalArgumentException();

	    	    final byte[] xCoordBytes = Arrays.copyOfRange(compressedKey, 1, compressedKey.length);
	    	    final BigInteger xCoord = new BigInteger(1, xCoordBytes);  // Range [0, 2^256)	 
	    	    
	    	    
	    	    BigInteger temp = xCoord.pow(2).add(curve.getA());
	    	    temp = temp.multiply(xCoord);
	    	    temp = temp.add(curve.getB());
		        temp = temp.modPow(POW, MODULUS);
	    	    
	    	    //temp = sqrtMod(temp.add(curveParamB));
		        
	    	    boolean tempIsOdd = temp.testBit(0);
	    	    boolean yShouldBeOdd = compressedKey[0] == 3;
	    	    if (tempIsOdd != yShouldBeOdd)
	    	        temp = temp.negate().mod(MODULUS);
	    	    final BigInteger yCoord = temp;

	    	    // Copy the x coordinate into the new
	    	    // uncompressed key, and change the type byte
	    	    byte[] result = Arrays.copyOf(compressedKey, 65);
	    	    result[0] = 0x04;

	    	    // Carefully copy the y coordinate into uncompressed key
	    	    final byte[] yCoordBytes = yCoord.toByteArray();
	    	    for (int i = 0; i < 32 && i < yCoordBytes.length; i++)
	    	        result[result.length - 1 - i] = yCoordBytes[yCoordBytes.length - 1 - i];

	    	    return result;
	    }
	        
		private static byte[] toUnsignedBytes(BigInteger i) {
			byte[] b = i.abs().toByteArray();
			//remove top sign bit  
			if (b[0] == 0) {
				b = Arrays.copyOfRange(b, 1, b.length);
			}
			return b;
		}


}