Skip to content

Commit 1d565ce

Browse files
fmonniotjimsch
andcommitted
Add RSA Support (#95)
* 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 <ietf@augustcellars.com>
1 parent 629912b commit 1d565ce

File tree

7 files changed

+451
-31
lines changed

7 files changed

+451
-31
lines changed

src/main/java/COSE/ASN1.java

Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,41 @@ public TagValue(int tagIn, ArrayList<TagValue> listIn) {
3333
tag = tagIn;
3434
list = listIn;
3535
}
36+
37+
@Override
38+
public String toString() {
39+
String t = tagToString(tag);
40+
if(list != null) {
41+
return "TagValue{tag=" + t + ", size = " + list.size() + "}";
42+
} else {
43+
return "TagValue{tag=" + t + ", value=" + bytesToHex(value) + '}';
44+
}
45+
}
46+
47+
private static String bytesToHex(byte[] bytes) {
48+
StringBuilder sb = new StringBuilder(bytes.length * 2);
49+
for(byte b: bytes)
50+
sb.append(String.format("%02x", b & 0xff));
51+
return sb.toString();
52+
}
53+
private static String tagToString(int tag) {
54+
switch (tag) {
55+
case 0x2:
56+
return "INTEGER";
57+
case 0x3:
58+
return "BITSTRING";
59+
case 0x4:
60+
return "OCTETSTRING";
61+
case 0x5:
62+
return "NULL";
63+
case 0x6:
64+
return "OBJECTIDENTIFIER";
65+
case 0x30:
66+
return "SEQUENCE";
67+
default:
68+
return Integer.toString(tag);
69+
}
70+
}
3671
}
3772

