cmsAssembleSignedData function
Assembles a detached CMS SignedData from a signedAttributes blob (the
SET OF from cmsSignedAttributes) and the RSA signature over it.
certificates is the DER chain, signer first. unsignedAttributes
(e.g. a signature-time-stamp) are carried in the SignerInfo's 1 field.
hash is the digest algorithm named in digestAlgorithms and the
SignerInfo (it must match what produced the message digest / signature).
Implementation
Uint8List cmsAssembleSignedData({
required Uint8List signedAttributes,
required Uint8List signature,
required List<Uint8List> certificates,
crypto.Hash hash = crypto.sha256,
List<Uint8List> unsignedAttributes = const [],
String eContentType = _Oid.data,
Uint8List? eContent,
Uint8List? signatureAlgorithm,
}) {
if (certificates.isEmpty) {
throw ArgumentError('at least the signer certificate is required');
}
final signerCert = X509Certificate.parse(certificates.first);
final digestOid = _digestOidFor(hash);
if (digestOid == null) {
throw ArgumentError('unsupported digest algorithm');
}
final digestAlgorithm = derSequence([derOid(digestOid), derNull()]);
final signerInfo = derSequence([
derInteger(BigInt.one),
derSequence([signerCert.issuerDer, derInteger(signerCert.serial)]),
digestAlgorithm,
// re-tag the SET of signed attributes as IMPLICIT [0]
Uint8List.fromList(signedAttributes)..[0] = DerTag.context(0),
// the signature AlgorithmIdentifier: rsaEncryption by default, or the
// ecdsa-with-SHAx identifier an EC signer passes in
signatureAlgorithm ?? derSequence([derOid(_Oid.rsaEncryption), derNull()]),
derOctetString(signature),
if (unsignedAttributes.isNotEmpty)
derContext(1, [for (final a in unsignedAttributes) ...a]),
]);
// RFC 5652 ยง5.1: SignedData is version 3 when the encapsulated content
// type is not id-data (e.g. an RFC 3161 TSTInfo), version 1 otherwise.
final version = eContentType == _Oid.data ? 1 : 3;
final signedData = derSequence([
derInteger(BigInt.from(version)),
derSet([digestAlgorithm]),
// encapContentInfo: detached carries only the type; encapsulated wraps
// the content in an EXPLICIT [0] OCTET STRING
derSequence([
derOid(eContentType),
if (eContent != null) derContext(0, derOctetString(eContent)),
]),
derContext(0, [for (final cert in certificates) ...cert]),
derSet([signerInfo]),
]);
return derSequence([
derOid(_Oid.signedData),
derContext(0, signedData),
]);
}