private static byte[] GenerateApkSignatureSchemeV2Block( SignerConfig signerConfigs, IDictionary <ContentDigestAlgorithm, byte[]> contentDigests) { // FORMAT: // * length-prefixed sequence of length-prefixed signer blocks. var signerBlocks = new List <byte[]>(1); var signerNumber = 0; signerNumber++; byte[] signerBlock; try { signerBlock = GenerateSignerBlock(signerConfigs, contentDigests); } catch (CryptographicException e) { throw new CryptographicException("Signer #" + signerNumber + " failed", e); } signerBlocks.Add(signerBlock); return(EncodeAsSequenceOfLengthPrefixedElements( new[] { EncodeAsSequenceOfLengthPrefixedElements(signerBlocks), })); }
private static byte[] GenerateApkSigningBlock(SignerConfig signerConfigs, IDictionary <ContentDigestAlgorithm, byte[]> contentDigests) { var apkSignatureSchemeV2Block = GenerateApkSignatureSchemeV2Block(signerConfigs, contentDigests); return(GenerateApkSigningBlock(apkSignatureSchemeV2Block)); }
/// <summary> /// Returns the names of JAR entries which this signer will produce as part of v1 signature. /// </summary> /// <param name="signerConfig"></param> /// <param name="minSdkVersion"></param> /// <returns></returns> public static ISet <string> GetOutputEntryNames(SignerConfig signerConfig, int minSdkVersion) { var result = new HashSet <string>(); var signerName = signerConfig.Name; result.Add("META-INF/" + signerName + ".SF"); var publicKey = signerConfig.Certificate.PublicKey; var signatureBlockFileName = "META-INF/" + signerName + "."; switch (publicKey.Key) { case RSACryptoServiceProvider _: signatureBlockFileName += "RSA"; break; case DSACryptoServiceProvider _: signatureBlockFileName += "DSA"; break; } result.Add(signatureBlockFileName); result.Add(ManifestEntryName); return(result); }
/// <summary> /// Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of /// JAR entries which need to be added to the APK as part of the signature. /// </summary> /// <param name="signerConfig"></param> /// <param name="digestAlgorithm"></param> /// <param name="apkSigningSchemeIds"></param> /// <param name="manifest"></param> /// <returns></returns> public static List <Tuple <string, byte[]> > SignManifest(SignerConfig signerConfig, DigestAlgorithm digestAlgorithm, IList <int> apkSigningSchemeIds, OutputManifestFile manifest) { // For each signer output .SF and .(RSA|DSA|EC) file, then output MANIFEST.MF. var signatureJarEntries = new List <Tuple <string, byte[]> >(2 * 1 + 1); var sfBytes = GenerateSignatureFile(apkSigningSchemeIds, digestAlgorithm, manifest); var signerName = signerConfig.Name; var signatureBlock = GenerateSignatureBlock(signerConfig, sfBytes); signatureJarEntries.Add(Tuple.Create("META-INF/" + signerName + ".SF", sfBytes)); var publicKey = signerConfig.Certificate.PublicKey; var signatureBlockFileName = "META-INF/" + signerName + "."; if (publicKey.Key is RSACryptoServiceProvider) { signatureBlockFileName += "RSA"; } else if (publicKey.Key is DSACryptoServiceProvider) { signatureBlockFileName += "DSA"; } signatureJarEntries.Add( Tuple.Create(signatureBlockFileName, signatureBlock)); signatureJarEntries.Add(Tuple.Create(ManifestEntryName, manifest.Contents)); return(signatureJarEntries); }
/// <summary> /// Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of /// JAR entries which need to be added to the APK as part of the signature. /// </summary> /// <param name="signerConfig"></param> /// <param name="jarEntryDigestAlgorithm"></param> /// <param name="jarEntryDigests"></param> /// <param name="apkSigningSchemeIds"></param> /// <param name="sourceManifestBytes"></param> /// <returns></returns> public static List <Tuple <string, byte[]> > Sign(SignerConfig signerConfig, DigestAlgorithm jarEntryDigestAlgorithm, IDictionary <string, byte[]> jarEntryDigests, IList <int> apkSigningSchemeIds, byte[] sourceManifestBytes) { var manifest = GenerateManifestFile( jarEntryDigestAlgorithm, jarEntryDigests, sourceManifestBytes); return(SignManifest( signerConfig, jarEntryDigestAlgorithm, apkSigningSchemeIds, manifest)); }
/// <summary> /// Signs the provided APK using APK Signature Scheme v2 and returns the APK Signing Block containing the signature. /// </summary> /// <param name="beforeCentralDir"></param> /// <param name="centralDir"></param> /// <param name="eocd"></param> /// <param name="signerConfig"></param> /// <returns></returns> public static byte[] GenerateApkSigningBlock(Stream beforeCentralDir, Stream centralDir, Stream eocd, SignerConfig signerConfig) { // Figure out which digest(s) to use for APK contents. ISet <ContentDigestAlgorithm> contentDigestAlgorithms = new HashSet <ContentDigestAlgorithm>(); contentDigestAlgorithms.Add(signerConfig.SignatureAlgorithm.ContentDigestAlgorithm); // Ensure that, when digesting, ZIP End of Central Directory record's Central Directory // offset field is treated as pointing to the offset at which the APK Signing Block will // start. var centralDirOffsetForDigesting = beforeCentralDir.Length; var eocdBuf = new MemoryStream(new byte[(int)eocd.Length], true); eocd.CopyTo(eocdBuf); eocdBuf.Position = 0; ZipUtils.SetZipEocdCentralDirectoryOffset(eocdBuf, centralDirOffsetForDigesting); // Compute digests of APK contents. IDictionary <ContentDigestAlgorithm, byte[]> contentDigests; // digest algorithm ID -> digest try { contentDigests = ComputeContentDigests( contentDigestAlgorithms, new[] { beforeCentralDir, centralDir, eocdBuf }); } catch (IOException e) { throw new IOException("Failed to read APK being signed", e); } catch (CryptographicException e) { throw new CryptographicException("Failed to compute digests of APK", e); } // Sign the digests and wrap the signatures and signer info into an APK Signing Block. return(GenerateApkSigningBlock(signerConfig, contentDigests)); }
private static byte[] GenerateSignatureBlock(SignerConfig signerConfig, byte[] signatureFileBytes) { var signerCert = signerConfig.Certificate; var digestAlgorithm = signerConfig.SignatureDigestAlgorithm; var digestAlgorithmId = digestAlgorithm.Oid; var content = new ContentInfo(DataOid, signatureFileBytes); var signedCms = new SignedCms(content, true); var signer = new CmsSigner(signerCert) { DigestAlgorithm = digestAlgorithmId }; signedCms.ComputeSignature(signer); var encoded = signedCms.Encode(); return(encoded); }
private static byte[] GenerateSignerBlock(SignerConfig signerConfig, IDictionary <ContentDigestAlgorithm, byte[]> contentDigests) { var publicKey = signerConfig.Certificates.PublicKey; var encodedPublicKey = EncodePublicKey(publicKey); var signedData = new V2SignatureSchemeBlock.SignedData(); try { signedData.Certificate = EncodeCertificates(signerConfig.Certificates); } catch (CryptographicException e) { throw new CryptographicException("Failed to encode certificates", e); } var digests = new List <Tuple <int, byte[]> >(1); var contentDigestAlgorithm = signerConfig.SignatureAlgorithm.ContentDigestAlgorithm; var contentDigest = contentDigests[contentDigestAlgorithm]; if (contentDigest == null) { throw new Exception( contentDigestAlgorithm + " content digest for " + signerConfig.SignatureAlgorithm + " not computed"); } digests.Add(Tuple.Create(signerConfig.SignatureAlgorithm.Id, contentDigest)); signedData.Digests = digests; var signer = new V2SignatureSchemeBlock.Signer(); // FORMAT: // * length-prefixed sequence of length-prefixed digests: // * uint32: signature algorithm ID // * length-prefixed bytes: digest of contents // * length-prefixed sequence of certificates: // * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded). // * length-prefixed sequence of length-prefixed additional attributes: // * uint32: ID // * (length - 4) bytes: value signer.SignedData = EncodeAsSequenceOfLengthPrefixedElements(new[] { EncodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(signedData.Digests), EncodeAsSequenceOfLengthPrefixedElements(new[] { signedData.Certificate }), // additional attributes new byte[0], }); signer.PublicKey = encodedPublicKey; signer.Signatures = new List <Tuple <int, byte[]> >(1); var signatureAlgorithm = signerConfig.SignatureAlgorithm; { var digestAlgorithm = signatureAlgorithm.DigestAlgorithm; byte[] signatureBytes; var x509Key = new X509AsymmetricSecurityKey(signerConfig.Certificates); if (signerConfig.Certificates.PrivateKey is RSACryptoServiceProvider x) { if (digestAlgorithm.Oid == DigestAlgorithm.SHA1.Oid) { var rsa = (RSA)x509Key.GetAsymmetricAlgorithm(SecurityAlgorithms.RsaSha1Signature, true); signatureBytes = rsa.SignData(signer.SignedData, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); } else if (digestAlgorithm.Oid == DigestAlgorithm.SHA256.Oid) { var rsa = (RSA)x509Key.GetAsymmetricAlgorithm(SecurityAlgorithms.RsaSha256Signature, true); signatureBytes = rsa.SignData(signer.SignedData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } else if (digestAlgorithm.Oid == DigestAlgorithm.SHA512.Oid) { var rsa = (RSA)x509Key.GetAsymmetricAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", true); signatureBytes = rsa.SignData(signer.SignedData, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1); } else { throw new CryptographicException($"Failed to sign using {digestAlgorithm.Name} unsupproted digest"); } } else if (signerConfig.Certificates.PrivateKey is DSACryptoServiceProvider dsa) { signatureBytes = dsa.SignData(signer.SignedData); } else { throw new CryptographicException("Failed to sign using " + digestAlgorithm.Name); } switch (publicKey.Key) { case RSACryptoServiceProvider rsaPub: using (var rsa2 = new RSACryptoServiceProvider()) using (var hash = digestAlgorithm.CreateInstance()) { rsa2.ImportParameters(rsaPub.ExportParameters(false)); if (!rsa2.VerifyData(signer.SignedData, hash, signatureBytes)) { throw new CryptographicException("Signature did not verify"); } } break; case DSACryptoServiceProvider dsaPub: using (var dsa2 = new DSACryptoServiceProvider()) { dsa2.ImportParameters(dsaPub.ExportParameters(false)); if (!dsa2.VerifyData(signer.SignedData, signatureBytes)) { throw new CryptographicException("Signature did not verify"); } } break; default: throw new CryptographicException( "Failed to verify generated " + digestAlgorithm.Name + " signature using" + " public key from certificate"); } signer.Signatures.Add(Tuple.Create(signatureAlgorithm.Id, signatureBytes)); } // FORMAT: // * length-prefixed signed data // * length-prefixed sequence of length-prefixed signatures: // * uint32: signature algorithm ID // * length-prefixed bytes: signature of signed data // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded) return(EncodeAsSequenceOfLengthPrefixedElements( new[] { signer.SignedData, EncodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes( signer.Signatures), signer.PublicKey, })); }