All Downloads are FREE. Search and download functionalities are using the official Maven repository.

COSE.OneKey Maven / Gradle / Ivy

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package COSE;

import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAMultiPrimePrivateCrtKeySpec;
import java.security.spec.RSAOtherPrimeInfo;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.X509EncodedKeySpec;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSAGenParameterSpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Arrays;

/**
 *
 * @author jimsch
 */
public class OneKey {

    protected CBORObject keyMap;
    private PrivateKey privateKey;
    private PublicKey publicKey;
    
    public OneKey() {
        keyMap = CBORObject.NewMap();
    }
    
    public OneKey(CBORObject keyData) throws CoseException {
        if (keyData.getType() != CBORType.Map) throw new CoseException("Key data is malformed");
        
        keyMap = keyData;
        CheckKeyState();
    }

    /**
     * Create a OneKey object from Java Public/Private keys.
     * @param pubKey - public key to use - may be null
     * @param privKey - private key to use - may be null
     * @throws CoseException Internal COSE Exception
     */
    public OneKey(PublicKey pubKey, PrivateKey privKey) throws CoseException {
        keyMap = CBORObject.NewMap();
        
        if (pubKey != null) {
            ArrayList spki = ASN1.DecodeSubjectPublicKeyInfo(pubKey.getEncoded());
            ArrayList alg = spki.get(0).list;
            if (Arrays.equals(alg.get(0).value, ASN1.oid_ecPublicKey)) {
                byte[] oid = (byte[]) alg.get(1).value;
                if (oid == null) throw new CoseException("Invalid SPKI structure");
                // EC2 Key
                keyMap.Add(KeyKeys.KeyType.AsCBOR(), KeyKeys.KeyType_EC2);
                if (Arrays.equals(oid, ASN1.Oid_secp256r1)) keyMap.Add(KeyKeys.EC2_Curve.AsCBOR(), KeyKeys.EC2_P256);
                else if (Arrays.equals(oid, ASN1.Oid_secp384r1)) keyMap.Add(KeyKeys.EC2_Curve.AsCBOR(), KeyKeys.EC2_P384);
                else if (Arrays.equals(oid, ASN1.Oid_secp521r1)) keyMap.Add(KeyKeys.EC2_Curve.AsCBOR(), KeyKeys.EC2_P521);
                else throw new CoseException("Unsupported curve");

                byte[] keyData = (byte[]) spki.get(1).value;
                if (keyData[1] == 2 || keyData[1] == 3) {
                    keyMap.Add(KeyKeys.EC2_X.AsCBOR(), Arrays.copyOfRange(keyData, 2, keyData.length));
                    keyMap.Add(KeyKeys.EC2_Y.AsCBOR(), keyData[1] == 2 ? false : true);                
                }
                else if (keyData[1] == 4) {
                    int keyLength = (keyData.length - 2)/2;
                    keyMap.Add(KeyKeys.EC2_X.AsCBOR(), Arrays.copyOfRange(keyData, 2, 2+keyLength));
                    keyMap.Add(KeyKeys.EC2_Y.AsCBOR(), Arrays.copyOfRange(keyData, 2+keyLength, keyData.length));                    
                }
                else throw new CoseException("Invalid key data");
            }
            else if (Arrays.equals(alg.get(0).value, ASN1.Oid_rsaEncryption)) {
                ASN1.TagValue compound = ASN1.DecodeCompound(1, spki.get(1).value);
                if(compound.list == null || compound.list.size() != 2) {
                    throw new CoseException("Invalid SPKI structure");
                }

                ASN1.TagValue n = compound.list.get(0);
                ASN1.TagValue e = compound.list.get(1);

                if(n.tag != 2 || e.tag != 2) {
                    throw new CoseException("Invalid SPKI structure");
                }

                keyMap.Add(KeyKeys.RSA_N.AsCBOR(), n.value);
                keyMap.Add(KeyKeys.RSA_E.AsCBOR(), e.value);
            }
            else {
                throw new CoseException("Unsupported Algorithm");
            }
            
            this.publicKey = pubKey;
        }
        
        if (privKey != null) {
            ArrayList pkl = ASN1.DecodePKCS8Structure(privKey.getEncoded());
            if (pkl.get(0).tag != 2) throw new CoseException("Invalid PKCS8 structure");
            ArrayList alg = pkl.get(1).list;

            if (Arrays.equals(alg.get(0).value, ASN1.oid_ecPublicKey)) {
                byte[] oid = (byte[]) alg.get(1).value;
                if (oid == null) throw new CoseException("Invalid PKCS8 structure");
                // EC2 Key
                if (!keyMap.ContainsKey(KeyKeys.KeyType.AsCBOR())) {
                    keyMap.Add(KeyKeys.KeyType.AsCBOR(), KeyKeys.KeyType_EC2);
                    if (Arrays.equals(oid, ASN1.Oid_secp256r1)) keyMap.Add(KeyKeys.EC2_Curve.AsCBOR(), KeyKeys.EC2_P256);
                    else if (Arrays.equals(oid, ASN1.Oid_secp384r1)) keyMap.Add(KeyKeys.EC2_Curve.AsCBOR(), KeyKeys.EC2_P384);
                    else if (Arrays.equals(oid, ASN1.Oid_secp521r1)) keyMap.Add(KeyKeys.EC2_Curve.AsCBOR(), KeyKeys.EC2_P521);
                    else throw new CoseException("Unsupported curve");
                }
                else {
                    if (!this.get(KeyKeys.KeyType).equals(KeyKeys.KeyType_EC2)) {
                        throw new CoseException("Public/Private key don't match");
                    }
                }

                ArrayList pkdl = ASN1.DecodePKCS8EC(pkl);
                if (pkdl.get(1).tag != 4) throw new CoseException("Invalid PKCS8 structure");
                byte[] keyData = pkdl.get(1).value;
                keyMap.Add(KeyKeys.EC2_D.AsCBOR(), keyData);
            }
            else if (Arrays.equals(alg.get(0).value, ASN1.Oid_rsaEncryption)) {
                ArrayList pkdl = ASN1.DecodePKCS8RSA(pkl);

                if(!keyMap.ContainsKey(KeyKeys.RSA_N.AsCBOR())) {
                    keyMap.Add(KeyKeys.RSA_N.AsCBOR(), pkdl.get(1).value);
                }

                if(!keyMap.ContainsKey(KeyKeys.RSA_E.AsCBOR())){
                    keyMap.Add(KeyKeys.RSA_E.AsCBOR(), pkdl.get(2).value);
                }

                keyMap.Add(KeyKeys.RSA_D.AsCBOR(), pkdl.get(3).value);
                keyMap.Add(KeyKeys.RSA_P.AsCBOR(), pkdl.get(4).value);
                keyMap.Add(KeyKeys.RSA_Q.AsCBOR(), pkdl.get(5).value);
                keyMap.Add(KeyKeys.RSA_DP.AsCBOR(), pkdl.get(6).value);
                keyMap.Add(KeyKeys.RSA_DQ.AsCBOR(), pkdl.get(7).value);
                keyMap.Add(KeyKeys.RSA_QI.AsCBOR(), pkdl.get(8).value);

                // todo multi prime keys
            }
            else {
                throw new CoseException("Unsupported Algorithm");
            }
            
            this.privateKey = privKey;            
        }
    }
    
    public void add(KeyKeys keyValue, CBORObject value) {
        keyMap.Add(keyValue.AsCBOR(), value);
    }
    
    public void add(CBORObject keyValue, CBORObject value) {
        keyMap.Add(keyValue, value);
    }
    
    public CBORObject get(KeyKeys keyValue) {
        return keyMap.get(keyValue.AsCBOR());
    }
    
    public CBORObject get(CBORObject keyValue) throws CoseException {
        if ((keyValue.getType() != CBORType.Integer) && (keyValue.getType() != CBORType.TextString)) throw new CoseException("keyValue type is incorrect");
        return keyMap.get(keyValue);
    }
 
    /**
     * Compares the key's assigned algorithm with the provided value, indicating if the values are the
     * same.
     * 
     * @param algorithmId
     *          the algorithm to compare or {@code null} to check for no assignment.
     * @return {@code true} if the current key has the provided algorithm assigned, or {@code false}
     *         otherwise
     */
    public boolean HasAlgorithmID(AlgorithmID algorithmId) {
        CBORObject thisObj = get(KeyKeys.Algorithm);
        CBORObject thatObj = (algorithmId == null ? null : algorithmId.AsCBOR());
        boolean result;

        if (thatObj == null) {
            result = (thisObj == null);
        } else {
            result = thatObj.equals(thisObj);
        }
        return result;
    }

    /**
     * Compares the key's assigned identifier with the provided value, indicating if the values are
     * the same.
     * 
     * @param id
     *          the identifier to compare or {@code null} to check for no assignment.
     * @return {@code true} if the current key has the provided identifier assigned, or {@code false}
     *         otherwise
     */
    @Deprecated
    public boolean HasKeyID(String id) {
        byte[] idB = StandardCharsets.UTF_8.encode(id).array();
        return HasKeyID(idB);
    }
    
    public boolean HasKeyID(byte[] id)
    {
        CBORObject thatObj = (id == null) ? null : CBORObject.FromObject(id);
        CBORObject thisObj = get(KeyKeys.KeyId);
        boolean result;
        if (thatObj == null) {
            result = (thisObj == null);
        } else {
            result = thatObj.equals(thisObj);
        }    
        return result;        
    }

    /**
    * Compares the key's assigned key type with the provided value, indicating if the values are the
    * same.
    * 
    * @param keyTypeObj
    *          the key type to compare or {@code null} to check for no assignment.
    * @return {@code true} if the current key has the provided identifier assigned, or {@code false}
    *         otherwise
    */
    public boolean HasKeyType(CBORObject keyTypeObj) {
        CBORObject thatObj = keyTypeObj;
        CBORObject thisObj = get(KeyKeys.KeyType);
        boolean result;
        if (thatObj == null) {
            result = (thisObj == null);
        } else {
            result = thatObj.equals(thisObj);
        }
        return result;
    }
  
    /**
     * Compares the key's assigned key operations with the provided value, indicating if the provided
     * value was found in the key operation values assigned to the key.
     * 
     * @param that
     *          the integer operation value to attempt to find in the values provided by the key or
     *          {@code null} to check for no assignment.
     * @return {@code true} if the current key has the provided value assigned, or {@code false}
     *         otherwise
     */
    public boolean HasKeyOp(Integer that) {
        CBORObject thisObj = get(KeyKeys.Key_Ops);
        boolean result;
        if (that == null) {
            result = (thisObj == null);
        } else {
            result = false;
            if (thisObj.getType() == CBORType.Integer) {
                if (thisObj.AsInt32() == that) {
                    result = true;
                }
            } else if (thisObj.getType() == CBORType.Array) {
                for (int i = 0; i < thisObj.size(); i++) {
                    if ((thisObj.get(i).getType() == CBORType.Integer) && (thisObj.get(i).AsInt32() == that)) {
                        result = true;
                        break;
                    }
               }
            }
        }
        return result;
    }

    private void CheckKeyState() throws CoseException {
        CBORObject val;
        
        //  Must have a key type
        val = OneKey.this.get(KeyKeys.KeyType);
        if ((val == null) || (val.getType() != CBORType.Integer)) throw new CoseException("Missing or incorrect key type field");
        
        if (val.equals(KeyKeys.KeyType_Octet)) {
            val = OneKey.this.get(KeyKeys.Octet_K);
            if ((val== null) || (val.getType() != CBORType.ByteString)) throw new CoseException("Malformed key structure");
        }
        else if (val.equals(KeyKeys.KeyType_EC2)) {
            CheckECKey();
        }
        else if (val.equals(KeyKeys.KeyType_OKP)) {
            CheckOkpKey();
        }
        else if (val.equals(KeyKeys.KeyType_RSA)) {
            CheckRsaKey();
        }
        else throw new CoseException("Unsupported key type");
    }
    
    private void CheckECKey() throws CoseException {
        // ECParameterSpec         params = null; //   new ECDomainParameters(curve.getCurve(), curve.getG(), curve.getN(), curve.getH());
        boolean                 needPublic = false;
        // ECPrivateKeySpec        privKeySpec = null;
        CBORObject              val;

        byte[] oid;
        CBORObject cn = this.get(KeyKeys.EC2_Curve);
        if (cn == KeyKeys.EC2_P256) {
            oid = ASN1.Oid_secp256r1;
        }
        else if (cn == KeyKeys.EC2_P384) {
            oid = ASN1.Oid_secp384r1;
        }
        else if (cn == KeyKeys.EC2_P521) {
            oid = ASN1.Oid_secp521r1;
        }
        else {
            throw new CoseException("Key has an unknown curve");
        }

        try {

            val = this.get(KeyKeys.EC2_D);
            if (val != null) {
                if (val.getType() != CBORType.ByteString) throw new CoseException("Malformed key structure");
                try {
                    byte[] privateBytes = ASN1.EncodeEcPrivateKey(oid, val.GetByteString(), null);
                    byte[] pkcs8 = ASN1.EncodePKCS8(ASN1.AlgorithmIdentifier(ASN1.oid_ecPublicKey, oid), privateBytes, null);
                    
                    KeyFactory fact = KeyFactory.getInstance("EC");
                    KeySpec keyspec = new PKCS8EncodedKeySpec(pkcs8);

                    privateKey = fact.generatePrivate(keyspec);
                }
                catch (NoSuchAlgorithmException e) {
                    throw new CoseException("Unsupported Algorithm", e);
                }
                catch (InvalidKeySpecException e) {
                    throw new CoseException("Invalid Private Key", e);
                }
            }

            val = this.get(KeyKeys.EC2_X);
            if (val == null) {
                if (privateKey == null) throw new CoseException("Malformed key structure");
                else needPublic = true;
            }
            else if (val.getType() != CBORType.ByteString) throw new CoseException("Malformed key structure");

            val = this.get(KeyKeys.EC2_Y);
            if (val == null) {
                if (privateKey == null) throw new CoseException("Malformed key structure");
                else needPublic = true;
            }
            else if ((val.getType() != CBORType.ByteString) && (val.getType() != CBORType.Boolean)) throw new CoseException("Malformed key structure");

            if (privateKey != null && needPublic) {
                byte[] pkcs8 = privateKey.getEncoded();
                return;

                // todo: calculate (and populate) public from private
            }

            byte[] spki = null;

           if (spki == null) {
                byte[] rgbKey = null;
                 byte[] X = this.get(KeyKeys.EC2_X).GetByteString();

                 if (this.get(KeyKeys.EC2_Y).getType()== CBORType.Boolean) {
                     rgbKey = new byte[X.length + 1];
                     System.arraycopy(X, 0, rgbKey, 1, X.length);
                     rgbKey[0] = (byte) (2 + (this.get(KeyKeys.EC2_Y).AsBoolean() ? 1 : 0));
                 }
                 else {
                     rgbKey = new byte[X.length*2+1];
                     System.arraycopy(X, 0,rgbKey, 1, X.length);
                     byte[] Y = this.get(KeyKeys.EC2_Y).GetByteString();
                     System.arraycopy(Y, 0, rgbKey, 1+X.length, X.length);
                     rgbKey[0] = 4;
                 }

                spki = ASN1.EncodeSubjectPublicKeyInfo(ASN1.AlgorithmIdentifier(ASN1.oid_ecPublicKey, oid), rgbKey);        
            }
       
            KeyFactory fact = KeyFactory.getInstance("EC"/*, "BC"*/);
            KeySpec keyspec = new X509EncodedKeySpec(spki);
            publicKey = fact.generatePublic(keyspec);
        }
        catch (NoSuchAlgorithmException e) {
            throw new CoseException("Alorithm unsupported", e);
        }
        catch (InvalidKeySpecException e) {
            throw new CoseException("Internal error on SPKI", e);
       }
        /*
        catch (NoSuchProviderException e) {
            throw new CoseException("BC not found");
        }
        */
/*        
        X9ECParameters          curve = GetCurve();
        ECDomainParameters      params = new ECDomainParameters(curve.getCurve(), curve.getG(), curve.getN(), curve.getH());
        boolean                 needPublic = false;
        ECPrivateKeyParameters  privKey = null;
        ECPublicKeyParameters   pubKey = null;
        CBORObject              val;

        val = OneKey.this.get(KeyKeys.EC2_D);
        if (val != null) {
            if (val.getType() != CBORType.ByteString) throw new CoseException("Malformed key structure");
            privKey = new ECPrivateKeyParameters(new BigInteger(1, val.GetByteString()),
                                                    params);
        }

        val = OneKey.this.get(KeyKeys.EC2_X);
        if (val == null) {
            if (privKey == null) throw new CoseException("Malformed key structure");
            else needPublic = true;
        }
        else if (val.getType() != CBORType.ByteString) throw new CoseException("Malformed key structure");

        val = OneKey.this.get(KeyKeys.EC2_Y);
        if (val == null) {
            if (privKey == null) throw new CoseException("Malformed key structure");
            else needPublic = true;
        }
        else if ((val.getType() != CBORType.ByteString) && (val.getType() != CBORType.Boolean)) throw new CoseException("Malformed key structure");

        if (privKey != null && needPublic) {
            // todo: calculate (and populate) public from private
            pubKey = new ECPublicKeyParameters(params.getG().multiply(privKey.getD()), params);
            byte[] rgbX = pubKey.getQ().normalize().getXCoord().getEncoded();
            byte[] rgbY = pubKey.getQ().normalize().getYCoord().getEncoded();
            add(KeyKeys.EC2_X, CBORObject.FromObject(rgbX));
            add(KeyKeys.EC2_Y, CBORObject.FromObject(rgbY));
        } else {
            // todo: validate public on curve
        }
        */
    }

    public ECGenParameterSpec GetCurve2() throws CoseException {
        if (OneKey.this.get(KeyKeys.KeyType) != KeyKeys.KeyType_EC2) throw new CoseException("Not an EC2 key");
        CBORObject cnCurve = OneKey.this.get(KeyKeys.EC2_Curve);
        
        if (cnCurve == KeyKeys.EC2_P256) return new ECGenParameterSpec("secp256r1");
        if (cnCurve == KeyKeys.EC2_P384) return new ECGenParameterSpec("secp384r1");
        if (cnCurve == KeyKeys.EC2_P521) return new ECGenParameterSpec("secp521r1");
        throw new CoseException("Unsupported curve " + cnCurve);        
    }

    static public OneKey generateKey(AlgorithmID algorithm) throws CoseException {
        return generateKey(algorithm, null);
    }

    /**
     * Generate a random key pair based on the given algorithm.
     * Some algorithm can take a parameter. For example, the RSA_PSS family of algorithm
     * can take the RSA key size as a parameter.
     *
     * @param algorithm the algorithm to generate a key pair for
     * @param parameters optional parameters to the key pair generator
     * @return the generated Key Pair
     * @throws CoseException
     */
    static public OneKey generateKey(AlgorithmID algorithm, String parameters) throws CoseException {
        OneKey returnThis;
        switch(algorithm) {
            case ECDSA_256:
                returnThis = generateECDSAKey("P-256", KeyKeys.EC2_P256); 
                break;
                
            case ECDSA_384:
                returnThis = generateECDSAKey("P-384", KeyKeys.EC2_P384);
                break;
                
            case ECDSA_512:
                returnThis = generateECDSAKey("P-521", KeyKeys.EC2_P521);
                break;
                
            case EDDSA:
                returnThis = generateOkpKey("Ed25519", KeyKeys.OKP_Ed25519);
                break;

            case RSA_PSS_256:
            case RSA_PSS_384:
            case RSA_PSS_512:
                int keySize = 2048;
                if(parameters != null) {
                    try {
                        keySize = Integer.parseInt(parameters);
                    } catch (NumberFormatException ignored) {}
                }
                returnThis = generateRSAKey(keySize);
                break;
                
            default:
                throw new CoseException("Unknown algorithm");
        }
        
        returnThis.add(KeyKeys.Algorithm, algorithm.AsCBOR());
        return returnThis;
    }
    