3873
// 1.2.840.10045.3.1.7
@@ -52,11 +87,15 @@ public TagValue(int tagIn, ArrayList<TagValue> listIn) {
5287
public static final byte[] Oid_Ed25519 = new byte[]{0x6, 0x3, 0x2b, 101, 112};
5388
// 1.3.101.113
5489
public static final byte[] Oid_Ed448 = new byte[]{0x6, 0x3, 0x2b, 101, 113};
90+
91+
// 1.2.840.113549.1.1.1
92+
public static final byte[] Oid_rsaEncryption = new byte[]{0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01};
5593

5694
private static final byte[] SequenceTag = new byte[]{0x30};
5795
private static final byte[] OctetStringTag = new byte[]{0x4};
5896
private static final byte[] BitStringTag = new byte[]{0x3};
59-
97+
private static final int IntegerTag = 2;
98+
6099
/**
61100
* Encode a subject public key info structure from an OID and the data bytes
62101
* for the key
@@ -252,13 +291,13 @@ public static byte[] EncodePKCS8(byte[] algorithm, byte[] keyBytes, byte[] spki)
252291
}
253292

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

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

@@ -286,37 +325,78 @@ public static ArrayList<TagValue> DecodePKCS8(byte[] encodedData) throws CoseExc
286325
if (retValue.size() == 4 && retValue.get(3).tag != 0xa0) {
287326
throw new CoseException("Invalid PKCS8 structure");
288327
}
289-
328+
329+
return retValue;
330+
}
331+
332+
333+
334+
/**
335+
* Decode a RSA PKCS#8 private key octet string
336+
*
337+
* @param pkcs8 The decoded PKCS#8 structure
338+
* @return tag/value from the decoded object
339+
* @throws CoseException - ASN.1 encoding errors
340+
*/
341+
public static ArrayList<TagValue> DecodePKCS8RSA(ArrayList<TagValue> pkcs8) throws CoseException {
290342
// Decode the contents of the octet string PrivateKey
291-
292-
byte[] pk = (byte[]) retValue.get(2).value;
343+
344+
byte[] pk = pkcs8.get(2).value;
345+
346+
TagValue pkd = DecodeCompound(0, pk);
347+
ArrayList<TagValue> pkdl = pkd.list;
348+
if (pkd.tag != 0x30) throw new CoseException("Invalid RSAPrivateKey");
349+
if (pkdl.size() < 8 || pkdl.size() > 11) throw new CoseException("Invalid RSAPrivateKey");
350+
351+
// We don't support multi-prime decoding (version 1), but we do support single prime (version 0)
352+
if (pkdl.get(0).tag != IntegerTag && pkcs8.get(0).value[0] != 1) {
353+
throw new CoseException("Invalid RSAPrivateKey");
354+
}
355+
356+
for (TagValue tagValue : pkd.list) {
357+
if(tagValue.tag != IntegerTag) throw new CoseException("Invalid RSAPrivateKey");
358+
}
359+
360+
return pkdl;
361+
}
362+
363+
/**
364+
* Decode an EC PKCS#8 private key octet string
365+
*
366+
* @param pkcs8 The decoded PKCS#8 structure
367+
* @return tag/value from the decoded object
368+
* @throws CoseException - ASN.1 encoding errors
369+
*/
370+
public static ArrayList<TagValue> DecodePKCS8EC(ArrayList<TagValue> pkcs8) throws CoseException {
371+
// Decode the contents of the octet string PrivateKey
372+
373+
byte[] pk = pkcs8.get(2).value;
374+
293375
TagValue pkd = DecodeCompound(0, pk);
294376
ArrayList<TagValue> pkdl = pkd.list;
295377
if (pkd.tag != 0x30) throw new CoseException("Invalid ECPrivateKey");
296378
if (pkdl.size() < 2 || pkdl.size() > 4) throw new CoseException("Invalid ECPrivateKey");
297-
298-
if (pkdl.get(0).tag != 2 && ((byte[]) retValue.get(0).value)[0] != 1) {
379+
380+
if (pkdl.get(0).tag != 2 && pkcs8.get(0).value[0] != 1) {
299381
throw new CoseException("Invalid ECPrivateKey");
300382
}
301-
383+
302384
if (pkdl.get(1).tag != 4) throw new CoseException("Invalid ECPrivateKey");
303-
385+
304386
if (pkdl.size() > 2) {
305387
if ((pkdl.get(2).tag & 0xff) != 0xA0) {
306388
if (pkdl.size() != 3 || (pkdl.get(2).tag & 0xff) != 0xa1) {
307389
throw new CoseException("Invalid ECPrivateKey");
308390
}
309391
} else {
310-
if (pkdl.size() == 4 && (pkdl.get(3).tag & 0xff) != 0xa1) throw new CoseException("Invalid ECPrivateKey");
392+
if (pkdl.size() == 4 && (pkdl.get(3).tag & 0xff) != 0xa1) throw new CoseException("Invalid ECPrivateKey");
311393
}
312394
}
313-
314-
retValue.get(2).list = pkdl;
315-
retValue.get(2).value = null;
316-
retValue.get(2).tag = 0x30;
317-
318-
return retValue;
395+
396+
return pkdl;
319397
}
398+
399+
320400

321401
public static byte[] EncodeSignature(byte[] r, byte[] s) throws CoseException {
322402
ArrayList<byte[]> x = new ArrayList<byte[]>();

src/main/java/COSE/AlgorithmID.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ public enum AlgorithmID {
5555
ECDH_SS_HKDF_256_AES_KW_128(-32, 0, 0),
5656
ECDH_SS_HKDF_256_AES_KW_192(-33, 0, 0),
5757
ECDH_SS_HKDF_256_AES_KW_256(-34, 0, 0),
58+
59+
RSA_PSS_256(-37, 0, 0),
60+
RSA_PSS_384(-38, 0, 0),
61+
RSA_PSS_512(-39, 0, 0),
5862
;
5963

6064
private final CBORObject value;

src/main/java/COSE/KeyKeys.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,38 @@ public enum KeyKeys {
1717
KeyId(2),
1818
Key_Ops(4),
1919
Base_IV(5),
20+
2021
Octet_K(-1),
22+
2123
EC2_Curve(-1),
2224
EC2_X(-2),
2325
EC2_Y(-3),
2426
EC2_D(-4),
27+
2528
OKP_Curve(-1),
2629
OKP_X(-2),
2730
OKP_D(-4),
31+
32+
RSA_N(-1),
33+
RSA_E(-2),
34+
RSA_D(-3),
35+
RSA_P(-4),
36+
RSA_Q(-5),
37+
RSA_DP(-6),
38+
RSA_DQ(-7),
39+
RSA_QI(-8),
40+
RSA_OTHER(-9),
41+
RSA__R_I(-10),
42+
RSA__D_I(-11),
43+
RSA__T_I(-12),
2844
;
2945

3046
private final CBORObject value;
3147

3248
public final static CBORObject KeyType_OKP = CBORObject.FromObject(1);
3349
public final static CBORObject KeyType_EC2 = CBORObject.FromObject(2);
3450
public final static CBORObject KeyType_Octet = CBORObject.FromObject(4);
51+
public final static CBORObject KeyType_RSA = CBORObject.FromObject(3);
3552

3653
public final static CBORObject EC2_P256 = CBORObject.FromObject(1);
3754
public final static CBORObject EC2_P384 = CBORObject.FromObject(2);

0 commit comments

Comments
 (0)