summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java
blob: e1662a72900e325e75e857c738d2877eae31e5e8 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                             
                                     








                                              



                                


                            
                                                           
                                                                        

                                  
                                               

                                   

                                                
                                       
                                                       

                                                   
                                                                                

                                                  
                                                                             
 
     







                                                                   





































                                                                                                                                
         







                                                                                                  
 

                                                                                                                        
                                                                                                                                         

                                                                                      
                                                                                                

                                                              
                                                                                                    





















                                                                                                                
                                                                                                











                                                                         




                                                                                           
                            


                                                                             















                                                                               





                                                                                                                       




                                                                                






                                                              
                                                                                                










                                                                        








                                                                  
                                                                                           


                                       

                                                                                                                               




                                                                                   

                                                                                                                    

                  
                                                                                                                    
                                        
                                                                                                                    

                                                                                        
                                                                                     


                                                                                 


















                                                                                                                                 



                                          
 








                                                                                                      
                                         

                                                                       
                                                                  
                                                                          
                                                              
                      



                                                                                                           



                                                                                



                                                                       
                                                                                 

                                                                  


                                                           
                                                    







                                                                                                                       

                                      


                                   





                                                             










                                                                        
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.ECFieldFp;
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;


// TODO: Auto-generated Javadoc
/**
 * The Class ECKeyEncoder.
 */
public class ECKeyEncoder {
	

    /** The Constant X962_UNCOMPRESSED_POINT_INDICATOR. */
    private static final byte X962_UNCOMPRESSED_POINT_INDICATOR = 0x04;
    
    /** The Constant X962_ODD. */
    private static final byte X962_ODD = 0x02;
    
    /** The Constant X962_EVEN. */
    private static final byte X962_EVEN = 0x03;
    
    /** The Constant ENCODING_X509. */
    public static final String ENCODING_X509 = "X509";
    
    /** The Constant ENCODING_X962_UNCOMPESSED. */
    public static final String ENCODING_X962_UNCOMPESSED = "X962_UNCOMPRESSED";
    
    /** The Constant ENCODING_X962_COMPRESSED. */
    public static final String ENCODING_X962_COMPRESSED = "X962_COMPRESSED";

    
    /**
     * From encoded.
     *
     * @param keyBytes the encoded key
     * @param oid the algorithm OID of the key algorithm
     * @param provider the provider of the security implementation
     * @return the public key
     */
    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;
    	
   	
    	//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);

