public version!: number; public originatorInfo?: OriginatorInfo; public recipientInfos!: RecipientInfo[]; public encryptedContentInfo!: EncryptedContentInfo; public unprotectedAttrs?: Attribute[];
public policy: Required<EncryptedContentInfoSplit>;
// Get internal properties from parsed schema this.version = asn1.result.version.valueBlock.valueDec;
if (ORIGINATOR_INFO in asn1.result) { this.originatorInfo = new OriginatorInfo({
schema: new asn1js.Sequence({
value: asn1.result.originatorInfo.valueBlock.value
})
});
}
this.recipientInfos = Array.from(asn1.result.recipientInfos, o => new RecipientInfo({ schema: o })); this.encryptedContentInfo = new EncryptedContentInfo({ schema: asn1.result.encryptedContentInfo });
if (UNPROTECTED_ATTRS in asn1.result) this.unprotectedAttrs = Array.from(asn1.result.unprotectedAttrs, o => new Attribute({ schema: o }));
}
public toSchema(): asn1js.Sequence { //#region Create array for output sequence const outputArray = [];
//#region Check type of certificate if (certificate.subjectPublicKeyInfo.algorithm.algorithmId.indexOf("1.2.840.113549") !== (-1))
variant = 1; // For the moment it is the only variant for RSA-based certificates else { if (certificate.subjectPublicKeyInfo.algorithm.algorithmId.indexOf("1.2.840.10045") !== (-1))
variant = 2; // For the moment it is the only variant for ECC-based certificates else thrownew Error(`Unknown type of certificate's public key: ${certificate.subjectPublicKeyInfo.algorithm.algorithmId}`);
} //#endregion
//#region Add new "recipient" depends on "variant" and certificate type switch (variant) { case1: // Key transport scheme
{
let algorithmId;
let algorithmParams;
const hashAlgorithm = new AlgorithmIdentifier({
algorithmId: hashOID,
algorithmParams: new asn1js.Null()
});
const rsaOAEPParams = new RSAESOAEPParams({
hashAlgorithm,
maskGenAlgorithm: new AlgorithmIdentifier({
algorithmId: "1.2.840.113549.1.1.8", // id-mgf1
algorithmParams: hashAlgorithm.toSchema()
})
});
algorithmParams = rsaOAEPParams.toSchema(); //#endregion
} else// Use old RSAES-PKCS1-v1_5 schema instead
{ //#region keyEncryptionAlgorithm
algorithmId = crypto.getOIDByAlgorithm({
name: "RSAES-PKCS1-v1_5"
}); if (algorithmId === EMPTY_STRING) thrownew Error("Can not find OID for RSAES-PKCS1-v1_5"); //#endregion
algorithmParams = new asn1js.Null();
}
//#region KeyTransRecipientInfo const keyInfo = new KeyTransRecipientInfo({
version: 0,
rid: new IssuerAndSerialNumber({
issuer: certificate.issuer,
serialNumber: certificate.serialNumber
}),
keyEncryptionAlgorithm: new AlgorithmIdentifier({
algorithmId,
algorithmParams
}),
recipientCertificate: certificate, // "encryptedKey" will be calculated in "encrypt" function
}); //#endregion
if (!parameters.keyEncryptionAlgorithmParams)
parameters.keyEncryptionAlgorithmParams = new asn1js.Null(); //#endregion
//#region Add new recipient based on passed variant switch (variant) { case1: // KEKRecipientInfo
{ // keyEncryptionAlgorithm const kekOID = crypto.getOIDByAlgorithm(parameters.keyEncryptionAlgorithm, true, "keyEncryptionAlgorithm");
//#region KEKRecipientInfo const keyInfo = new KEKRecipientInfo({
version: 4,
kekid: new KEKIdentifier({
keyIdentifier: new asn1js.OctetString({ valueHex: parameters.keyIdentifier })
}),
keyEncryptionAlgorithm: new AlgorithmIdentifier({
algorithmId: kekOID, /* ForAES-KWparamsareNULL,butforotheralgorithmcouldanothersituation.
*/
algorithmParams: parameters.keyEncryptionAlgorithmParams
}),
preDefinedKEK: preDefinedData // "encryptedKey" would be set in "ecrypt" function
}); //#endregion
//#region PasswordRecipientinfo const keyInfo = new PasswordRecipientinfo({
version: 0,
keyDerivationAlgorithm: new AlgorithmIdentifier({
algorithmId: pbkdf2OID,
algorithmParams: pbkdf2Params.toSchema()
}),
keyEncryptionAlgorithm: new AlgorithmIdentifier({
algorithmId: kekOID, /* ForAES-KWparamsareNULL,butforotheralgorithmcouldbeanothersituation.
*/
algorithmParams: parameters.keyEncryptionAlgorithmParams
}),
password: preDefinedData // "encryptedKey" would be set in "encrypt" function
}); //#endregion
//#region Final values for "CMS_ENVELOPED_DATA" this.recipientInfos.push(new RecipientInfo({
variant: 4,
value: keyInfo
})); //#endregion
} break; default: thrownew Error(`Unknown value for"variant": ${variant}`);
} //#endregion
}
// In fact there is no need in so long UKM, but RFC2631 // has requirement that "UserKeyMaterial" must be 512 bits long const ukmBuffer = new ArrayBuffer(64); const ukmView = new Uint8Array(ukmBuffer);
crypto.getRandomValues(ukmView); // Generate random values in 64 bytes long buffer
const recipientInfoParams = {
version: 3, // "originator" will be calculated in "encrypt" function because ephemeral key would be generated there
ukm: new asn1js.OctetString({ valueHex: ukmBuffer }),
keyEncryptionAlgorithm: new AlgorithmIdentifier({
algorithmId: ecdhOID,
algorithmParams: aesKW.toSchema()
}),
recipientEncryptedKeys: new RecipientEncryptedKeys({
encryptedKeys: [encryptedKey]
})
}; const keyInfo = new KeyAgreeRecipientInfo(Object.assign(recipientInfoParams, extraRecipientInfoParams)); //#endregion
//#region Final values for "CMS_ENVELOPED_DATA" this.recipientInfos.push(new RecipientInfo({
variant: 2,
value: keyInfo
})); //#endregion
}
/** *CreatesanewCMSEnvelopedDatacontentwithencrypteddata *@paramcontentEncryptionAlgorithmWebCryptoalgorithm.Forthemomentherecouldbeonly"AES-CBC"or"AES-GCM"algorithms. *@paramcontentToEncryptContenttoencrypt *@paramcryptoCryptoengine
*/ public async encrypt(contentEncryptionAlgorithm: Algorithm, contentToEncrypt: ArrayBuffer, crypto = common.getCrypto(true)): Promise<(void | { ecdhPrivateKey: CryptoKey; })[]> { //#region Initial variables const ivBuffer = new ArrayBuffer(16); // For AES we need IV 16 bytes long const ivView = new Uint8Array(ivBuffer);
crypto.getRandomValues(ivView);
const contentView = new Uint8Array(contentToEncrypt); //#endregion
//#endregion //#region Append common information to CMS_ENVELOPED_DATA this.version = 2; this.encryptedContentInfo = new EncryptedContentInfo({
disableSplit: this.policy.disableSplit,
contentType: "1.2.840.113549.1.7.1", // "data"
contentEncryptionAlgorithm: new AlgorithmIdentifier({
algorithmId: contentEncryptionOID,
algorithmParams: new asn1js.OctetString({ valueHex: ivBuffer })
}),
encryptedContent: new asn1js.OctetString({ valueHex: encryptedContent })
}); //#endregion
//#region Special sub-functions to work with each recipient's type const SubKeyAgreeRecipientInfo = async (index: number) => { //#region Initial variables const recipientInfo = this.recipientInfos[index].value as KeyAgreeRecipientInfo;
let recipientCurve: string; //#endregion
//#region Get public key and named curve from recipient's certificate or public key
let recipientPublicKey: CryptoKey; if (recipientInfo.recipientPublicKey) {
recipientCurve = (recipientInfo.recipientPublicKey.algorithm as EcKeyAlgorithm).namedCurve;
recipientPublicKey = recipientInfo.recipientPublicKey;
} elseif (recipientInfo.recipientCertificate) { const curveObject = recipientInfo.recipientCertificate.subjectPublicKeyInfo.algorithm.algorithmParams;
if (curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) thrownew Error(`Incorrect "recipientCertificate"for index ${index}`);
//#region Get length of used AES-KW algorithm const aesKWAlgorithm = new AlgorithmIdentifier({ schema: recipientInfo.keyEncryptionAlgorithm.algorithmParams });
//#region Get SHA algorithm used together with ECDH const ecdhAlgorithm = crypto.getAlgorithmByOID<any>(recipientInfo.keyEncryptionAlgorithm.algorithmId, true, "ecdhAlgorithm"); //#endregion
const derivedKeyRaw = await common.kdf(ecdhAlgorithm.kdf, derivedBits, kwAlgorithm.length, encodedInfo, crypto); //#endregion //#region Import AES-KW key from result of KDF function const awsKW = await crypto.importKey("raw", derivedKeyRaw, { name: "AES-KW" }, true, ["wrapKey"]); //#endregion //#region Finally wrap session key by using AES-KW algorithm const wrappedKey = await crypto.wrapKey("raw", sessionKey, awsKW, { name: "AES-KW" }); //#endregion //#region Append all necessary data to current CMS_RECIPIENT_INFO object //#region OriginatorIdentifierOrKey const originator = new OriginatorIdentifierOrKey();
originator.variant = 3;
originator.value = OriginatorPublicKey.fromBER(exportedECDHPublicKey);
//#region RSA-OAEP case if (algorithmParameters.name === "RSA-OAEP") { const schema = recipientInfo.keyEncryptionAlgorithm.algorithmParams; const rsaOAEPParams = new RSAESOAEPParams({ schema });
algorithmParameters.hash = crypto.getAlgorithmByOID(rsaOAEPParams.hashAlgorithm.algorithmId); if (("name" in algorithmParameters.hash) === false) thrownew Error(`Incorrect OID for hash algorithm: ${rsaOAEPParams.hashAlgorithm.algorithmId}`);
} //#endregion
//#region Get WebCrypto form of "keyEncryptionAlgorithm" const kekAlgorithm = crypto.getAlgorithmByOID(recipientInfo.keyEncryptionAlgorithm.algorithmId, true, "kekAlgorithm"); //#endregion
const kekKey = await crypto.importKey("raw", new Uint8Array(recipientInfo.preDefinedKEK),
kekAlgorithm, true,
["wrapKey"]); // Too specific for AES-KW //#endregion
//#region Wrap previously exported session key
const wrappedKey = await crypto.wrapKey("raw", sessionKey, kekKey, kekAlgorithm); //#endregion //#region Append all necessary data to current CMS_RECIPIENT_INFO object //#region RecipientEncryptedKey
recipientInfo.encryptedKey = new asn1js.OctetString({ valueHex: wrappedKey }); //#endregion //#endregion
};
const SubPasswordRecipientinfo = async (index: number) => { //#region Initial variables const recipientInfo = this.recipientInfos[index].value as PasswordRecipientinfo; // TODO Remove `as PasswordRecipientinfo`
let pbkdf2Params: PBKDF2Params; //#endregion
//#region Check that we have encoded "keyDerivationAlgorithm" plus "PBKDF2_params" in there
if (!recipientInfo.keyDerivationAlgorithm) thrownew Error("Please append encoded \"keyDerivationAlgorithm\"");
if (!recipientInfo.keyDerivationAlgorithm.algorithmParams) thrownew Error("Incorrectly encoded \"keyDerivationAlgorithm\"");
//#region Check for input parameters if ((recipientIndex + 1) > this.recipientInfos.length) { thrownew Error(`Maximum value for"index" is: ${this.recipientInfos.length - 1}`);
} //#endregion
//#region Special sub-functions to work with each recipient's type const SubKeyAgreeRecipientInfo = async (index: number) => { //#region Initial variables const recipientInfo = this.recipientInfos[index].value as KeyAgreeRecipientInfo; // TODO Remove `as KeyAgreeRecipientInfo` //#endregion
let curveOID: string;
let recipientCurve: string;
let recipientCurveLength: number; const originator = recipientInfo.originator;
//#region Get "namedCurve" parameter from recipient's certificate
if (decryptionParameters.recipientCertificate) { const curveObject = decryptionParameters.recipientCertificate.subjectPublicKeyInfo.algorithm.algorithmParams; if (curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) { thrownew Error(`Incorrect "recipientCertificate"for index ${index}`);
}
curveOID = curveObject.valueBlock.toString();
} elseif (originator.value.algorithm.algorithmParams) { const curveObject = originator.value.algorithm.algorithmParams; if (curveObject.constructor.blockName() !== asn1js.ObjectIdentifier.blockName()) { thrownew Error(`Incorrect originator for index ${index}`);
}
curveOID = curveObject.valueBlock.toString();
} else { thrownew Error("Parameter \"recipientCertificate\" is mandatory for \"KeyAgreeRecipientInfo\" if algorithm params are missing from originator");
}
if (!decryptionParameters.recipientPrivateKey) thrownew Error("Parameter \"recipientPrivateKey\" is mandatory for \"KeyAgreeRecipientInfo\"");
//#region Get length of used AES-KW algorithm const aesKWAlgorithm = new AlgorithmIdentifier({ schema: recipientInfo.keyEncryptionAlgorithm.algorithmParams });
//#region Get SHA algorithm used together with ECDH const ecdhAlgorithm = crypto.getAlgorithmByOID<any>(recipientInfo.keyEncryptionAlgorithm.algorithmId, true, "ecdhAlgorithm"); if (!ecdhAlgorithm.name) { thrownew Error(`Incorrect OID for key encryption algorithm: ${recipientInfo.keyEncryptionAlgorithm.algorithmId}`);
} //#endregion
//#region RSA-OAEP case if (algorithmParameters.name === "RSA-OAEP") { const schema = recipientInfo.keyEncryptionAlgorithm.algorithmParams; const rsaOAEPParams = new RSAESOAEPParams({ schema });
algorithmParameters.hash = crypto.getAlgorithmByOID(rsaOAEPParams.hashAlgorithm.algorithmId); if (("name" in algorithmParameters.hash) === false) thrownew Error(`Incorrect OID for hash algorithm: ${rsaOAEPParams.hashAlgorithm.algorithmId}`);
} //#endregion
let privateKey: CryptoKey;
let keyCrypto: SubtleCrypto = crypto; if (BufferSourceConverter.isBufferSource(decryptionParameters.recipientPrivateKey)) {
privateKey = await crypto.importKey( "pkcs8",
decryptionParameters.recipientPrivateKey,
algorithmParameters, true,
["decrypt"]
);
} else {
privateKey = decryptionParameters.recipientPrivateKey; if ("crypto" in decryptionParameters && decryptionParameters.crypto) {
keyCrypto = decryptionParameters.crypto.subtle;
}
}
//#region Import KEK from pre-defined data if (!decryptionParameters.preDefinedData) thrownew Error("Parameter \"preDefinedData\" is mandatory for \"KEKRecipientInfo\"");
//#region Get WebCrypto form of "keyEncryptionAlgorithm" const kekAlgorithm = crypto.getAlgorithmByOID<any>(recipientInfo.keyEncryptionAlgorithm.algorithmId, true, "kekAlgorithm"); //#endregion
const importedKey = await crypto.importKey("raw",
decryptionParameters.preDefinedData,
kekAlgorithm, true,
["unwrapKey"]); // Too specific for AES-KW
//#endregion //#region Unwrap previously exported session key //#region Get WebCrypto form of content encryption algorithm const algorithmId = this.encryptedContentInfo.contentEncryptionAlgorithm.algorithmId; const contentEncryptionAlgorithm = crypto.getAlgorithmByOID<any>(algorithmId, true, "contentEncryptionAlgorithm"); if (!contentEncryptionAlgorithm.name) { thrownew Error(`Incorrect "contentEncryptionAlgorithm": ${algorithmId}`);
} //#endregion
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.