Skip to content

Commit

Permalink
Add support for pkcs encryption with secret key for recipient digital…
Browse files Browse the repository at this point in the history
  • Loading branch information
Frederik Libert committed Dec 22, 2023
1 parent 2427a93 commit 0551e02
Showing 1 changed file with 78 additions and 28 deletions.
106 changes: 78 additions & 28 deletions lib/pkcs7.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,6 @@ p7.createSignedData = function() {
* }]
* });
*
* TODO: Support [subjectKeyIdentifier] as signer's ID.
*
* @param signer the signer information:
* key the signer's private key.
* [certificate] a certificate containing the public key
Expand All @@ -241,6 +239,7 @@ p7.createSignedData = function() {
* [issuer] the issuer attributes (eg: cert.issuer.attributes).
* [serialNumber] the signer's certificate's serial number in
* hexadecimal (eg: cert.serialNumber).
* [subjectKeyIdentifier] use this as alternative for certificate or issuer and serialNumber in case you use a keypair without a certificate
* [digestAlgorithm] the message digest OID, as a string, to use
* (eg: forge.pki.oids.sha1).
* [authenticatedAttributes] an optional array of attributes
Expand Down Expand Up @@ -271,16 +270,16 @@ p7.createSignedData = function() {
// ensure OID known for digest algorithm
var digestAlgorithm = signer.digestAlgorithm || forge.pki.oids.sha1;
switch(digestAlgorithm) {
case forge.pki.oids.sha1:
case forge.pki.oids.sha256:
case forge.pki.oids.sha384:
case forge.pki.oids.sha512:
case forge.pki.oids.md5:
break;
default:
throw new Error(
'Could not add PKCS#7 signer; unknown message digest algorithm: ' +
digestAlgorithm);
case forge.pki.oids.sha1:
case forge.pki.oids.sha256:
case forge.pki.oids.sha384:
case forge.pki.oids.sha512:
case forge.pki.oids.md5:
break;
default:
throw new Error(
'Could not add PKCS#7 signer; unknown message digest algorithm: ' +
digestAlgorithm);
}

let signatureAlgorithm = (signer.signatureAlgorithm) ? signer.signatureAlgorithm : forge.pki.oids.rsaEncryption;
Expand Down Expand Up @@ -720,7 +719,7 @@ p7.createEnvelopedData = function() {
* Add (another) entity to list of recipients.
*
* @param publicKey
* @param subjectKeyIdentifier
* @param subjectKeyIdentifier identifier for the public key
*/
addRecipientPublicKey: function(publicKey, subjectKeyIdentifier) {
let key = (typeof publicKey === 'string') ? forge.pki.publicKeyFromPem(publicKey) : publicKey;
Expand All @@ -737,6 +736,26 @@ p7.createEnvelopedData = function() {
});
},

/**
* Add (another) entity to list of recipients.
*
* @param secretKey symmetric key to use for this recipient (kek)
* @param kekIdentifier identifier for this key (kek)
* @param keyEncryptionAlgorithm algorithm to use to protect the content encryption key
* @param keyEncryptionFunction function that implements the key encryption algorithm
*/
addRecipientSecretKey: function(secretKey, kekIdentifier, keyEncryptionAlgorithm, keyEncryptionFunction) {
msg.recipients.push({
version: 4,
kekIdentifier: kekIdentifier,
encryptedContent: {
algorithm: keyEncryptionAlgorithm,
key: secretKey,
keyEncryptionFunction: keyEncryptionFunction
}
});
},

/**
* Encrypt enveloped content.
*
Expand All @@ -751,7 +770,7 @@ p7.createEnvelopedData = function() {
* @param [cipher] The OID of the symmetric cipher to use.
*/
encrypt: function(key, cipher) {
// Part 1: Symmetric encryption
// Part 1: Content encryption with symmetric key
if(msg.encryptedContent.content === undefined) {
cipher = cipher || msg.encryptedContent.algorithm;
key = key || msg.encryptedContent.key;
Expand Down Expand Up @@ -813,7 +832,7 @@ p7.createEnvelopedData = function() {
msg.encryptedContent.content = ciph.output;
}

// Part 2: asymmetric encryption for each recipient
// Part 2: content encryption key encryption for each recipient
for(var i = 0; i < msg.recipients.length; ++i) {
var recipient = msg.recipients[i];

Expand All @@ -828,10 +847,13 @@ p7.createEnvelopedData = function() {
recipient.encryptedContent.key.encrypt(
msg.encryptedContent.key.data);
break;

default:
throw new Error('Unsupported asymmetric cipher, OID ' +
recipient.encryptedContent.algorithm);
if (recipient.encryptedContent.keyEncryptionFunction) {
// caller specified function to support alternative encryption so call it.
recipient.encryptedContent.content = recipient.encryptedContent.keyEncryptionFunction(msg.encryptedContent.key, recipient.encryptedContent.key);
} else {
throw new Error('Unsupported key encryption algorithm: OID ' + recipient.encryptedContent.algorithm);
}
}
}
}
Expand Down Expand Up @@ -869,11 +891,25 @@ function _recipientFromAsn1(obj) {
};
}

/**
* Supports SubjectKeyIdentifier, KEKIdentifier, IssuerAndSerialNumber
**/
function _createAsn1RecipientIdentifier(obj) {
if (obj.version === 2) {
// subjectKeyIdentifier
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
obj.subjectKeyIdentifier);
} else if (obj.version === 4) {
// KEKIdentifier
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// keyIdentifier
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
obj.kekIdentifier),
// date
// asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''),
// other
// asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
]);
} else {
// issuerAndSerialNumber
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
Expand All @@ -886,14 +922,7 @@ function _createAsn1RecipientIdentifier(obj) {
}
}

/**
* Converts a single recipient object to an ASN.1 object.
*
* @param obj the recipient object.
*
* @return the ASN.1 RecipientInfo.
*/
function _recipientToAsn1(obj) {
function _createAsn1RecipientInfo(obj) {
return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// Version
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false,
Expand All @@ -905,14 +934,35 @@ function _recipientToAsn1(obj) {
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(obj.encryptedContent.algorithm).getBytes()),
// Parameter, force NULL, only RSA supported for now.
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
// asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')
]),
// EncryptedKey
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false,
obj.encryptedContent.content)
]);
}

/**
* Converts a single recipient object to an ASN.1 object.
* Supports KEKRecipientInfo, KetTransRecipientInfo.
*
* @param obj the recipient object.
*
* @return the ASN.1 RecipientInfo.
*/
function _recipientToAsn1(obj) {
if (obj.version === 4) {
// kekri [2] KEKRecipientInfo
return asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [
_createAsn1RecipientInfo(obj)
]);
} else {
// ktri KeyTransRecipientInfo
return _createAsn1RecipientInfo(obj);
}

}

/**
* Map a set of RecipientInfo ASN.1 objects to recipient objects.
*
Expand Down Expand Up @@ -1059,7 +1109,7 @@ function _signerToAsn1(obj) {
// algorithm
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false,
asn1.oidToDer(obj.signatureAlgorithm).getBytes()),
_createDigestEncryptionAlgorithmParameters(obj)
_createDigestEncryptionAlgorithmParameters(obj)
]));

// encryptedDigest
Expand Down

0 comments on commit 0551e02

Please sign in to comment.