Skip to content

Commit

Permalink
Add RSA Support (#95)
Browse files Browse the repository at this point in the history
* Checkpoint RSA code

This also has some certificate code which should be separated.

* Add CheckRsaKey basic extraction

* Add support for multi prime key

and use crt-based key spec for single prime key

* Add key generation for RSA

* Support RSA in OneKey.PublicKey()

* Add some debug information to ASN1.TagValue.toString

* Add support to create OneKey from JAVA RSA keys.

* Remove cer file

* Separate PKCS decoding from the key's octetstring

* Unify OneKey constructor on Java's generic Public/Private keys

* Remove Edwards keys from this PR

* Use braces for multiline if

* key generation can now take parameters

and RSA generation take advantage of that

* Remove anima/x509 from RegressionTest

Co-authored-by: Jim Schaad <[email protected]>
  • Loading branch information
fmonniot and jimsch committed Jan 27, 2020
1 parent 629912b commit 1d565ce
Show file tree
Hide file tree
Showing 7 changed files with 451 additions and 31 deletions.
116 changes: 98 additions & 18 deletions src/main/java/COSE/ASN1.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,41 @@ public TagValue(int tagIn, ArrayList<TagValue> listIn) {
tag = tagIn;
list = listIn;
}

@Override
public String toString() {
String t = tagToString(tag);
if(list != null) {
return "TagValue{tag=" + t + ", size = " + list.size() + "}";
} else {
return "TagValue{tag=" + t + ", value=" + bytesToHex(value) + '}';
}
}

private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for(byte b: bytes)
sb.append(String.format("%02x", b & 0xff));
return sb.toString();
}
private static String tagToString(int tag) {
switch (tag) {
case 0x2:
return "INTEGER";
case 0x3:
return "BITSTRING";
case 0x4:
return "OCTETSTRING";
case 0x5:
return "NULL";
case 0x6:
return "OBJECTIDENTIFIER";
case 0x30:
return "SEQUENCE";
default:
return Integer.toString(tag);
}
}
}

