Created
January 14, 2026 21:24
-
-
Save 5HT/7201247a23c9927b98d2ecd378f936af to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # CMS AUTH aka CMC | |
| Show basic code in Swift how to perform CMS AUTH, CMC I believe it is called? | |
| ``` | |
| AuthenticatedData ::= SEQUENCE { | |
| version CMSVersion, | |
| originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, | |
| recipientInfos RecipientInfos, | |
| macAlgorithm MessageAuthenticationCodeAlgorithm, | |
| digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL, | |
| encapContentInfo EncapsulatedContentInfo, | |
| authAttrs [2] IMPLICIT AuthAttributes OPTIONAL, | |
| mac MessageAuthenticationCode, | |
| unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL } | |
| CMSVersion ::= INTEGER -- usually 0 for this content type | |
| OriginatorInfo ::= SEQUENCE { | |
| certs [0] IMPLICIT CertificateSet OPTIONAL, | |
| crls [1] IMPLICIT CertificateRevocationLists OPTIONAL } | |
| RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo | |
| MessageAuthenticationCodeAlgorithm ::= AlgorithmIdentifier{MAC-ALGORITHM,...} | |
| DigestAlgorithmIdentifier ::= AlgorithmIdentifier{DIGEST-ALGORITHM,...} | |
| EncapsulatedContentInfo ::= SEQUENCE { | |
| eContentType ContentType, | |
| eContent [0] EXPLICIT OCTET STRING OPTIONAL } | |
| AuthAttributes ::= SET SIZE (1..MAX) OF Attribute | |
| -- MUST be DER-encoded if present | |
| -- MUST include content-type + message-digest if eContentType ≠ id-data | |
| MessageAuthenticationCode ::= OCTET STRING | |
| UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute | |
| ``` | |
| ## Option 0 | |
| ``` | |
| import SwiftASN1 | |
| import Crypto | |
| import X509 // swift-certificates | |
| struct AuthenticatedData: DERParseable, DERSerializable { | |
| var version: Int | |
| var originatorInfo: OriginatorInfo? | |
| var recipientInfos: [RecipientInfo] | |
| var macAlgorithm: AlgorithmIdentifier | |
| var digestAlgorithm: AlgorithmIdentifier? | |
| var encapContentInfo: EncapsulatedContentInfo | |
| var authAttrs: [CMSAttribute]? | |
| var mac: Data // OCTET STRING | |
| var unauthAttrs: [CMSAttribute]? | |
| // Implement parse(from:) and serialize(into:) according to ASN.1 above | |
| // ... | |
| } | |
| func createSimpleAuthenticatedData( | |
| content: Data, | |
| contentType: ObjectIdentifier, // usually .data | |
| macKey: SymmetricKey, // e.g. HMAC key | |
| recipients: [RecipientInfoBuilder] // key transport / agreement / etc. | |
| ) throws -> Data { | |
| let digestAlgo = AlgorithmIdentifier.algorithmIdentifier(.sha256) | |
| // 1. Compute MAC input (content + authAttrs if present) | |
| let macAlgo = AlgorithmIdentifier.algorithmIdentifier(.hmacWithSHA256) | |
| // Minimal authAttrs: content-type + message-digest | |
| let attrs = try buildRequiredAuthAttrs(contentType: contentType, content: content) | |
| let attrsDER = try DER.Serializer.serialized { $0.serializeSequence { /* attrs */ } } | |
| let macInput = content + attrsDER // simplified – real is more involved | |
| let mac = HMAC<SHA256>.authenticationCode(for: macInput, using: macKey) | |
| // 2. Build recipientInfos (encrypt macKey to each recipient) | |
| let recipientInfos = try recipients.map { try $0.build(for: macKey) } | |
| let authData = AuthenticatedData( | |
| version: 0, | |
| originatorInfo: nil, | |
| recipientInfos: recipientInfos, | |
| macAlgorithm: macAlgo, | |
| digestAlgorithm: digestAlgo, | |
| encapContentInfo: EncapsulatedContentInfo(eContentType: contentType, eContent: content), | |
| authAttrs: attrs, | |
| mac: Data(mac), | |
| unauthAttrs: nil | |
| ) | |
| let contentInfo = ContentInfo.authenticatedData(authData) | |
| return try DER.Serializer.serialized { try contentInfo.serialize(into: &$0) } | |
| } | |
| ``` | |
| ## Option 1 – Modern pure-Swift approach (recommended 2025–2026)Use these Swift-native libraries together: | |
| * swift-asn1 (from Apple or pointfreeco/swift-asn1) — for DER encoding | |
| * swift-crypto — for hashing & signing | |
| * swift-certificates — for X.509 certificate handling | |
| Basic skeleton for creating a CMS SignedData (very simplified / incomplete — real implementation needs full SignedData → SignerInfo → SignedAttributes → digest + signature): | |
| ```swift | |
| import Crypto | |
| import X509 // from swift-certificates | |
| import SwiftASN1 | |
| func createSimpleCMSSignedData( | |
| content: Data, | |
| signerCert: X509.Certificate, | |
| signerKey: P384.Signing.PrivateKey // or P256 / RSA etc. | |
| ) throws -> Data { | |
| // 1. Compute content digest (usually sha256) | |
| let digest = SHA256.hash(data: content) | |
| let digestBytes = Array(digest) | |
| // 2. Prepare signed attributes (minimal set) | |
| // You MUST include contentType + messageDigest + signingTime in real code | |
| let signedAttrs: [CMSAttribute] = [ | |
| // contentType(1.2.840.113549.1.7.1) -- Data | |
| // message-digest = digestBytes | |
| // signing-time = now | |
| ] | |
| let signedAttrsDER = try DER.Serializer.serialized { coder in | |
| try coder.serializeSequence { | |
| for attr in signedAttrs { try attr.serialize(into: &coder) } | |
| } | |
| } | |
| // 3. Hash signed attributes (IMPLICIT SET → DER) | |
| let signedAttrsDigest = SHA256.hash(data: signedAttrsDER) | |
| // 4. Sign the digest of signed attributes | |
| let signature = try signerKey.signature(for: signedAttrsDigest) | |
| // 5. Build SignerInfo | |
| let signerInfo = CMSSignerInfo( | |
| version: .v1, | |
| sid: .issuerAndSerialNumber( | |
| issuer: signerCert.issuer, | |
| serialNumber: signerCert.serialNumber | |
| ), | |
| digestAlgorithm: .sha256, | |
| signedAttrs: signedAttrs, | |
| signatureAlgorithm: .ecdsaP384sha384, // match your key | |
| signature: signature.rawRepresentation | |
| ) | |
| // 6. Build SignedData | |
| let signedData = CMSSignedData( | |
| version: .v1, | |
| digestAlgorithms: [.sha256], | |
| encapContentInfo: .data(content), | |
| certificates: [signerCert], | |
| signerInfos: [signerInfo] | |
| ) | |
| // 7. Wrap in ContentInfo | |
| let contentInfo = CMSContentInfo.signedData(signedData) | |
| // 8. DER-encode everything | |
| let der = try DER.Serializer.serialized { | |
| try contentInfo.serialize(into: &$0) | |
| } | |
| return Data(der) | |
| } | |
| ``` | |
| ## Option 2 – If you really want Apple's high-level CMS API (macOS only) | |
| ``` | |
| // macOS only – does NOT compile on iOS | |
| import Security | |
| func createCMSSignedDataMacOS( | |
| data: Data, | |
| identity: SecIdentity | |
| ) throws -> Data { | |
| var encoder: CMSEncoder? | |
| var status = CMSEncoderCreate(&encoder) | |
| guard status == errSecSuccess, let encoder else { | |
| throw NSError(domain: NSOSStatusErrorDomain, code: Int(status)) | |
| } | |
| status = CMSEncoderSetDetachedContent(encoder, false) | |
| status = CMSEncoderAddSigners(encoder, identity) | |
| // Optional: add more options (timestamp, etc.) | |
| // CMSEncoderAddBinaryHeader(…) | |
| var encodedData: CFData? | |
| status = CMSEncoderCopyEncodedContent(encoder, data as CFData, &encodedData) | |
| guard status == errSecSuccess, let encoded = encodedData as? Data else { | |
| throw NSError(domain: NSOSStatusErrorDomain, code: Int(status)) | |
| } | |
| return encoded | |
| } | |
| ``` | |
| ## Option 3. Simple ECDSA | |
| ``` | |
| import Crypto | |
| func simpleDetachedSignature( | |
| data: Data, | |
| privateKey: P384.Signing.PrivateKey | |
| ) throws -> Data { | |
| let signature = try privateKey.signature(for: data) | |
| return signature.rawRepresentation // DER-encoded ECDSA signature | |
| } | |
| ``` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment