![JAR search and dependency download from the Maven repository](/logo.png)
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