    static public OneKey generateKey(CBORObject curve) throws CoseException {
        String curveName;
        OneKey returnThis;
        
        switch (curve.AsInt32()) {
            case 1:
                curveName = "P-256";
                returnThis = generateECDHKey(curveName, curve);
                return returnThis;

            case 2:
                curveName = "P-384";
                returnThis = generateECDHKey(curveName, curve);
                return returnThis;
            
            case 3:
                curveName = "P-521";
                returnThis = generateECDHKey(curveName, curve);
                return returnThis;
                
            case 6:
                curveName = "Ed25519";
                return generateOkpKey(curveName, curve);
                
            case 7:
                curveName = "Ed448";
                return generateOkpKey(curveName, curve);
                
            case 4:
                curveName = "X25519";
                return generateOkpKey(curveName, curve);
                
            case 5:
                curveName = "X448";
                return generateOkpKey(curveName, curve);

            default:
                throw new CoseException("Unknown curve");
        }
    }
    
    static private OneKey generateECDHKey(String curveName, CBORObject curve) throws CoseException {
        try {
            
            int curveSize;
            
            switch (curveName) {
                case "P-256":
                    curveName = "secp256r1";
                    curveSize = 256;
                    break;

                case "P-384":
                    curveName="secp384r1";
                    curveSize = 384;
                    break;

                case "P-521":
                    curveName = "secp521r1";
                    curveSize = 521;
                    break;
                    
                default:
                    throw new CoseException("Internal Error");
            }

            ECGenParameterSpec paramSpec = new ECGenParameterSpec(curveName);
            KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
            gen.initialize(paramSpec);
            
            KeyPair keyPair = gen.genKeyPair();
            
            ECPoint pubPoint = ((ECPublicKey) keyPair.getPublic()).getW();
                        
            byte[] rgbX = ArrayFromBigNum(pubPoint.getAffineX(), curveSize);
            byte[] rgbY = ArrayFromBigNum(pubPoint.getAffineY(), curveSize);
            byte[] rgbD = ArrayFromBigNum(((ECPrivateKey) keyPair.getPrivate()).getS(), curveSize);

            OneKey key = new OneKey();

            key.add(KeyKeys.KeyType, KeyKeys.KeyType_EC2);
            key.add(KeyKeys.EC2_Curve, curve);
            key.add(KeyKeys.EC2_X, CBORObject.FromObject(rgbX));
            key.add(KeyKeys.EC2_Y, CBORObject.FromObject(rgbY));
            key.add(KeyKeys.EC2_D, CBORObject.FromObject(rgbD));
            key.publicKey = keyPair.getPublic();
            key.privateKey = keyPair.getPrivate();
            
            return key;

        }
        catch (NoSuchAlgorithmException e) {
            throw new CoseException("No provider for algorithm", e);
        }
        catch (InvalidAlgorithmParameterException e) {
            throw new CoseException("THe curve is not supported", e);
        }
    }
    
    static private byte[] ArrayFromBigNum(BigInteger n, int curveSize) {
        byte[] rgb = new byte[(curveSize+7)/8];
        byte[] rgb2 = n.toByteArray();
        if (rgb.length == rgb2.length) return rgb2;
        if (rgb2.length > rgb.length) {
            System.arraycopy(rgb2, rgb2.length-rgb.length, rgb, 0, rgb.length);
        }
        else {
            System.arraycopy(rgb2, 0, rgb, rgb.length-rgb2.length, rgb2.length);
        }
        return rgb;
    }
    
    static private OneKey generateECDSAKey(String curveName, CBORObject curve) throws CoseException { 
        try {
            
            int curveSize;
            
            switch (curveName) {
                case "P-256":
                    curveName = "secp256r1";
                    curveSize = 256;
                    break;

                case "P-384":
                    curveName="secp384r1";
                    curveSize = 384;
                    break;

                case "P-521":
                    curveName = "secp521r1";
                    curveSize = 521;
                    break;
                    
                default:
                    throw new CoseException("Internal Error");
            }

            ECGenParameterSpec paramSpec = new ECGenParameterSpec(curveName);
            KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
            gen.initialize(paramSpec);
            
            KeyPair keyPair = gen.genKeyPair();
            
            ECPoint pubPoint = ((ECPublicKey) keyPair.getPublic()).getW();
                        
            byte[] rgbX = ArrayFromBigNum(pubPoint.getAffineX(), curveSize);
            byte[] rgbY = ArrayFromBigNum(pubPoint.getAffineY(), curveSize);
            byte[] rgbD = ArrayFromBigNum(((ECPrivateKey) keyPair.getPrivate()).getS(), curveSize);

            OneKey key = new OneKey();

            key.add(KeyKeys.KeyType, KeyKeys.KeyType_EC2);
            key.add(KeyKeys.EC2_Curve, curve);
            key.add(KeyKeys.EC2_X, CBORObject.FromObject(rgbX));
            key.add(KeyKeys.EC2_Y, CBORObject.FromObject(rgbY));
            key.add(KeyKeys.EC2_D, CBORObject.FromObject(rgbD));
            key.publicKey = keyPair.getPublic();
            key.privateKey = keyPair.getPrivate();
            
            return key;

        }
        catch (NoSuchAlgorithmException e) {
            throw new CoseException("No provider for algorithm", e);
        }
        catch (InvalidAlgorithmParameterException e) {
            throw new CoseException("The curve is not supported", e);
        }
    }
    
