diff --git a/README.md b/README.md
index 6a4d8da..4e2d9a1 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ import java.security.Provider;
public class InstallBouncyCastle {
private static final Provider PROVIDER;
-
+
public static void installProvider() throws Exception {
if (PROVIDER != null) return;
PROVIDER = new BouncyCastleProvider();
@@ -66,4 +66,4 @@ Go ahead, file issues, make pull requests. There is an automated build process
Currently setup to build in the NetBeans IDE. Automated checking is performed using the [COSE Examples](https://github.com/cose-wg/Examples) as part of the suite.
-The examples are located by the following method. 1) If 'c:\\Projects\\cose\\" exists then it uses that as the directory to look in for the examples. 2) It expects that the examples are in the same directory as the pom.xml file.
+The examples are imported into `src/test/resources/Examples`.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 16dd923..ceeacda 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.augustcellars.cose
cose-java
- 1.1.0
+ 1.1.1-SNAPSHOT
com.augustcellars.cose:cose-java
A Java implementation that supports the COSE secure message specification.
@@ -38,7 +38,7 @@
https://oss.sonatype.org/content/repositories/snapshots
-
+
@@ -139,7 +139,7 @@
-
+
org.apache.maven.plugins
maven-javadoc-plugin
@@ -157,7 +157,7 @@
-
+
org.apache.maven.plugins
maven-source-plugin
@@ -183,7 +183,7 @@
true
-
+
@@ -192,7 +192,7 @@
UTF-8
-
+
@@ -203,4 +203,4 @@
-
+
diff --git a/src/main/java/COSE/CryptoContext.java b/src/main/java/COSE/CryptoContext.java
new file mode 100644
index 0000000..3bdc64f
--- /dev/null
+++ b/src/main/java/COSE/CryptoContext.java
@@ -0,0 +1,25 @@
+package COSE;
+
+
+import java.security.Provider;
+
+/**
+ * Specify which JCA Provider to use for signing and verifying messages
+ */
+public class CryptoContext {
+
+ private Provider provider;
+
+ public CryptoContext(Provider provider) {
+ this.provider = provider;
+ }
+
+ public Provider getProvider() {
+ return provider;
+ }
+
+ public void setProvider(Provider provider) {
+ this.provider = provider;
+ }
+
+}
diff --git a/src/main/java/COSE/Encrypt0Message.java b/src/main/java/COSE/Encrypt0Message.java
index 34aac7a..af654cc 100644
--- a/src/main/java/COSE/Encrypt0Message.java
+++ b/src/main/java/COSE/Encrypt0Message.java
@@ -24,11 +24,11 @@ public class Encrypt0Message extends EncryptCommon {
public Encrypt0Message() {
this(true, true);
}
-
+
/**
* Create a Encrypt0Message object. This object corresponds to the encrypt
message format in COSE.
- *
+ *
* @param emitTag is the leading CBOR tag emitted
* @param emitContent is the content emitted
*/
@@ -38,11 +38,18 @@ public Encrypt0Message(boolean emitTag, boolean emitContent) {
this.emitTag = emitTag;
this.emitContent = emitContent;
}
-
+
+ /**
+ * Gets the {@link CryptoContext} to set a different JCA Provider
+ */
+ public CryptoContext getCryptoContext() {
+ return cryptoContext;
+ }
+
@Override
public void DecodeFromCBORObject(CBORObject obj) throws CoseException {
if (obj.size() != 3) throw new CoseException("Invalid Encrypt0 structure");
-
+
if (obj.get(0).getType() == CBORType.ByteString) {
if (obj.get(0).GetByteString().length == 0) {
rgbProtected = new byte[0];
@@ -53,43 +60,43 @@ public void DecodeFromCBORObject(CBORObject obj) throws CoseException {
objProtected = CBORObject.DecodeFromBytes(rgbProtected);
if (objProtected.getType() != CBORType.Map) throw new CoseException("Invalid Encrypt0 structure");
}
-
+
}
else throw new CoseException("Invalid Encrypt0 structure");
-
+
if(obj.get(1).getType() == CBORType.Map) objUnprotected = obj.get(1);
else throw new CoseException("Invalid Encrypt0 structure");
-
+
if (obj.get(2).getType() == CBORType.ByteString) rgbEncrypt = obj.get(2).GetByteString();
else if (!obj.get(2).isNull()) throw new CoseException("Invalid Encrypt0 structure");
-
-
+
+
}
-
+
/**
- * Internal function used to construct the CBORObject
+ * Internal function used to construct the CBORObject
* @return the constructed CBORObject
* @throws CoseException if the content has not yet been encrypted
*/
@Override
protected CBORObject EncodeCBORObject() throws CoseException {
if (rgbEncrypt == null) throw new CoseException("Encrypt function not called");
-
+
CBORObject obj = CBORObject.NewArray();
if (objProtected.size() > 0) obj.Add(objProtected.EncodeToBytes());
else obj.Add(CBORObject.FromObject(new byte[0]));
-
+
obj.Add(objUnprotected);
-
+
if (emitContent) obj.Add(rgbEncrypt);
else obj.Add(CBORObject.Null);
-
+
return obj;
}
-
+
/**
* Decrypt the message using the passed in key.
- *
+ *
* @param rgbKey key for decryption
* @return the decrypted content
* @throws CoseException - Error during decryption
@@ -97,10 +104,10 @@ protected CBORObject EncodeCBORObject() throws CoseException {
public byte[] decrypt(byte[] rgbKey) throws CoseException {
return super.decryptWithKey(rgbKey);
}
-
+
/**
* Encrypt the message using the passed in key.
- *
+ *
* @param rgbKey key used for encryption
* @throws CoseException - Error during decryption
* @throws IllegalStateException - Error during decryption
diff --git a/src/main/java/COSE/EncryptCommon.java b/src/main/java/COSE/EncryptCommon.java
index 277d980..223d93a 100644
--- a/src/main/java/COSE/EncryptCommon.java
+++ b/src/main/java/COSE/EncryptCommon.java
@@ -20,32 +20,35 @@
* @author jimsch
*/
public abstract class EncryptCommon extends Message {
+
+ protected CryptoContext cryptoContext = new CryptoContext(null);
+
private final String AES_SPEC = "AES";
-
+
private final String AES_CCM_SPEC = AES_SPEC + "/CCM/NoPadding";
private final int AES_CCM_16_IV_LENGTH = 13;
private final int AES_CCM_64_IV_LENGTH = 7;
-
+
private final String AES_GCM_SPEC = AES_SPEC + "/GCM/NoPadding";
private final int AES_GCM_IV_LENGTH = 96;
-
+
protected String context;
protected byte[] rgbEncrypt;
SecureRandom random = new SecureRandom();
-
+
protected byte[] decryptWithKey(byte[] rgbKey) throws CoseException {
CBORObject algX = findAttribute(HeaderKeys.Algorithm);
AlgorithmID alg = AlgorithmID.FromCBOR(algX);
-
+
if (rgbEncrypt == null) throw new CoseException("No Encrypted Content Specified");
-
+
switch (alg) {
case AES_GCM_128:
case AES_GCM_192:
case AES_GCM_256:
AES_GCM_Decrypt(alg, rgbKey);
break;
-
+
case AES_CCM_16_64_128:
case AES_CCM_16_64_256:
case AES_CCM_64_64_128:
@@ -56,18 +59,18 @@ protected byte[] decryptWithKey(byte[] rgbKey) throws CoseException {
case AES_CCM_64_128_256:
AES_CCM_Decrypt(alg, rgbKey);
break;
-
+
default:
throw new CoseException("Unsupported Algorithm Specified");
}
-
+
return rgbContent;
}
-
+
void encryptWithKey(byte[] rgbKey) throws CoseException, IllegalStateException {
CBORObject algX = findAttribute(HeaderKeys.Algorithm);
AlgorithmID alg = AlgorithmID.FromCBOR(algX);
-
+
if (rgbContent == null) throw new CoseException("No Content Specified");
switch (alg) {
@@ -91,10 +94,10 @@ void encryptWithKey(byte[] rgbKey) throws CoseException, IllegalStateException {
default:
throw new CoseException("Unsupported Algorithm Specified");
}
-
+
ProcessCounterSignatures();
}
-
+
private int getAES_CCM_IVSize(AlgorithmID alg) throws CoseException {
switch (alg) {
case AES_CCM_16_64_128:
@@ -110,7 +113,7 @@ private int getAES_CCM_IVSize(AlgorithmID alg) throws CoseException {
}
throw new CoseException("Unsupported algorithm: " + alg);
}
-
+
private void AES_CCM_Decrypt(AlgorithmID alg, byte[] rgbKey) throws CoseException, IllegalStateException {
// validate key
if (rgbKey.length != alg.getKeySize()/8) {
@@ -129,14 +132,16 @@ private void AES_CCM_Decrypt(AlgorithmID alg, byte[] rgbKey) throws CoseExceptio
if (iv.GetByteString().length != ivLen) {
throw new CoseException("IV size is incorrect");
}
-
+
try {
- Cipher cipher = Cipher.getInstance(AES_CCM_SPEC);
+ Cipher cipher = cryptoContext.getProvider() != null ?
+ Cipher.getInstance(AES_CCM_SPEC, cryptoContext.getProvider()) :
+ Cipher.getInstance(AES_CCM_SPEC);
cipher.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(rgbKey, AES_SPEC),
new GCMParameterSpec(alg.getTagSize(), iv.GetByteString()));
cipher.updateAAD(getAADBytes());
-
+
rgbContent = new byte[cipher.getOutputSize(rgbEncrypt.length)];
ByteBuffer input = ByteBuffer.wrap(rgbEncrypt);
ByteBuffer output = ByteBuffer.wrap(rgbContent);
@@ -153,14 +158,14 @@ private void AES_CCM_Decrypt(AlgorithmID alg, byte[] rgbKey) throws CoseExceptio
throw new CoseException("Decryption failure", ex);
}
}
-
-
+
+
private void AES_CCM_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseException, IllegalStateException {
// validate key
if (rgbKey.length != alg.getKeySize()/8) {
throw new CoseException("Key Size is incorrect");
}
-
+
// obtain and validate iv
CBORObject iv = findAttribute(HeaderKeys.IV);
int ivLen = getAES_CCM_IVSize(alg);
@@ -177,9 +182,11 @@ private void AES_CCM_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseExceptio
throw new CoseException("IV is too long.");
}
}
-
+
try {
- Cipher cipher = Cipher.getInstance(AES_CCM_SPEC);
+ Cipher cipher = cryptoContext.getProvider() != null ?
+ Cipher.getInstance(AES_CCM_SPEC, cryptoContext.getProvider()) :
+ Cipher.getInstance(AES_CCM_SPEC);
cipher.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(rgbKey, AES_SPEC),
new GCMParameterSpec(alg.getTagSize(), iv.GetByteString()));
@@ -217,8 +224,9 @@ private void AES_GCM_Decrypt(AlgorithmID alg, byte[] rgbKey) throws CoseExceptio
try {
// create and prepare cipher
- Cipher cipher;
- cipher = Cipher.getInstance(AES_GCM_SPEC);
+ Cipher cipher = cryptoContext.getProvider() != null ?
+ Cipher.getInstance(AES_GCM_SPEC, cryptoContext.getProvider()) :
+ Cipher.getInstance(AES_GCM_SPEC);
cipher.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(rgbKey, "AES"),
new GCMParameterSpec(alg.getTagSize(), iv.GetByteString()));
@@ -249,7 +257,7 @@ private void AES_GCM_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseExceptio
if (rgbKey.length != alg.getKeySize()/8) {
throw new CoseException("Key Size is incorrect");
}
-
+
// obtain and validate iv
CBORObject iv = findAttribute(HeaderKeys.IV);
if (iv == null) {
@@ -266,9 +274,11 @@ private void AES_GCM_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseExceptio
throw new CoseException("IV size is incorrect");
}
}
-
+
try {
- Cipher cipher = Cipher.getInstance(AES_GCM_SPEC);
+ Cipher cipher = cryptoContext.getProvider() != null ?
+ Cipher.getInstance(AES_GCM_SPEC, cryptoContext.getProvider()) :
+ Cipher.getInstance(AES_GCM_SPEC);
cipher.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(rgbKey, AES_SPEC),
new GCMParameterSpec(alg.getTagSize(), iv.GetByteString()));
@@ -284,10 +294,10 @@ private void AES_GCM_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseExceptio
throw new CoseException("Encryption failure", ex);
}
}
-
+
private byte[] getAADBytes() {
CBORObject obj = CBORObject.NewArray();
-
+
obj.Add(context);
if (objProtected.size() == 0) rgbProtected = new byte[0];
else rgbProtected = objProtected.EncodeToBytes();
@@ -295,23 +305,23 @@ private byte[] getAADBytes() {
obj.Add(CBORObject.FromObject(externalData));
return obj.EncodeToBytes();
}
-
+
/**
* Used to obtain the encrypted content for the cases where detached content
* is requested.
- *
+ *
* @return bytes of the encrypted content
* @throws CoseException if content has not been encrypted
*/
public byte[] getEncryptedContent() throws CoseException{
if (rgbEncrypt == null) throw new CoseException("No Encrypted Content Specified");
-
+
return rgbEncrypt;
}
-
+
/**
* Set the encrypted content for detached content cases.
- *
+ *
* @param rgb encrypted content to be used
*/
public void setEncryptedContent(byte[] rgb) {
@@ -333,7 +343,7 @@ protected void ProcessCounterSignatures() throws CoseException {
addAttribute(HeaderKeys.CounterSignature, list, Attribute.UNPROTECTED);
}
}
-
+
if (counterSign1 != null) {
counterSign1.sign(rgbProtected, rgbEncrypt);
addAttribute(HeaderKeys.CounterSignature0, counterSign1.EncodeToCBORObject(), Attribute.UNPROTECTED);
@@ -343,7 +353,7 @@ protected void ProcessCounterSignatures() throws CoseException {
public boolean validate(CounterSign1 countersignature) throws CoseException {
return countersignature.validate(rgbProtected, rgbEncrypt);
}
-
+
public boolean validate(CounterSign countersignature) throws CoseException {
return countersignature.validate(rgbProtected, rgbEncrypt);
}
diff --git a/src/main/java/COSE/MacCommon.java b/src/main/java/COSE/MacCommon.java
index 7b9ff2c..8fb8651 100644
--- a/src/main/java/COSE/MacCommon.java
+++ b/src/main/java/COSE/MacCommon.java
@@ -24,10 +24,13 @@
*/
public abstract class MacCommon extends Message {
+
+ protected CryptoContext cryptoContext = new CryptoContext(null);
+
protected byte[] rgbTag;
protected String strContext;
protected SecureRandom random = new SecureRandom();
-
+
protected MacCommon() {
super();
}
@@ -37,7 +40,7 @@ protected void CreateWithKey(byte[] rgbKey) throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(algX);
if (rgbContent == null) throw new CoseException("No Content Specified");
-
+
switch (alg) {
case HMAC_SHA_256_64:
case HMAC_SHA_256:
@@ -45,7 +48,7 @@ protected void CreateWithKey(byte[] rgbKey) throws CoseException {
case HMAC_SHA_512:
rgbTag = HMAC(alg, rgbKey);
break;
-
+
case AES_CBC_MAC_128_64:
case AES_CBC_MAC_256_64:
case AES_CBC_MAC_128_128:
@@ -56,18 +59,18 @@ protected void CreateWithKey(byte[] rgbKey) throws CoseException {
default:
throw new CoseException("Unsupported MAC Algorithm");
}
-
+
ProcessCounterSignatures();
}
-
+
protected boolean Validate(byte[] rgbKey) throws CoseException {
boolean f;
int i;
byte[] rgbTest;
-
+
CBORObject algX = findAttribute(CBORObject.FromObject(1)); //HeaderKeys.Algorithm);
AlgorithmID alg = AlgorithmID.FromCBOR(algX);
-
+
switch (alg) {
case HMAC_SHA_256_64:
case HMAC_SHA_256:
@@ -75,18 +78,18 @@ protected boolean Validate(byte[] rgbKey) throws CoseException {
case HMAC_SHA_512:
rgbTest = HMAC(alg, rgbKey);
break;
-
+
case AES_CBC_MAC_128_64:
case AES_CBC_MAC_256_64:
case AES_CBC_MAC_128_128:
case AES_CBC_MAC_256_128:
rgbTest = AES_CBC_MAC(alg, rgbKey);
break;
-
+
default:
throw new CoseException("Unsupported MAC Algorithm");
}
-
+
if (rgbTest.length != rgbTag.length) return false;
f = true;
for (i=0; i spki = ASN1.DecodeSubjectPublicKeyInfo(pubKey.getEncoded());
ArrayList alg = spki.get(0).list;
@@ -84,12 +94,12 @@ public OneKey(PublicKey pubKey, PrivateKey privKey) throws CoseException {
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);
+ 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));
+ keyMap.Add(KeyKeys.EC2_Y.AsCBOR(), Arrays.copyOfRange(keyData, 2+keyLength, keyData.length));
}
else throw new CoseException("Invalid key data");
}
@@ -136,10 +146,10 @@ else if (Arrays.equals(oid, ASN1.Oid_Ed448))
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");
@@ -220,32 +230,32 @@ else if (Arrays.equals(oid, ASN1.Oid_Ed448))
else {
throw new CoseException("Unsupported Algorithm");
}
-
- this.privateKey = privKey;
+
+ 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}
@@ -267,7 +277,7 @@ public boolean HasAlgorithmID(AlgorithmID algorithmId) {
/**
* 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}
@@ -278,7 +288,7 @@ 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);
@@ -288,14 +298,14 @@ public boolean HasKeyID(byte[] id)
result = (thisObj == null);
} else {
result = thatObj.equals(thisObj);
- }
- return result;
+ }
+ 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}
@@ -312,11 +322,11 @@ public boolean HasKeyType(CBORObject keyTypeObj) {
}
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.
@@ -348,11 +358,11 @@ public boolean HasKeyOp(Integer that) {
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");
@@ -368,7 +378,7 @@ else if (val.equals(KeyKeys.KeyType_RSA)) {
}
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;
@@ -398,8 +408,10 @@ else if (cn == KeyKeys.EC2_P521) {
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");
+
+ KeyFactory fact = cryptoContext.getProvider() != null ?
+ KeyFactory.getInstance("EC", cryptoContext.getProvider()) :
+ KeyFactory.getInstance("EC");
KeySpec keyspec = new PKCS8EncodedKeySpec(pkcs8);
privateKey = fact.generatePrivate(keyspec);
@@ -445,17 +457,20 @@ else if (cn == KeyKeys.EC2_P521) {
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 = new byte[X.length+Y.length+1];
+ System.arraycopy(X, 0, rgbKey, 1, X.length);
+ System.arraycopy(Y, 0, rgbKey, 1 + X.length, Y.length);
rgbKey[0] = 4;
}
- spki = ASN1.EncodeSubjectPublicKeyInfo(ASN1.AlgorithmIdentifier(ASN1.oid_ecPublicKey, oid), rgbKey);
+
+ spki = ASN1.EncodeSubjectPublicKeyInfo(ASN1.AlgorithmIdentifier(ASN1.oid_ecPublicKey, oid), rgbKey);
}
-
- KeyFactory fact = KeyFactory.getInstance("EC"/*, "BC"*/);
+
+ KeyFactory fact = cryptoContext.getProvider() != null ?
+ KeyFactory.getInstance("EC", cryptoContext.getProvider()) :
+ KeyFactory.getInstance("EC");
KeySpec keyspec = new X509EncodedKeySpec(spki);
publicKey = fact.generatePublic(keyspec);
}
@@ -470,7 +485,7 @@ else if (cn == KeyKeys.EC2_P521) {
throw new CoseException("BC not found");
}
*/
-/*
+/*
X9ECParameters curve = GetCurve();
ECDomainParameters params = new ECDomainParameters(curve.getCurve(), curve.getG(), curve.getN(), curve.getH());
boolean needPublic = false;
@@ -515,11 +530,11 @@ else if (cn == KeyKeys.EC2_P521) {
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);
+ throw new CoseException("Unsupported curve " + cnCurve);
}
static public OneKey generateKey(AlgorithmID algorithm) throws CoseException {
@@ -537,22 +552,37 @@ static public OneKey generateKey(AlgorithmID algorithm) throws CoseException {
* @throws CoseException
*/
static public OneKey generateKey(AlgorithmID algorithm, String parameters) throws CoseException {
+ return generateKey(algorithm, parameters, 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
+ * @param provider JCA provider to use
+ * @return the generated Key Pair
+ * @throws CoseException
+ */
+ static public OneKey generateKey(AlgorithmID algorithm, String parameters, Provider provider) throws CoseException {
OneKey returnThis;
switch(algorithm) {
case ECDSA_256:
- returnThis = generateECDSAKey("P-256", KeyKeys.EC2_P256);
+ returnThis = generateECDSAKey("P-256", KeyKeys.EC2_P256, provider);
break;
-
+
case ECDSA_384:
- returnThis = generateECDSAKey("P-384", KeyKeys.EC2_P384);
+ returnThis = generateECDSAKey("P-384", KeyKeys.EC2_P384, provider);
break;
-
+
case ECDSA_512:
- returnThis = generateECDSAKey("P-521", KeyKeys.EC2_P521);
+ returnThis = generateECDSAKey("P-521", KeyKeys.EC2_P521, provider);
break;
-
+
case EDDSA:
- returnThis = generateOkpKey("Ed25519", KeyKeys.OKP_Ed25519);
+ returnThis = generateOkpKey("Ed25519", KeyKeys.OKP_Ed25519, provider);
break;
case RSA_PSS_256:
@@ -564,63 +594,67 @@ static public OneKey generateKey(AlgorithmID algorithm, String parameters) throw
keySize = Integer.parseInt(parameters);
} catch (NumberFormatException ignored) {}
}
- returnThis = generateRSAKey(keySize);
+ returnThis = generateRSAKey(keySize, provider);
break;
-
+
default:
throw new CoseException("Unknown algorithm");
}
-
+
returnThis.add(KeyKeys.Algorithm, algorithm.AsCBOR());
return returnThis;
}
-
+
static public OneKey generateKey(CBORObject curve) throws CoseException {
+ return generateKey(curve, null);
+ }
+
+ static public OneKey generateKey(CBORObject curve, Provider provider) throws CoseException {
String curveName;
OneKey returnThis;
-
+
switch (curve.AsInt32()) {
case 1:
curveName = "P-256";
- returnThis = generateECDHKey(curveName, curve);
+ returnThis = generateECDHKey(curveName, curve, provider);
return returnThis;
case 2:
curveName = "P-384";
- returnThis = generateECDHKey(curveName, curve);
+ returnThis = generateECDHKey(curveName, curve, provider);
return returnThis;
-
+
case 3:
curveName = "P-521";
- returnThis = generateECDHKey(curveName, curve);
+ returnThis = generateECDHKey(curveName, curve, provider);
return returnThis;
-
+
case 6:
curveName = "Ed25519";
- return generateOkpKey(curveName, curve);
-
+ return generateOkpKey(curveName, curve, provider);
+
case 7:
curveName = "Ed448";
- return generateOkpKey(curveName, curve);
-
+ return generateOkpKey(curveName, curve, provider);
+
case 4:
curveName = "X25519";
- return generateOkpKey(curveName, curve);
-
+ return generateOkpKey(curveName, curve, provider);
+
case 5:
curveName = "X448";
- return generateOkpKey(curveName, curve);
+ return generateOkpKey(curveName, curve, provider);
default:
throw new CoseException("Unknown curve");
}
}
-
- static private OneKey generateECDHKey(String curveName, CBORObject curve) throws CoseException {
+
+ static private OneKey generateECDHKey(String curveName, CBORObject curve, Provider provider) throws CoseException {
try {
-
+
int curveSize;
-
+
switch (curveName) {
case "P-256":
curveName = "secp256r1";
@@ -636,19 +670,21 @@ static private OneKey generateECDHKey(String curveName, CBORObject curve) throws
curveName = "secp521r1";
curveSize = 521;
break;
-
+
default:
throw new CoseException("Internal Error");
}
ECGenParameterSpec paramSpec = new ECGenParameterSpec(curveName);
- KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
+ KeyPairGenerator gen = provider != null ?
+ KeyPairGenerator.getInstance("EC", provider) :
+ 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);
@@ -662,7 +698,7 @@ static private OneKey generateECDHKey(String curveName, CBORObject curve) throws
key.add(KeyKeys.EC2_D, CBORObject.FromObject(rgbD));
key.publicKey = keyPair.getPublic();
key.privateKey = keyPair.getPrivate();
-
+
return key;
}
@@ -673,7 +709,7 @@ static private OneKey generateECDHKey(String curveName, CBORObject curve) throws
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();
@@ -686,12 +722,12 @@ static private byte[] ArrayFromBigNum(BigInteger n, int curveSize) {
}
return rgb;
}
-
- static private OneKey generateECDSAKey(String curveName, CBORObject curve) throws CoseException {
+
+ static private OneKey generateECDSAKey(String curveName, CBORObject curve, Provider provider) throws CoseException {
try {
-
+
int curveSize;
-
+
switch (curveName) {
case "P-256":
curveName = "secp256r1";
@@ -707,19 +743,21 @@ static private OneKey generateECDSAKey(String curveName, CBORObject curve) throw
curveName = "secp521r1";
curveSize = 521;
break;
-
+
default:
throw new CoseException("Internal Error");
}
ECGenParameterSpec paramSpec = new ECGenParameterSpec(curveName);
- KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
+ KeyPairGenerator gen = provider != null ?
+ KeyPairGenerator.getInstance("EC", provider) :
+ 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);
@@ -733,7 +771,7 @@ static private OneKey generateECDSAKey(String curveName, CBORObject curve) throw
key.add(KeyKeys.EC2_D, CBORObject.FromObject(rgbD));
key.publicKey = keyPair.getPublic();
key.privateKey = keyPair.getPrivate();
-
+
return key;
}
@@ -744,12 +782,12 @@ static private OneKey generateECDSAKey(String curveName, CBORObject curve) throw
throw new CoseException("The curve is not supported", e);
}
}
-
+
/**
- * Create a OneKey object with only the public fields. Filters out the
+ * 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()
@@ -775,9 +813,9 @@ else if (val.equals(KeyKeys.KeyType_RSA)) {
else {
return null;
}
-
+
// Allow them to use the same underlying public key object
-
+
newKey.publicKey = publicKey;
for (CBORObject obj : keyMap.getKeys()) {
@@ -793,30 +831,30 @@ else if (obj.getType() == CBORType.TextString) {
}
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
*/
@@ -824,10 +862,10 @@ 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
*/
@@ -835,23 +873,23 @@ 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.
+ * 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.
@@ -893,11 +931,13 @@ else if (cn == KeyKeys.OKP_X448) {
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");
+
+ KeyFactory fact = cryptoContext.getProvider() != null ?
+ KeyFactory.getInstance(algName, cryptoContext.getProvider()) :
+ KeyFactory.getInstance(algName, "EdDSA");
KeySpec keyspec = new PKCS8EncodedKeySpec(pkcs8);
privateKey = fact.generatePrivate(keyspec);
@@ -929,11 +969,13 @@ else if (cn == KeyKeys.OKP_X448) {
if (spki == null) {
byte[] rgbKey = this.get(KeyKeys.OKP_X).GetByteString();
-
- spki = ASN1.EncodeSubjectPublicKeyInfo(ASN1.AlgorithmIdentifier(oid, null), rgbKey);
+
+ spki = ASN1.EncodeSubjectPublicKeyInfo(ASN1.AlgorithmIdentifier(oid, null), rgbKey);
}
-
- KeyFactory fact = KeyFactory.getInstance("EdDSA", "EdDSA");
+
+ KeyFactory fact = cryptoContext.getProvider() != null ?
+ KeyFactory.getInstance("EdDSA", cryptoContext.getProvider()) :
+ KeyFactory.getInstance("EdDSA", "EdDSA");
KeySpec keyspec = new X509EncodedKeySpec(spki);
publicKey = fact.generatePublic(keyspec);
}
@@ -944,14 +986,14 @@ else if (cn == KeyKeys.OKP_X448) {
throw new CoseException("Internal error on SPKI", e);
}
}
-
- static private OneKey generateOkpKey(String curveName, CBORObject curve) throws CoseException {
- try {
+
+ static private OneKey generateOkpKey(String curveName, CBORObject curve, Provider provider) throws CoseException {
+ try {
switch (curveName) {
case "Ed25519":
-
+
break;
-
+
case "Ed448":
case "X22519":
case "X448":
@@ -962,11 +1004,13 @@ static private OneKey generateOkpKey(String curveName, CBORObject curve) throws
}
EdDSAGenParameterSpec paramSpec = new EdDSAGenParameterSpec(curveName);
- KeyPairGenerator gen = KeyPairGenerator.getInstance("EdDSA", "EdDSA");
+ KeyPairGenerator gen = provider != null ?
+ KeyPairGenerator.getInstance("EdDSA", provider) :
+ KeyPairGenerator.getInstance("EdDSA", "EdDSA");
gen.initialize(paramSpec);
-
+
KeyPair keyPair = gen.genKeyPair();
-
+
byte[] rgbX = ((EdDSAPublicKey) keyPair.getPublic()).getAbyte();
byte[] rgbD = ((EdDSAPrivateKey) keyPair.getPrivate()).getSeed();
@@ -978,7 +1022,7 @@ static private OneKey generateOkpKey(String curveName, CBORObject curve) throws
key.add(KeyKeys.OKP_D, CBORObject.FromObject(rgbD));
key.publicKey = keyPair.getPublic();
key.privateKey = keyPair.getPrivate();
-
+
return key;
}
catch (NoSuchAlgorithmException | NoSuchProviderException e) {
@@ -987,7 +1031,7 @@ static private OneKey generateOkpKey(String curveName, CBORObject curve) throws
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
@@ -1012,7 +1056,9 @@ private void CheckRsaKey() throws CoseException {
);
try {
- KeyFactory factory = KeyFactory.getInstance("RSA");
+ KeyFactory factory = cryptoContext.getProvider() != null ?
+ KeyFactory.getInstance("RSA", cryptoContext.getProvider()) :
+ KeyFactory.getInstance("RSA");
publicKey = factory.generatePublic(spec);
} catch (NoSuchAlgorithmException ex) {
throw new CoseException("No provider for algorithm", ex);
@@ -1099,7 +1145,9 @@ private void CheckRsaKey() throws CoseException {
try {
- KeyFactory factory = KeyFactory.getInstance("RSA");
+ KeyFactory factory = cryptoContext.getProvider() != null ?
+ KeyFactory.getInstance("RSA", cryptoContext.getProvider()) :
+ KeyFactory.getInstance("RSA");
privateKey = factory.generatePrivate(privateKeySpec);
} catch (NoSuchAlgorithmException ex) {
throw new CoseException("No provider for algorithm", ex);
@@ -1109,9 +1157,11 @@ private void CheckRsaKey() throws CoseException {
}
}
- static private OneKey generateRSAKey(int keySize) throws CoseException {
+ static private OneKey generateRSAKey(int keySize, Provider provider) throws CoseException {
try {
- KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
+ KeyPairGenerator gen = provider != null ?
+ KeyPairGenerator.getInstance("RSA", provider) :
+ KeyPairGenerator.getInstance("RSA");
gen.initialize(keySize);
KeyPair keyPair = gen.genKeyPair();
diff --git a/src/main/java/COSE/Recipient.java b/src/main/java/COSE/Recipient.java
index d76e04c..ba88483 100644
--- a/src/main/java/COSE/Recipient.java
+++ b/src/main/java/COSE/Recipient.java
@@ -20,7 +20,7 @@
import javax.crypto.spec.SecretKeySpec;
/**
- *
+ *
* @author jimsch
*/
public class Recipient extends Message {
@@ -28,23 +28,31 @@ public class Recipient extends Message {
private OneKey senderKey;
byte[] rgbEncrypted;
List recipientList;
-
+ private CryptoContext cryptoContext = new CryptoContext(null);
+
+ /**
+ * Gets the {@link CryptoContext} to set a different JCA Provider
+ */
+ public CryptoContext getCryptoContext() {
+ return cryptoContext;
+ }
+
@Override
public void DecodeFromCBORObject(CBORObject objRecipient) throws CoseException {
if ((objRecipient.size() != 3) && (objRecipient.size() !=4)) throw new CoseException("Invalid Recipient structure");
-
+
if (objRecipient.get(0).getType() == CBORType.ByteString) {
if (objRecipient.get(0).GetByteString().length == 0) objProtected = CBORObject.NewMap();
else objProtected = CBORObject.DecodeFromBytes(objRecipient.get(0).GetByteString());
}
else throw new CoseException("Invalid Recipient structure");
-
+
if (objRecipient.get(1).getType() == CBORType.Map) objUnprotected = objRecipient.get(1);
else throw new CoseException("Invalid Recipient structure");
-
+
if (objRecipient.get(2).getType() == CBORType.ByteString) rgbEncrypted = objRecipient.get(2).GetByteString();
else throw new CoseException("Invalid Recipient structure");
-
+
if (objRecipient.size() == 4) {
if (objRecipient.get(3).getType() == CBORType.Array) {
recipientList = new ArrayList<>();
@@ -59,11 +67,11 @@ public void DecodeFromCBORObject(CBORObject objRecipient) throws CoseException {
}
@Override
- protected CBORObject EncodeCBORObject() throws CoseException {
+ protected CBORObject EncodeCBORObject() throws CoseException {
CBORObject obj = CBORObject.NewArray();
if (objProtected.size() > 0) obj.Add(objProtected.EncodeToBytes());
else obj.Add(CBORObject.FromObject(new byte[0]));
-
+
obj.Add(objUnprotected);
obj.Add(rgbEncrypted);
if (recipientList != null) {
@@ -73,14 +81,14 @@ protected CBORObject EncodeCBORObject() throws CoseException {
}
obj.Add(objR);
}
-
+
return obj;
}
-
+
public byte[] decrypt(AlgorithmID algCEK, Recipient recip) throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
byte[] rgbKey = null;
-
+
if (recip != this) {
for (Recipient r : recipientList) {
if (recip == r) {
@@ -94,12 +102,12 @@ else if (!r.recipientList.isEmpty()) {
}
}
}
-
+
switch (alg) {
case Direct: // Direct
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Mismatch of algorithm and key");
return privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString();
-
+
case HKDF_HMAC_SHA_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Needs to be an octet key");
return HKDF(privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString(), algCEK.getKeySize(), algCEK, "SHA256");
@@ -117,55 +125,55 @@ else if (!r.recipientList.isEmpty()) {
}
else if (privateKey != null) throw new CoseException("Key and algorithm do not agree");
return AES_KeyWrap_Decrypt(alg, rgbKey);
-
+
case ECDH_ES_HKDF_256:
case ECDH_SS_HKDF_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
return HKDF(rgbKey, algCEK.getKeySize(), algCEK, "SHA256");
-
+
case ECDH_ES_HKDF_512:
case ECDH_SS_HKDF_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
return HKDF(rgbKey, algCEK.getKeySize(), algCEK, "SHA512");
-
+
case ECDH_ES_HKDF_256_AES_KW_128:
case ECDH_SS_HKDF_256_AES_KW_128:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 128, AlgorithmID.AES_KW_128, "SHA256");
return AES_KeyWrap_Decrypt(AlgorithmID.AES_KW_128, rgbKey);
-
+
case ECDH_ES_HKDF_256_AES_KW_192:
case ECDH_SS_HKDF_256_AES_KW_192:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 192, AlgorithmID.AES_KW_192, "SHA256");
return AES_KeyWrap_Decrypt(AlgorithmID.AES_KW_192, rgbKey);
-
+
case ECDH_ES_HKDF_256_AES_KW_256:
case ECDH_SS_HKDF_256_AES_KW_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
rgbKey = ECDH_GenSecret(privateKey);
rgbKey = HKDF(rgbKey, 256, AlgorithmID.AES_KW_256, "SHA256");
return AES_KeyWrap_Decrypt(AlgorithmID.AES_KW_256, rgbKey);
-
+
default:
throw new CoseException("Unsupported Recipent Algorithm");
}
}
-
+
public void encrypt() throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
byte[] rgbKey = null;
SecureRandom random;
-
+
int recipientTypes = 0;
-
+
if (recipientList != null && !recipientList.isEmpty()) {
if (privateKey != null) throw new CoseException("Cannot have dependent recipients if key is specified");
-
+
for (Recipient r : recipientList) {
switch (r.getRecipientType()) {
case 1:
@@ -173,16 +181,16 @@ public void encrypt() throws CoseException {
recipientTypes |= 1;
rgbKey = r.getKey(alg);
break;
-
+
default:
recipientTypes |= 2;
break;
}
}
}
-
+
if (recipientTypes == 3) throw new CoseException("Do not mix direct and indirect recipients");
-
+
if (recipientTypes == 2) {
rgbKey = new byte[alg.getKeySize()/8];
random = new SecureRandom();
@@ -195,7 +203,7 @@ public void encrypt() throws CoseException {
case HKDF_HMAC_SHA_512:
rgbEncrypted = new byte[0];
break;
-
+
case AES_KW_128:
case AES_KW_192:
case AES_KW_256:
@@ -205,14 +213,14 @@ public void encrypt() throws CoseException {
}
rgbEncrypted = AES_KeyWrap_Encrypt(alg, rgbKey);
break;
-
+
case ECDH_ES_HKDF_256:
case ECDH_ES_HKDF_512:
case ECDH_SS_HKDF_256:
case ECDH_SS_HKDF_512:
rgbEncrypted = new byte[0];
break;
-
+
case ECDH_ES_HKDF_256_AES_KW_128:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
@@ -233,7 +241,7 @@ public void encrypt() throws CoseException {
rgbKey = HKDF(rgbKey, 128, AlgorithmID.AES_KW_128, "SHA256");
rgbEncrypted = AES_KeyWrap_Encrypt(AlgorithmID.AES_KW_128, rgbKey);
break;
-
+
case ECDH_ES_HKDF_256_AES_KW_192:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
@@ -279,7 +287,7 @@ public void encrypt() throws CoseException {
default:
throw new CoseException("Unsupported Recipient Algorithm");
}
-
+
if (recipientList != null) {
for (Recipient r : recipientList) {
r.SetContent(rgbKey);
@@ -292,7 +300,7 @@ public void addRecipient(Recipient recipient) {
if (recipientList == null) recipientList = new ArrayList();
recipientList.add(recipient);
}
-
+
public List getRecipientList() {
return recipientList;
}
@@ -300,11 +308,11 @@ public List getRecipientList() {
public Recipient getRecipient(int iRecipient) {
return recipientList.get(iRecipient);
}
-
+
public int getRecipientCount() {
return recipientList.size();
}
-
+
public int getRecipientType() throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
switch (alg) {
@@ -316,43 +324,43 @@ public int getRecipientType() throws CoseException {
case ECDH_SS_HKDF_256:
case ECDH_SS_HKDF_512:
return 1;
-
+
default:
return 9;
}
}
-
+
public byte[] getKey(AlgorithmID algCEK) throws CoseException {
byte[] rgbSecret;
SecureRandom random;
-
+
if (privateKey == null) throw new CoseException("Private key not set for recipient");
-
+
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
-
+
switch (alg) {
case Direct:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Key and algorithm do not agree");
return privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString();
-
+
case AES_KW_128:
case AES_KW_192:
case AES_KW_256:
if (!privateKey.HasKeyType(KeyKeys.KeyType_Octet)) throw new CoseException("Key and algorithm do not agree");
return privateKey.get(KeyKeys.Octet_K).GetByteString();
-
+
case ECDH_ES_HKDF_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA256");
-
+
case ECDH_ES_HKDF_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
ECDH_GenEphemeral();
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA512");
-
+
case ECDH_SS_HKDF_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
if (findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR()) == null) {
@@ -363,7 +371,7 @@ public byte[] getKey(AlgorithmID algCEK) throws CoseException {
}
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA256");
-
+
case ECDH_SS_HKDF_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) throw new CoseException("Key and algorithm do not agree");
if (findAttribute(HeaderKeys.HKDF_Context_PartyU_nonce.AsCBOR()) == null) {
@@ -374,11 +382,11 @@ public byte[] getKey(AlgorithmID algCEK) throws CoseException {
}
rgbSecret = ECDH_GenSecret(privateKey);
return HKDF(rgbSecret, algCEK.getKeySize(), algCEK, "SHA512");
-
+
case HKDF_HMAC_SHA_256:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Needs to be an octet key");
return HKDF(privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString(), algCEK.getKeySize(), algCEK, "SHA256");
-
+
case HKDF_HMAC_SHA_512:
if (privateKey.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_Octet) throw new CoseException("Needs to be an octet key");
return HKDF(privateKey.get(KeyKeys.Octet_K.AsCBOR()).GetByteString(), algCEK.getKeySize(), algCEK, "SHA512");
@@ -387,10 +395,10 @@ public byte[] getKey(AlgorithmID algCEK) throws CoseException {
throw new CoseException("Recipient Algorithm not supported");
}
}
-
+
/**
* Set the key for encrypting/decrypting the recipient key.
- *
+ *
* @param key private key for encrypting or decrypting
* @exception CoseException Internal COSE package error.
* @deprecated In COSE 0.9.1, use SetKey(OneKey)
@@ -399,10 +407,10 @@ public byte[] getKey(AlgorithmID algCEK) throws CoseException {
public void SetKey(CBORObject key) throws CoseException {
privateKey = new OneKey(key);
}
-
+
/**
* Set the key for encrypting/decrypting the recipient key.
- *
+ *
* @param key private key for encrypting or decrypting
*/
public void SetKey(OneKey key) {
@@ -413,17 +421,19 @@ public void SetKey(OneKey key) {
public void SetSenderKey(CBORObject key) throws CoseException {
senderKey = new OneKey(key);
}
-
+
public void SetSenderKey(OneKey key) {
senderKey = key;
}
-
+
private byte[] AES_KeyWrap_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseException
{
if (rgbKey.length != alg.getKeySize() / 8) throw new CoseException("Key is not the correct size");
try {
- Cipher cipher = Cipher.getInstance("AESWrap");
+ Cipher cipher = cryptoContext.getProvider() != null ?
+ Cipher.getInstance("AESWrap", cryptoContext.getProvider()) :
+ Cipher.getInstance("AESWrap");
cipher.init(Cipher.WRAP_MODE, new SecretKeySpec(rgbKey, "AESWrap"));
return cipher.wrap(new SecretKeySpec(rgbContent, "AES"));
} catch (NoSuchAlgorithmException ex) {
@@ -432,13 +442,15 @@ private byte[] AES_KeyWrap_Encrypt(AlgorithmID alg, byte[] rgbKey) throws CoseEx
throw new CoseException("Key Wrap failure", ex);
}
}
-
+
private byte[] AES_KeyWrap_Decrypt(AlgorithmID alg, byte[] rgbKey) throws CoseException
{
if (rgbKey.length != alg.getKeySize() / 8) throw new CoseException("Key is not the correct size");
try {
- Cipher cipher = Cipher.getInstance("AESWrap");
+ Cipher cipher = cryptoContext.getProvider() != null ?
+ Cipher.getInstance("AESWrap", cryptoContext.getProvider()) :
+ Cipher.getInstance("AESWrap");
cipher.init(Cipher.UNWRAP_MODE, new SecretKeySpec(rgbKey, "AESWrap"));
return ((SecretKeySpec)cipher.unwrap(rgbEncrypted, "AES", Cipher.SECRET_KEY)).getEncoded();
} catch (NoSuchAlgorithmException ex) {
@@ -453,18 +465,18 @@ private byte[] AES_KeyWrap_Decrypt(AlgorithmID alg, byte[] rgbKey) throws CoseEx
throw new CoseException("Key Unwrap failure", ex);
}
}
-
+
private void ECDH_GenEphemeral() throws CoseException {
OneKey secretKey = OneKey.generateKey(privateKey.get(KeyKeys.EC2_Curve));
-
+
// pack into EPK header
CBORObject epk = secretKey.PublicKey().AsCBOR();
addAttribute(HeaderKeys.ECDH_EPK, epk, Attribute.UNPROTECTED);
-
+
// apply as senderKey
senderKey = secretKey;
}
-
+
private byte[] ECDH_GenSecret(OneKey key) throws CoseException {
OneKey epk;
if (senderKey != null) {
@@ -480,7 +492,7 @@ private byte[] ECDH_GenSecret(OneKey key) throws CoseException {
}
epk = new OneKey(cn);
}
-
+
if (key.get(KeyKeys.KeyType.AsCBOR()) != KeyKeys.KeyType_EC2) {
throw new CoseException("Not an EC2 Key");
}
@@ -490,11 +502,13 @@ private byte[] ECDH_GenSecret(OneKey key) throws CoseException {
if (epk.get(KeyKeys.EC2_Curve.AsCBOR()) != key.get(KeyKeys.EC2_Curve.AsCBOR())) {
throw new CoseException("Curves are not the same");
}
-
+
try {
PublicKey pubKey = epk.AsPublicKey();
PrivateKey privKey = key.AsPrivateKey();
- KeyAgreement ecdh = KeyAgreement.getInstance("ECDH");
+ KeyAgreement ecdh = cryptoContext.getProvider() != null ?
+ KeyAgreement.getInstance("ECDH", cryptoContext.getProvider()) :
+ KeyAgreement.getInstance("ECDH");
ecdh.init(privKey);
ecdh.doPhase(pubKey, true);
return ecdh.generateSecret();
@@ -509,9 +523,11 @@ private byte[] HKDF(byte[] secret, int cbitKey, AlgorithmID alg, String digest)
final String HMAC_ALG_NAME = "Hmac" + digest;
byte[] rgbContext = GetKDFInput(cbitKey, alg);
-
+
try {
- Mac hmac = Mac.getInstance(HMAC_ALG_NAME);
+ Mac hmac = cryptoContext.getProvider() != null ?
+ Mac.getInstance(HMAC_ALG_NAME, cryptoContext.getProvider()) :
+ Mac.getInstance(HMAC_ALG_NAME);
int hashLen = hmac.getMacLength();
CBORObject cnSalt = findAttribute(HeaderKeys.HKDF_Salt.AsCBOR());
@@ -551,12 +567,12 @@ private byte[] HKDF(byte[] secret, int cbitKey, AlgorithmID alg, String digest)
private byte[] GetKDFInput(int cbitKey, AlgorithmID algorithmID) {
CBORObject obj;
-
+
CBORObject contextArray = CBORObject.NewArray();
-
+
// First element is - algorithm ID
contextArray.Add(algorithmID.AsCBOR());
-
+
// Second item is - Party U info
CBORObject info = CBORObject.NewArray();
contextArray.Add(info);
diff --git a/src/main/java/COSE/SignCommon.java b/src/main/java/COSE/SignCommon.java
index 4b4c3a0..d009816 100644
--- a/src/main/java/COSE/SignCommon.java
+++ b/src/main/java/COSE/SignCommon.java
@@ -7,6 +7,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
+import java.security.Provider;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Arrays;
@@ -26,9 +27,8 @@ byte[] computeSignature(byte[] rgbToBeSigned, OneKey cnKey) throws CoseException
static byte[] computeSignature(AlgorithmID alg, byte[] rgbToBeSigned, OneKey cnKey) throws CoseException {
String algName = null;
- String provider = null;
int sigLen = 0;
-
+
switch (alg) {
case ECDSA_256:
algName = "SHA256withECDSA";
@@ -44,35 +44,35 @@ static byte[] computeSignature(AlgorithmID alg, byte[] rgbToBeSigned, OneKey cnK
break;
case EDDSA:
algName = "NonewithEdDSA";
- provider = "EdDSA";
break;
-
+
case RSA_PSS_256:
algName = "SHA256withRSA/PSS";
break;
-
+
case RSA_PSS_384:
algName = "SHA384withRSA/PSS";
break;
-
+
case RSA_PSS_512:
algName = "SHA512withRSA/PSS";
break;
-
+
default:
throw new CoseException("Unsupported Algorithm Specified");
}
-
+
if (cnKey == null) {
throw new NullPointerException();
}
-
+
PrivateKey privKey = cnKey.AsPrivateKey();
if (privKey == null) {
throw new CoseException("Private key required to sign");
}
-
+
byte[] result = null;
+ Provider provider = cnKey.getCryptoContext().getProvider();
try {
Signature sig = provider == null ? Signature.getInstance(algName) :
Signature.getInstance(algName, provider);
@@ -87,10 +87,10 @@ static byte[] computeSignature(AlgorithmID alg, byte[] rgbToBeSigned, OneKey cnK
} catch (Exception ex) {
throw new CoseException("Signature failure", ex);
}
-
+
return result;
}
-
+
private static byte[] convertDerToConcat(byte[] der, int len) throws CoseException {
// this is far too naive
byte[] concat = new byte[len * 2];
@@ -104,7 +104,7 @@ private static byte[] convertDerToConcat(byte[] der, int len) throws CoseExcepti
// offset actually 4 + (7-bits of byte 1)
kLen = 4 + (der[1] & 0x7f);
}
-
+
// calculate start/end of R
int rOff = kLen;
int rLen = der[rOff - 1];
@@ -117,7 +117,7 @@ private static byte[] convertDerToConcat(byte[] der, int len) throws CoseExcepti
}
// copy R
System.arraycopy(der, rOff, concat, rPad, rLen);
-
+
// calculate start/end of S
int sOff = rOff + rLen + 2;
int sLen = der[sOff - 1];
@@ -130,10 +130,10 @@ private static byte[] convertDerToConcat(byte[] der, int len) throws CoseExcepti
}
// copy S
System.arraycopy(der, sOff, concat, len + sPad, sLen);
-
+
return concat;
}
-
+
boolean validateSignature(byte[] rgbToBeSigned, byte[] rgbSignature, OneKey cnKey) throws CoseException {
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
return validateSignature(alg, rgbToBeSigned, rgbSignature, cnKey);
@@ -141,7 +141,6 @@ boolean validateSignature(byte[] rgbToBeSigned, byte[] rgbSignature, OneKey cnKe
static boolean validateSignature(AlgorithmID alg, byte[] rgbToBeSigned, byte[] rgbSignature, OneKey cnKey) throws CoseException {
String algName = null;
- String provider = null;
boolean convert = false;
switch (alg) {
@@ -157,12 +156,11 @@ static boolean validateSignature(AlgorithmID alg, byte[] rgbToBeSigned, byte[] r
algName = "SHA512withECDSA";
convert = true;
break;
-
+
case EDDSA:
algName = "NonewithEdDSA";
- provider = "EdDSA";
break;
-
+
case RSA_PSS_256:
algName = "SHA256withRSA/PSS";
break;
@@ -187,6 +185,7 @@ static boolean validateSignature(AlgorithmID alg, byte[] rgbToBeSigned, byte[] r
if (pubKey == null) {
throw new CoseException("Public key required to verify");
}
+ Provider provider = cnKey.getCryptoContext().getProvider();
boolean result = false;
try {
diff --git a/src/main/java/COSE/Signer.java b/src/main/java/COSE/Signer.java
index a147b03..7b0b1ef 100644
--- a/src/main/java/COSE/Signer.java
+++ b/src/main/java/COSE/Signer.java
@@ -25,36 +25,36 @@ public class Signer extends Attribute {
protected byte[] rgbSignature;
protected String contextString;
OneKey cnKey;
-
+
/**
* Create a new signer object to add to a SignMessage
*/
public Signer() {
contextString = "Signature";
}
-
+
/**
* Create a new signer object for a SignMessage and set the key to be used.
- *
+ *
* @param key key to use for signing.
*/
public Signer(OneKey key) {
contextString = "Signature";
cnKey = key;
}
-
+
/**
* Remove the key object from the signer
- *
+ *
* @since COSE 0.9.1
*/
public void clearKey() {
cnKey = null;
}
-
+
/**
* Set a key object on a signer
- *
+ *
* @since COSE 0.9.1
* @param keyIn key to be used for signing or verification
* @throws CoseException - Invalid key passed in
@@ -62,45 +62,45 @@ public void clearKey() {
public void setKey(OneKey keyIn) throws CoseException {
setupKey(keyIn);
}
-
+
/**
* Set the key on the object, if there is not a signature on this object then set
* the algorithm and the key id from the key if they exist on the key and do not exist in the message.
- *
+ *
* @param key key to be used]
*/
private void setupKey(OneKey key) throws CoseException {
CBORObject cn2;
CBORObject cn;
-
+
cnKey = key;
if (rgbSignature != null) return;
-
+
cn = key.get(KeyKeys.Algorithm);
if (cn != null) {
cn2 = findAttribute(HeaderKeys.Algorithm);
if (cn2 == null) addAttribute(HeaderKeys.Algorithm, cn, Attribute.PROTECTED);
}
-
+
cn = key.get(KeyKeys.KeyId);
if (cn != null) {
cn2 = findAttribute(HeaderKeys.KID);
if (cn2 == null) addAttribute(HeaderKeys.KID, cn, Attribute.UNPROTECTED);
}
}
-
+
/**
* Internal function used in creating a Sign1Message object from a byte string.
- *
+ *
* @param obj COSE_Sign1 encoded object.
* @throws CoseException Errors generated by the COSE module
*/
protected void DecodeFromCBORObject(CBORObject obj) throws CoseException {
if (obj.getType() != CBORType.Array) throw new CoseException("Invalid Signer structure");
-
+
if (obj.size() != 3) throw new CoseException("Invalid Signer structure");
-
+
if (obj.get(0).getType() == CBORType.ByteString) {
rgbProtected = obj.get(0).GetByteString();
if (rgbProtected.length == 0) {
@@ -112,12 +112,12 @@ protected void DecodeFromCBORObject(CBORObject obj) throws CoseException {
}
}
else throw new CoseException("Invalid Signer structure");
-
+
if (obj.get(1).getType() == CBORType.Map) {
objUnprotected = obj.get(1);
}
else throw new CoseException("Invalid Signer structure");
-
+
if (obj.get(2).getType() == CBORType.ByteString) rgbSignature = obj.get(2).GetByteString();
else if (!obj.get(2).isNull()) throw new CoseException("Invalid Signer structure");
@@ -127,13 +127,13 @@ protected void DecodeFromCBORObject(CBORObject obj) throws CoseException {
(countersignature.getValues().isEmpty())) {
throw new CoseException("Invalid countersignature attribute");
}
-
+
if (countersignature.get(0).getType() == CBORType.Array) {
for (CBORObject csObj : countersignature.getValues()) {
if (csObj.getType() != CBORType.Array) {
throw new CoseException("Invalid countersignature attribute");
}
-
+
CounterSign cs = new CounterSign(csObj);
cs.setObject(this);
this.addCountersignature(cs);
@@ -145,22 +145,22 @@ protected void DecodeFromCBORObject(CBORObject obj) throws CoseException {
this.addCountersignature(cs);
}
}
-
+
countersignature = this.findAttribute(HeaderKeys.CounterSignature0, UNPROTECTED);
if (countersignature != null) {
if (countersignature.getType() != CBORType.ByteString) {
throw new CoseException("Invalid Countersignature0 attribute");
}
-
+
CounterSign1 cs = new CounterSign1(countersignature.GetByteString());
cs.setObject(this);
this.counterSign1 = cs;
}
- }
-
+ }
+
/**
* Internal function used to create a serialization of a COSE_Sign1 message
- *
+ *
* @return CBOR object which can be encoded.
* @throws CoseException Errors generated by the COSE module
*/
@@ -169,11 +169,11 @@ protected CBORObject EncodeToCBORObject() throws CoseException {
if (rgbSignature == null) throw new CoseException("Message not yet signed");
if (rgbProtected == null) throw new CoseException("Internal Error");
CBORObject obj = CBORObject.NewArray();
-
+
obj.Add(rgbProtected);
obj.Add(objUnprotected);
obj.Add(rgbSignature);
-
+
return obj;
}
@@ -183,7 +183,7 @@ public void sign(byte[] rgbBodyProtected, byte[] rgbContent) throws CoseExceptio
if(objProtected.size() == 0) rgbProtected = new byte[0];
else rgbProtected = objProtected.EncodeToBytes();
}
-
+
CBORObject obj = CBORObject.NewArray();
obj.Add(contextString);
obj.Add(rgbBodyProtected);
@@ -192,12 +192,12 @@ public void sign(byte[] rgbBodyProtected, byte[] rgbContent) throws CoseExceptio
obj.Add(rgbContent);
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
-
- rgbSignature = SignCommon.computeSignature(alg, obj.EncodeToBytes(), cnKey);
-
+
+ rgbSignature = SignCommon.computeSignature(alg, obj.EncodeToBytes(), cnKey);
+
ProcessCounterSignatures();
}
-
+
public boolean validate(byte[] rgbBodyProtected, byte[] rgbContent) throws CoseException
{
CBORObject obj = CBORObject.NewArray();
@@ -209,31 +209,31 @@ public boolean validate(byte[] rgbBodyProtected, byte[] rgbContent) throws CoseE
AlgorithmID alg = AlgorithmID.FromCBOR(findAttribute(HeaderKeys.Algorithm));
- return SignCommon.validateSignature(alg, obj.EncodeToBytes(), rgbSignature, cnKey);
+ return SignCommon.validateSignature(alg, obj.EncodeToBytes(), rgbSignature, cnKey);
}
-
-
+
+
List counterSignList = new ArrayList();
CounterSign1 counterSign1;
-
+
public void addCountersignature(CounterSign countersignature)
{
counterSignList.add(countersignature);
}
-
+
public List getCountersignerList() {
return counterSignList;
}
-
+
public CounterSign1 getCountersign1() {
return counterSign1;
}
-
+
public void setCountersign1(CounterSign1 value) {
counterSign1 = value;
}
-
+
protected void ProcessCounterSignatures() throws CoseException {
if (!counterSignList.isEmpty()) {
if (counterSignList.size() == 1) {
@@ -249,19 +249,19 @@ protected void ProcessCounterSignatures() throws CoseException {
addAttribute(HeaderKeys.CounterSignature, list, Attribute.UNPROTECTED);
}
}
-
+
if (counterSign1 != null) {
counterSign1.sign(rgbProtected, rgbSignature);
addAttribute(HeaderKeys.CounterSignature0, counterSign1.EncodeToCBORObject(), Attribute.UNPROTECTED);
}
}
-
+
public boolean validate(CounterSign1 countersignature) throws CoseException {
return countersignature.validate(rgbProtected, rgbSignature);
}
-
+
public boolean validate(CounterSign countersignature) throws CoseException {
return countersignature.validate(rgbProtected, rgbSignature);
}
-
+
}
diff --git a/src/test/java/COSE/RegressionTest.java b/src/test/java/COSE/RegressionTest.java
index 6a506d0..9f30cb6 100644
--- a/src/test/java/COSE/RegressionTest.java
+++ b/src/test/java/COSE/RegressionTest.java
@@ -30,27 +30,32 @@ public class RegressionTest extends TestBase {
@Parameters(name = "{index}: {0})")
public static Collection