				AlgorithmParameters parameters = AlgorithmParameters.getInstance(keyAlgName, provider);
				parameters.init(new ECGenParameterSpec(curveName));
				ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);			
				
				//reconstruct the uncompressed version with the curve
				byte[] uncompressed = decompressPubkey(keyBytes, ecParameters);
				
				//decode the uncompressed key
				return fromUncompressedPoint(uncompressed, ecParameters, provider);

			} 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, provider);
    		
    		} catch(Exception e) {
    			//failed
    		}
    	}
    	
    	
    	throw new IllegalArgumentException("public key format unknown");
    }

    
    
    	/**
	     * Gets the encoded.
	     *
	     * @param key the public key
	     * @param encoding the encoding ("X509","X962_UNCOMPRESSED","X962_COMPRESSED")
    	 * @param provider 
	     * @return the encoded key
	     */
	    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();
	        		
	    	    	int fieldSize = (((ECPublicKey)key).getParams().getCurve().getField().getFieldSize() + 7) / 8;
	    	    	int keySizeBytes = fieldSize + 1;
	    	    	final byte[] compressed = new byte[keySizeBytes];
	  
        		
	   		   		
	        		byte[] x = toUnsignedBytes(point.getAffineX());
	        		
	        		BigInteger y = point.getAffineY();
	        		
	                
	                //compression indicator
	        		if (y.testBit(0)) {
	        			compressed[0] = 0x03;
	        		} else {
	        			compressed[0] = 0x02;
	        		}
	    	    	System.arraycopy(x, 0, compressed, 1 + fieldSize - x.length, x.length);
	                
	                return compressed;
    			}
    		}
    		
    		throw new IllegalArgumentException("unknown encoding");

    		
    	}
    	
 
	    /**
    	 * From uncompressed point.
    	 *
    	 * @param encoded the public key in uncompressed encoding
    	 * @param params the elliptic curve parameters 
    	 * @return the EC public key
    	 * @throws Exception the exception
    	 */
    	private static ECPublicKey fromUncompressedPoint(
	            final byte[] encoded, final ECParameterSpec params, Provider provider)
	            throws Exception {

	        int offset = 0;
	        if (encoded[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 (encoded.length != 1 + 2 * keySizeBytes) {
	            throw new IllegalArgumentException("Invalid uncompressedPoint encoding, not the correct size");
	        }

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

	    
	    /**
    	 * To uncompressed point.
    	 *
    	 * @param publicKey the public key
    	 * @return the encoded public key
    	 */
    	private static byte[] toUncompressedPoint(final ECPublicKey publicKey) {
	    	
	    	final byte[] xCoordBytes = toUnsignedBytes(publicKey.getW().getAffineX());
	    	final byte[] yCoordBytes = toUnsignedBytes(publicKey.getW().getAffineY());
	    	
	    	int fieldSize = (publicKey.getParams().getCurve().getField().getFieldSize() + 7) / 8;
	    	int keySizeBytes = 2 * fieldSize + 1;
	    	final byte[] uncompressedPoint = new byte[keySizeBytes];
	    	
	    	System.arraycopy(xCoordBytes, 0, uncompressedPoint, 1 + fieldSize - xCoordBytes.length, xCoordBytes.length);
	    	System.arraycopy(yCoordBytes, 0, uncompressedPoint, 1 + 2 * fieldSize - yCoordBytes.length, yCoordBytes.length);
	    	uncompressedPoint[0] = 0x04;

	        return uncompressedPoint;
	    }
	    

	   
	    /**
    	 * Decompress public key.
    	 *
    	 * @param compressedKey the compressed public key
    	 * @param ecParameters the elliptic curve parameters
    	 * @return uncompressed encoded public key
    	 */
    	private static byte[] decompressPubkey(byte[] compressedKey, ECParameterSpec ecParameters ) {
	    		    		
	    		EllipticCurve curve = ecParameters.getCurve();
	    	
	    	    // Check array length and type indicator byte
	    	    if (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)	 
	    	    
	    	    ECFieldFp ecf = (ECFieldFp) curve.getField();
	    	    BigInteger modulus = ecf.getP();
	    	    BigInteger pow = modulus.add(BigInteger.ONE).shiftRight(2);
	    	    
	    	    
	    	    BigInteger temp = xCoord.pow(2).add(curve.getA());
	    	    temp = temp.multiply(xCoord);
	    	    temp = temp.add(curve.getB());
		        temp = temp.modPow(pow, modulus);		        
	    	    boolean tempIsOdd = temp.testBit(0);
	    	    boolean yShouldBeOdd = compressedKey[0] == 3;
	    	    if (tempIsOdd != yShouldBeOdd) {
	    	        temp = temp.negate().mod(modulus);
	    	    }
	    	    final BigInteger yCoord = temp;
	    	    
	    	    final byte[] yCoordBytes = toUnsignedBytes(yCoord);
	    	    
		    	int fieldSize = (curve.getField().getFieldSize() + 7) / 8;
		    	byte[] result = new byte[2 * fieldSize + 1];
    	    	System.arraycopy(compressedKey, 0,result, 1 + fieldSize - compressedKey.length, compressedKey.length);
    	    	System.arraycopy(yCoordBytes, 0,result, 1 + 2 * fieldSize - yCoordBytes.length, yCoordBytes.length);
	    	    // set uncompressed key indicator
	    	    result[0] = 0x04;

	    	    return result;
	    }
	        
		/**
		 * To unsigned bytes.
		 *
		 * @param i the i
		 * @return the unsigned bytes of the integer
		 */
		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;
		}


}