    /**
     * Create a OneKey object with only the public fields.  Filters out the 
     * private key fields but leaves all positive number labels and text labels
     * along with negative number labels that are public fields.
     * 
     * @return public version of the key
     */
    public OneKey PublicKey()
    {
        OneKey newKey = new OneKey();
        CBORObject val = this.get(KeyKeys.KeyType);
        if (val.equals(KeyKeys.KeyType_Octet)) {
            return null;
        }
        else if (val.equals(KeyKeys.KeyType_EC2)) {
            newKey.add(KeyKeys.EC2_Curve, get(KeyKeys.EC2_Curve));
            newKey.add(KeyKeys.EC2_X, get(KeyKeys.EC2_X));
            newKey.add(KeyKeys.EC2_Y, get(KeyKeys.EC2_Y));
        }
        else if (val.equals(KeyKeys.KeyType_OKP)) {
            newKey.add(KeyKeys.OKP_Curve, get(KeyKeys.OKP_Curve));
            newKey.add(KeyKeys.OKP_X, get(KeyKeys.OKP_X));
        }
        else if (val.equals(KeyKeys.KeyType_RSA)) {
            newKey.add(KeyKeys.RSA_N, get(KeyKeys.RSA_N));
            newKey.add(KeyKeys.RSA_E, get(KeyKeys.RSA_E));
        }
        else {
            return null;
        }
        
        //  Allow them to use the same underlying public key object
        
        newKey.publicKey = publicKey;

        for (CBORObject obj : keyMap.getKeys()) {
            val = keyMap.get(obj);
            if (obj.getType() == CBORType.Integer) {
                if (obj.AsInt32() > 0) {
                    newKey.add(obj, val);
                }
            }
            else if (obj.getType() == CBORType.TextString) {
                newKey.add(obj, val);
            }
        }
        return newKey;
    }
    
    /**
     * Encode to a byte string
     * 
     * @return encoded object as bytes.
     */
    public byte[] EncodeToBytes()
    {
        return keyMap.EncodeToBytes();
    }
    
    /**
     * Return the key as a CBOR object
     * 
     * @return The key
     */
    public CBORObject AsCBOR()
    {
        return keyMap;
    }
    
    /**
     * Return a java.security.PublicKey that is the same as the OneKey key
     * 
     * @return the key
     * @throws CoseException If there is a conversion error
     */
    public PublicKey AsPublicKey() throws CoseException
    {
        return publicKey;
    }
    
    /**
     * Return a java.security.PrivateKey that is the same as the OneKey key
     * 
     * @return the key
     * @throws CoseException if there is a conversion error
     */
    public PrivateKey AsPrivateKey() throws CoseException
    {
        return privateKey;
    }
    
    private Object UserData;
    
    /**
     * Return the user data field.
     * 
     * The user data object allows for an application to associate a piece of arbitrary
     * data with a key and retrieve it later.  
     * @return the user data object
     */
    public Object getUserData() {
        return UserData;
    }
    
    /**
     * Set the user data field.
     * 
     * The user data field allows for an application to associate a piece of arbitrary
     * data with a key and retrieve it later.
     * @param newData Data field to be saved.
     */
    public void setUserData(Object newData) {
        UserData = newData;
    }