// 1.2.840.10045.3.1.7
Expand All @@ -52,11 +87,15 @@ public TagValue(int tagIn, ArrayList<TagValue> listIn) {
public static final byte[] Oid_Ed25519 = new byte[]{0x6, 0x3, 0x2b, 101, 112};
// 1.3.101.113
public static final byte[] Oid_Ed448 = new byte[]{0x6, 0x3, 0x2b, 101, 113};

// 1.2.840.113549.1.1.1
public static final byte[] Oid_rsaEncryption = new byte[]{0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01};

private static final byte[] SequenceTag = new byte[]{0x30};
private static final byte[] OctetStringTag = new byte[]{0x4};
private static final byte[] BitStringTag = new byte[]{0x3};

private static final int IntegerTag = 2;

/**
* Encode a subject public key info structure from an OID and the data bytes
* for the key
Expand Down Expand Up @@ -252,13 +291,13 @@ public static byte[] EncodePKCS8(byte[] algorithm, byte[] keyBytes, byte[] spki)
}

/**
* Decode an EC PKCS#8 private key structure
*
* Decode a PKCS#8 private key structure, leaving the private key as an octetstring.
* @param encodedData bytes containing the private key
* @return tag/value from the decoded object
* @throws CoseException - ASN.1 encoding errors
*/
public static ArrayList<TagValue> DecodePKCS8(byte[] encodedData) throws CoseException
public static ArrayList<TagValue> DecodePKCS8Structure(byte[] encodedData) throws CoseException
{
TagValue pkcs8 = DecodeCompound(0, encodedData);
if (pkcs8.tag != 0x30) throw new CoseException("Invalid PKCS8 structure");
Expand All @@ -268,7 +307,7 @@ public static ArrayList<TagValue> DecodePKCS8(byte[] encodedData) throws CoseExc
}

// Version number - we currently only support one version
if (retValue.get(0).tag != 2 && ((byte[]) retValue.get(0).value)[0] != 0) {
if (retValue.get(0).tag != IntegerTag && retValue.get(0).value[0] != 0) {
throw new CoseException("Invalid PKCS8 structure");
}

Expand All @@ -286,37 +325,78 @@ public static ArrayList<TagValue> DecodePKCS8(byte[] encodedData) throws CoseExc
if (retValue.size() == 4 && retValue.get(3).tag != 0xa0) {
throw new CoseException("Invalid PKCS8 structure");
}


return retValue;
}



/**
* Decode a RSA PKCS#8 private key octet string
*
* @param pkcs8 The decoded PKCS#8 structure
* @return tag/value from the decoded object
* @throws CoseException - ASN.1 encoding errors
*/
public static ArrayList<TagValue> DecodePKCS8RSA(ArrayList<TagValue> pkcs8) throws CoseException {
// Decode the contents of the octet string PrivateKey

byte[] pk = (byte[]) retValue.get(2).value;

byte[] pk = pkcs8.get(2).value;

TagValue pkd = DecodeCompound(0, pk);
ArrayList<TagValue> pkdl = pkd.list;
if (pkd.tag != 0x30) throw new CoseException("Invalid RSAPrivateKey");
if (pkdl.size() < 8 || pkdl.size() > 11) throw new CoseException("Invalid RSAPrivateKey");

// We don't support multi-prime decoding (version 1), but we do support single prime (version 0)
if (pkdl.get(0).tag != IntegerTag && pkcs8.get(0).value[0] != 1) {
throw new CoseException("Invalid RSAPrivateKey");
}

for (TagValue tagValue : pkd.list) {
if(tagValue.tag != IntegerTag) throw new CoseException("Invalid RSAPrivateKey");
}

return pkdl;
}

/**
* Decode an EC PKCS#8 private key octet string
*
* @param pkcs8 The decoded PKCS#8 structure
* @return tag/value from the decoded object
* @throws CoseException - ASN.1 encoding errors
*/
public static ArrayList<TagValue> DecodePKCS8EC(ArrayList<TagValue> pkcs8) throws CoseException {
// Decode the contents of the octet string PrivateKey

byte[] pk = pkcs8.get(2).value;

TagValue pkd = DecodeCompound(0, pk);
ArrayList<TagValue> pkdl = pkd.list;
if (pkd.tag != 0x30) throw new CoseException("Invalid ECPrivateKey");
if (pkdl.size() < 2 || pkdl.size() > 4) throw new CoseException("Invalid ECPrivateKey");
if (pkdl.get(0).tag != 2 && ((byte[]) retValue.get(0).value)[0] != 1) {

if (pkdl.get(0).tag != 2 && pkcs8.get(0).value[0] != 1) {
throw new CoseException("Invalid ECPrivateKey");
}

if (pkdl.get(1).tag != 4) throw new CoseException("Invalid ECPrivateKey");

if (pkdl.size() > 2) {
if ((pkdl.get(2).tag & 0xff) != 0xA0) {
if (pkdl.size() != 3 || (pkdl.get(2).tag & 0xff) != 0xa1) {
throw new CoseException("Invalid ECPrivateKey");
}
} else {
if (pkdl.size() == 4 && (pkdl.get(3).tag & 0xff) != 0xa1) throw new CoseException("Invalid ECPrivateKey");
if (pkdl.size() == 4 && (pkdl.get(3).tag & 0xff) != 0xa1) throw new CoseException("Invalid ECPrivateKey");
}
}

retValue.get(2).list = pkdl;
retValue.get(2).value = null;
retValue.get(2).tag = 0x30;

return retValue;

return pkdl;
}



public static byte[] EncodeSignature(byte[] r, byte[] s) throws CoseException {
ArrayList<byte[]> x = new ArrayList<byte[]>();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/COSE/AlgorithmID.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public enum AlgorithmID {
ECDH_SS_HKDF_256_AES_KW_128(-32, 0, 0),
ECDH_SS_HKDF_256_AES_KW_192(-33, 0, 0),
ECDH_SS_HKDF_256_AES_KW_256(-34, 0, 0),

RSA_PSS_256(-37, 0, 0),
RSA_PSS_384(-38, 0, 0),
RSA_PSS_512(-39, 0, 0),
;

private final CBORObject value;
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/COSE/KeyKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,38 @@ public enum KeyKeys {
KeyId(2),
Key_Ops(4),
Base_IV(5),

Octet_K(-1),

EC2_Curve(-1),
EC2_X(-2),
EC2_Y(-3),
EC2_D(-4),

OKP_Curve(-1),
OKP_X(-2),
OKP_D(-4),

RSA_N(-1),
RSA_E(-2),
RSA_D(-3),
RSA_P(-4),
RSA_Q(-5),
RSA_DP(-6),
RSA_DQ(-7),
RSA_QI(-8),
RSA_OTHER(-9),
RSA__R_I(-10),
RSA__D_I(-11),
RSA__T_I(-12),
;

private final CBORObject value;

public final static CBORObject KeyType_OKP = CBORObject.FromObject(1);
public final static CBORObject KeyType_EC2 = CBORObject.FromObject(2);
public final static CBORObject KeyType_Octet = CBORObject.FromObject(4);
public final static CBORObject KeyType_RSA = CBORObject.FromObject(3);

public final static CBORObject EC2_P256 = CBORObject.FromObject(1);
public final static CBORObject EC2_P384 = CBORObject.FromObject(2);
Expand Down
Loading

0 comments on commit 1d565ce

Please sign in to comment.