    private void CheckOkpKey() throws CoseException {
        boolean                 needPublic = false;
        CBORObject              val;
        String  algName;

        byte[] oid;
        CBORObject cn = this.get(KeyKeys.OKP_Curve);
        if (cn == KeyKeys.OKP_Ed25519) {
            oid = ASN1.Oid_Ed25519;
            algName = "EdDSA";
        }
        else if (cn == KeyKeys.OKP_Ed448) {
            oid = ASN1.Oid_Ed448;
            algName = "EdDSA";
        }
        else if (cn == KeyKeys.OKP_X25519) {
            oid = ASN1.Oid_X25519;
            algName = "EdDH";
        }
        else if (cn == KeyKeys.OKP_X448) {
            oid = ASN1.Oid_X448;
            algName = "ECDH";
        }
        else {
            throw new CoseException("Key has an unknown curve");
        }

        try {

            val = this.get(KeyKeys.OKP_D);
            if (val != null) {
                if (val.getType() != CBORType.ByteString) throw new CoseException("Malformed key structure");
                try {
                    
                    byte[] privateKeyBytes = ASN1.EncodeOctetString(val.GetByteString());
                    byte[] pkcs8 = ASN1.EncodePKCS8(ASN1.AlgorithmIdentifier(oid, null), privateKeyBytes, null);
                    
                    KeyFactory fact = KeyFactory.getInstance(algName, "EdDSA");
                    KeySpec keyspec = new PKCS8EncodedKeySpec(pkcs8);

                    privateKey = fact.generatePrivate(keyspec);
                }
                catch (NoSuchAlgorithmException e) {
                    throw new CoseException("Unsupported Algorithm", e);
                }
                catch (InvalidKeySpecException e) {
                    throw new CoseException("Invalid Private Key", e);
                }
            }

            val = this.get(KeyKeys.OKP_X);
            if (val == null) {
                if (privateKey == null) throw new CoseException("Malformed key structure");
                else needPublic = true;
            }
            else if (val.getType() != CBORType.ByteString) throw new CoseException("Malformed key structure");

            if (privateKey != null && needPublic) {
                byte[] pkcs8 = privateKey.getEncoded();
                return;

                // todo: calculate (and populate) public from private
            }

            byte[] spki = null;

           if (spki == null) {
                byte[] rgbKey =  this.get(KeyKeys.OKP_X).GetByteString();

                
                spki = ASN1.EncodeSubjectPublicKeyInfo(ASN1.AlgorithmIdentifier(oid, null), rgbKey);        
            }
       
            KeyFactory fact = KeyFactory.getInstance("EdDSA", "EdDSA");
            KeySpec keyspec = new X509EncodedKeySpec(spki);
            publicKey = fact.generatePublic(keyspec);
        }
        catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            throw new CoseException("Algorithm unsupported", e);
        }
        catch (InvalidKeySpecException e) {
            throw new CoseException("Internal error on SPKI", e);
        }
    }
    
    static private OneKey generateOkpKey(String curveName, CBORObject curve) throws CoseException { 
        try {            
            switch (curveName) {
                case "Ed25519":
                    
                    break;
                    
                case "Ed448":
                case "X22519":
                case "X448":
                    throw new CoseException("Algorithm not supported.");

                default:
                    throw new CoseException("Internal Error");
            }

            EdDSAGenParameterSpec paramSpec = new EdDSAGenParameterSpec(curveName);
            KeyPairGenerator gen = KeyPairGenerator.getInstance("EdDSA", "EdDSA");
            gen.initialize(paramSpec);
            
            KeyPair keyPair = gen.genKeyPair();
                                    
            byte[] rgbX = ((EdDSAPublicKey) keyPair.getPublic()).getAbyte();
            byte[] rgbD = ((EdDSAPrivateKey) keyPair.getPrivate()).getSeed();

            OneKey key = new OneKey();

            key.add(KeyKeys.KeyType, KeyKeys.KeyType_OKP);
            key.add(KeyKeys.OKP_Curve, curve);
            key.add(KeyKeys.OKP_X, CBORObject.FromObject(rgbX));
            key.add(KeyKeys.OKP_D, CBORObject.FromObject(rgbD));
            key.publicKey = keyPair.getPublic();
            key.privateKey = keyPair.getPrivate();
            
            return key;
        }
        catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            throw new CoseException("No provider for algorithm", e);
        }
        catch (InvalidAlgorithmParameterException e) {
            throw new CoseException("The curve is not supported", e);
        }
    }    

    private void CheckRsaKey() throws CoseException {
        CBORObject n = this.get(KeyKeys.RSA_N);         // modulus, positive int
        CBORObject e = this.get(KeyKeys.RSA_E);         // public exponent, positive int
        CBORObject d = this.get(KeyKeys.RSA_D);         // private exponent, positive int
        CBORObject p = this.get(KeyKeys.RSA_P);         // the prime factor p of n
        CBORObject q = this.get(KeyKeys.RSA_Q);         // the prime factor q of n
        CBORObject dP = this.get(KeyKeys.RSA_DP);       // d mod (p - 1)
        CBORObject dQ = this.get(KeyKeys.RSA_DQ);       // d mod (q - 1)
        CBORObject qInv = this.get(KeyKeys.RSA_QI);     // CRT coefficient
        CBORObject other = this.get(KeyKeys.RSA_OTHER); // other prime info, contains map of (ri, di, ti)

        // Public key
        if (n != null && e != null) {
            if (n.getType() != CBORType.ByteString || e.getType() != CBORType.ByteString) {
                throw new CoseException("Malformed key structure");
            }

            RSAPublicKeySpec spec = new RSAPublicKeySpec(
                    new BigInteger(1, n.GetByteString()),
                    new BigInteger(1, e.GetByteString())
            );

            try {
                KeyFactory factory = KeyFactory.getInstance("RSA");
                publicKey = factory.generatePublic(spec);
            } catch (NoSuchAlgorithmException ex) {
                throw new CoseException("No provider for algorithm", ex);
            } catch (InvalidKeySpecException ex) {
                throw new CoseException("Invalid Public Key", ex);
            }
        }

        // Private key
        if (n != null && e != null && d != null && p != null &&
                q != null && dP != null && dQ != null && qInv != null) {
            if (n.getType() != CBORType.ByteString ||
                    e.getType() != CBORType.ByteString ||
                    d.getType() != CBORType.ByteString ||
                    p.getType() != CBORType.ByteString ||
                    q.getType() != CBORType.ByteString ||
                    dP.getType() != CBORType.ByteString ||
                    dQ.getType() != CBORType.ByteString ||
                    qInv.getType() != CBORType.ByteString) {
                throw new CoseException("Malformed key structure");
            }

            RSAPrivateKeySpec privateKeySpec;
            if (other == null) {
                // Single prime private key
                privateKeySpec = new RSAPrivateCrtKeySpec(
                        new BigInteger(1, n.GetByteString()),
                        new BigInteger(1, e.GetByteString()),
                        new BigInteger(1, d.GetByteString()),
                        new BigInteger(1, p.GetByteString()),
                        new BigInteger(1, q.GetByteString()),
                        new BigInteger(1, dP.GetByteString()),
                        new BigInteger(1, dQ.GetByteString()),
                        new BigInteger(1, qInv.GetByteString())
                );
            } else {
                // Multi prime private key
                if (other.getType() != CBORType.Array) {
                    throw new CoseException("Malformed key structure");
                }

                // Validate and build an array of other prime
                RSAOtherPrimeInfo[] others = new RSAOtherPrimeInfo[other.size()];
                for (int i = 0; i < other.size(); i++) {
                    CBORObject object = other.get(i);

                    if (object.getType() != CBORType.Map) {
                        throw new CoseException("Malformed key structure");
                    }

                    CBORObject ri = object.get(KeyKeys.RSA__R_I.AsCBOR());
                    CBORObject di = object.get(KeyKeys.RSA__D_I.AsCBOR());
                    CBORObject ti = object.get(KeyKeys.RSA__T_I.AsCBOR());

                    if (ri == null || di == null || ti == null) {
                        throw new CoseException("Malformed key structure");
                    }

                    if (ri.getType() != CBORType.ByteString ||
                            di.getType() != CBORType.ByteString ||
                            ti.getType() != CBORType.ByteString) {
                        throw new CoseException("Malformed key structure");
                    }

                    others[i] = new RSAOtherPrimeInfo(
                            new BigInteger(1, ri.GetByteString()),
                            new BigInteger(1, di.GetByteString()),
                            new BigInteger(1, ti.GetByteString())
                    );
                }

                privateKeySpec = new RSAMultiPrimePrivateCrtKeySpec(
                        new BigInteger(1, n.GetByteString()),
                        new BigInteger(1, e.GetByteString()),
                        new BigInteger(1, d.GetByteString()),
                        new BigInteger(1, p.GetByteString()),
                        new BigInteger(1, q.GetByteString()),
                        new BigInteger(1, dP.GetByteString()),
                        new BigInteger(1, dQ.GetByteString()),
                        new BigInteger(1, qInv.GetByteString()),
                        others
                );
            }


            try {
                KeyFactory factory = KeyFactory.getInstance("RSA");
                privateKey = factory.generatePrivate(privateKeySpec);
            } catch (NoSuchAlgorithmException ex) {
                throw new CoseException("No provider for algorithm", ex);
            } catch (InvalidKeySpecException ex) {
                throw new CoseException("Invalid Private Key", ex);
            }
        }
    }

    static private OneKey generateRSAKey(int keySize) throws CoseException {
        try {
            KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
            gen.initialize(keySize);

            KeyPair keyPair = gen.genKeyPair();

            RSAPrivateCrtKey priv = (RSAPrivateCrtKey) keyPair.getPrivate();

            OneKey key = new OneKey();

            key.add(KeyKeys.KeyType, KeyKeys.KeyType_RSA);
            key.add(KeyKeys.RSA_N, CBORObject.FromObject(priv.getModulus().toByteArray()));
            key.add(KeyKeys.RSA_E, CBORObject.FromObject(priv.getPublicExponent().toByteArray()));
            key.add(KeyKeys.RSA_D, CBORObject.FromObject(priv.getPrivateExponent().toByteArray()));
            key.add(KeyKeys.RSA_P, CBORObject.FromObject(priv.getPrimeP().toByteArray()));
            key.add(KeyKeys.RSA_Q, CBORObject.FromObject(priv.getPrimeQ().toByteArray()));
            key.add(KeyKeys.RSA_DP, CBORObject.FromObject(priv.getPrimeExponentP().toByteArray()));
            key.add(KeyKeys.RSA_DQ, CBORObject.FromObject(priv.getPrimeExponentQ().toByteArray()));
            key.add(KeyKeys.RSA_QI, CBORObject.FromObject(priv.getCrtCoefficient().toByteArray()));

            key.publicKey = keyPair.getPublic();
            key.privateKey = keyPair.getPrivate();

            return key;

        }
        catch (NoSuchAlgorithmException e) {
            throw new CoseException("No provider for algorithm", e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy