├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetDropDown.xml ├── deploymentTargetSelector.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── migrations.xml ├── misc.xml └── runConfigurations.xml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── main.zip │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── debug23.keystore │ ├── testkey.past │ └── testkey.pk8 │ ├── ic_launcher-playstore.png │ ├── java │ ├── com │ │ ├── aefyr │ │ │ └── pseudoapksigner │ │ │ │ ├── Base64.java │ │ │ │ ├── Constants.java │ │ │ │ ├── IOUtils.java │ │ │ │ ├── ManifestBuilder.java │ │ │ │ ├── PseudoApkSigner.java │ │ │ │ ├── SignatureFileGenerator.java │ │ │ │ ├── Utils.java │ │ │ │ └── ZipAlignZipOutputStream.java │ │ ├── android │ │ │ └── apksig │ │ │ │ ├── ApkSigner.java │ │ │ │ ├── ApkSignerEngine.java │ │ │ │ ├── ApkVerificationIssue.java │ │ │ │ ├── ApkVerifier.java │ │ │ │ ├── Constants.java │ │ │ │ ├── DefaultApkSignerEngine.java │ │ │ │ ├── Hints.java │ │ │ │ ├── SigningCertificateLineage.java │ │ │ │ ├── SourceStampVerifier.java │ │ │ │ ├── apk │ │ │ │ ├── ApkFormatException.java │ │ │ │ ├── ApkSigningBlockNotFoundException.java │ │ │ │ ├── ApkUtils.java │ │ │ │ ├── ApkUtilsLite.java │ │ │ │ ├── CodenameMinSdkVersionException.java │ │ │ │ └── MinSdkVersionException.java │ │ │ │ ├── internal │ │ │ │ ├── apk │ │ │ │ │ ├── AndroidBinXmlParser.java │ │ │ │ │ ├── ApkSigResult.java │ │ │ │ │ ├── ApkSignerInfo.java │ │ │ │ │ ├── ApkSigningBlockUtils.java │ │ │ │ │ ├── ApkSigningBlockUtilsLite.java │ │ │ │ │ ├── ApkSupportedSignature.java │ │ │ │ │ ├── ContentDigestAlgorithm.java │ │ │ │ │ ├── NoApkSupportedSignaturesException.java │ │ │ │ │ ├── SignatureAlgorithm.java │ │ │ │ │ ├── SignatureInfo.java │ │ │ │ │ ├── SignatureNotFoundException.java │ │ │ │ │ ├── stamp │ │ │ │ │ │ ├── SourceStampCertificateLineage.java │ │ │ │ │ │ ├── SourceStampConstants.java │ │ │ │ │ │ ├── SourceStampVerifier.java │ │ │ │ │ │ ├── V1SourceStampSigner.java │ │ │ │ │ │ ├── V1SourceStampVerifier.java │ │ │ │ │ │ ├── V2SourceStampSigner.java │ │ │ │ │ │ └── V2SourceStampVerifier.java │ │ │ │ │ ├── v1 │ │ │ │ │ │ ├── DigestAlgorithm.java │ │ │ │ │ │ ├── V1SchemeConstants.java │ │ │ │ │ │ ├── V1SchemeSigner.java │ │ │ │ │ │ └── V1SchemeVerifier.java │ │ │ │ │ ├── v2 │ │ │ │ │ │ ├── V2SchemeConstants.java │ │ │ │ │ │ ├── V2SchemeSigner.java │ │ │ │ │ │ └── V2SchemeVerifier.java │ │ │ │ │ ├── v3 │ │ │ │ │ │ ├── V3SchemeConstants.java │ │ │ │ │ │ ├── V3SchemeSigner.java │ │ │ │ │ │ ├── V3SchemeVerifier.java │ │ │ │ │ │ └── V3SigningCertificateLineage.java │ │ │ │ │ └── v4 │ │ │ │ │ │ ├── V4SchemeSigner.java │ │ │ │ │ │ ├── V4SchemeVerifier.java │ │ │ │ │ │ └── V4Signature.java │ │ │ │ ├── asn1 │ │ │ │ │ ├── Asn1BerParser.java │ │ │ │ │ ├── Asn1Class.java │ │ │ │ │ ├── Asn1DecodingException.java │ │ │ │ │ ├── Asn1DerEncoder.java │ │ │ │ │ ├── Asn1EncodingException.java │ │ │ │ │ ├── Asn1Field.java │ │ │ │ │ ├── Asn1OpaqueObject.java │ │ │ │ │ ├── Asn1TagClass.java │ │ │ │ │ ├── Asn1Tagging.java │ │ │ │ │ ├── Asn1Type.java │ │ │ │ │ └── ber │ │ │ │ │ │ ├── BerDataValue.java │ │ │ │ │ │ ├── BerDataValueFormatException.java │ │ │ │ │ │ ├── BerDataValueReader.java │ │ │ │ │ │ ├── BerEncoding.java │ │ │ │ │ │ ├── ByteBufferBerDataValueReader.java │ │ │ │ │ │ └── InputStreamBerDataValueReader.java │ │ │ │ ├── compat │ │ │ │ │ ├── ClassCompat.java │ │ │ │ │ ├── IntConsumerCompat.java │ │ │ │ │ ├── IntSupplierCompat.java │ │ │ │ │ ├── OptionalIntCompat.java │ │ │ │ │ └── SupplierCompat.java │ │ │ │ ├── jar │ │ │ │ │ ├── ManifestParser.java │ │ │ │ │ ├── ManifestWriter.java │ │ │ │ │ └── SignatureFileWriter.java │ │ │ │ ├── oid │ │ │ │ │ └── OidConstants.java │ │ │ │ ├── pkcs7 │ │ │ │ │ ├── AlgorithmIdentifier.java │ │ │ │ │ ├── Attribute.java │ │ │ │ │ ├── ContentInfo.java │ │ │ │ │ ├── EncapsulatedContentInfo.java │ │ │ │ │ ├── IssuerAndSerialNumber.java │ │ │ │ │ ├── Pkcs7Constants.java │ │ │ │ │ ├── Pkcs7DecodingException.java │ │ │ │ │ ├── SignedData.java │ │ │ │ │ ├── SignerIdentifier.java │ │ │ │ │ └── SignerInfo.java │ │ │ │ ├── util │ │ │ │ │ ├── AndroidSdkVersion.java │ │ │ │ │ ├── ByteArrayDataSink.java │ │ │ │ │ ├── ByteBufferDataSource.java │ │ │ │ │ ├── ByteBufferSink.java │ │ │ │ │ ├── ByteBufferUtils.java │ │ │ │ │ ├── ByteStreams.java │ │ │ │ │ ├── ChainedDataSource.java │ │ │ │ │ ├── DelegatingX509Certificate.java │ │ │ │ │ ├── FileChannelDataSource.java │ │ │ │ │ ├── GuaranteedEncodedFormX509Certificate.java │ │ │ │ │ ├── InclusiveIntRange.java │ │ │ │ │ ├── MessageDigestSink.java │ │ │ │ │ ├── OutputStreamDataSink.java │ │ │ │ │ ├── Pair.java │ │ │ │ │ ├── RandomAccessFileDataSink.java │ │ │ │ │ ├── TeeDataSink.java │ │ │ │ │ ├── VerityTreeBuilder.java │ │ │ │ │ └── X509CertificateUtils.java │ │ │ │ ├── x509 │ │ │ │ │ ├── AttributeTypeAndValue.java │ │ │ │ │ ├── Certificate.java │ │ │ │ │ ├── Extension.java │ │ │ │ │ ├── Name.java │ │ │ │ │ ├── RSAPublicKey.java │ │ │ │ │ ├── RelativeDistinguishedName.java │ │ │ │ │ ├── SubjectPublicKeyInfo.java │ │ │ │ │ ├── TBSCertificate.java │ │ │ │ │ ├── Time.java │ │ │ │ │ └── Validity.java │ │ │ │ └── zip │ │ │ │ │ ├── CentralDirectoryRecord.java │ │ │ │ │ ├── EocdRecord.java │ │ │ │ │ ├── LocalFileRecord.java │ │ │ │ │ └── ZipUtils.java │ │ │ │ ├── util │ │ │ │ ├── DataSink.java │ │ │ │ ├── DataSinks.java │ │ │ │ ├── DataSource.java │ │ │ │ ├── DataSources.java │ │ │ │ ├── ReadableDataSink.java │ │ │ │ ├── RunnablesExecutor.java │ │ │ │ └── RunnablesProvider.java │ │ │ │ └── zip │ │ │ │ ├── ZipFormatException.java │ │ │ │ └── ZipSections.java │ │ ├── github │ │ │ ├── angads25 │ │ │ │ └── filepicker │ │ │ │ │ ├── controller │ │ │ │ │ ├── DialogSelectionListener.java │ │ │ │ │ ├── NotifyItemChecked.java │ │ │ │ │ └── adapters │ │ │ │ │ │ └── FileListAdapter.java │ │ │ │ │ ├── model │ │ │ │ │ ├── DialogConfigs.java │ │ │ │ │ ├── DialogProperties.java │ │ │ │ │ ├── FileListItem.java │ │ │ │ │ └── MarkedItemList.java │ │ │ │ │ ├── utils │ │ │ │ │ ├── ExtensionFilter.java │ │ │ │ │ └── Utility.java │ │ │ │ │ ├── view │ │ │ │ │ └── FilePickerDialog.java │ │ │ │ │ └── widget │ │ │ │ │ ├── MaterialCheckbox.java │ │ │ │ │ └── OnCheckedChangeListener.java │ │ │ └── paul035 │ │ │ │ └── LocaleHelper.java │ │ └── starry │ │ │ ├── FileUtils.java │ │ │ └── FileUtilsWrapper.java │ ├── io │ │ └── github │ │ │ └── abdurazaaqmohammed │ │ │ └── apksigner │ │ │ ├── CustomArrayAdapter.java │ │ │ ├── LegacyUtils.java │ │ │ ├── MainActivity.java │ │ │ ├── MismatchedSplitsException.java │ │ │ └── SignWrapper.java │ └── yuku │ │ └── ambilwarna │ │ ├── AmbilWarnaDialog.java │ │ └── AmbilWarnaSquare.java │ └── res │ ├── anim │ ├── loading.xml │ ├── marked_item_animation.xml │ └── unmarked_item_animation.xml │ ├── drawable-hdpi │ ├── ambilwarna_arrow_down.png │ ├── ambilwarna_arrow_right.png │ ├── ambilwarna_cursor.png │ └── ambilwarna_target.png │ ├── drawable-ldpi │ ├── ambilwarna_arrow_down.png │ ├── ambilwarna_arrow_right.png │ ├── ambilwarna_cursor.png │ └── ambilwarna_target.png │ ├── drawable-mdpi │ ├── ambilwarna_alphacheckered.png │ ├── ambilwarna_arrow_down.png │ ├── ambilwarna_arrow_right.png │ ├── ambilwarna_cursor.png │ ├── ambilwarna_hue.png │ └── ambilwarna_target.png │ ├── drawable-xhdpi │ ├── ambilwarna_arrow_down.png │ ├── ambilwarna_arrow_right.png │ ├── ambilwarna_cursor.png │ └── ambilwarna_target.png │ ├── drawable │ ├── ambilwarna_alphacheckered_tiled.xml │ ├── bottom_shadow.9.png │ ├── ic_launcher.xml │ ├── reload.xml │ └── settings.xml │ ├── layout-land │ └── ambilwarna_dialog.xml │ ├── layout-v11 │ └── dialog_footer.xml │ ├── layout-v14 │ └── dialog_settings.xml │ ├── layout-v21 │ └── dialog_file_list_item.xml │ ├── layout │ ├── activity_main.xml │ ├── ambilwarna_dialog.xml │ ├── dialog_file_list.xml │ ├── dialog_file_list_item.xml │ ├── dialog_footer.xml │ ├── dialog_header.xml │ ├── dialog_main.xml │ └── dialog_settings.xml │ ├── mipmap-hdpi │ ├── ic_directory_parent.png │ ├── ic_type_file.png │ └── ic_type_folder.png │ ├── mipmap-mdpi │ ├── ic_directory_parent.png │ ├── ic_type_file.png │ └── ic_type_folder.png │ ├── mipmap-xhdpi │ ├── ic_directory_parent.png │ ├── ic_type_file.png │ └── ic_type_folder.png │ ├── mipmap-xxhdpi │ ├── ic_directory_parent.png │ ├── ic_type_file.png │ └── ic_type_folder.png │ ├── values-es │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-land │ └── dimen.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-ru │ └── strings.xml │ ├── values-tr │ └── strings.xml │ ├── values-uk │ └── strings.xml │ ├── values-v21 │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ ├── values-xlarge-land │ └── dimen.xml │ └── values │ ├── attrs.xml │ ├── colors.xml │ ├── dimen.xml │ ├── dimens.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | /images 17 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | APK Signer -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # APK Signer 2 | Simple Android app to sign APKs, supports signing split APKs and multiple files. 3 | 4 | # Usage 5 | 6 | There are 3 ways to open the APK to sign: 7 | * Share the file and select Sign APK in the share menu 8 | * Press (open) the file and select Sign APK in available options 9 | * Open the app from launcher and press the button then select the APK file(s). 10 | 11 | Note: Some apps verify the signature of the APK or take other measures to check if the app was modified, which may cause it to crash on startup. 12 | 13 | # Used projects 14 | ⭐ [Android port of apksig library](https://github.com/MuntashirAkon/apksig-android) by [MuntashirAkon](https://github.com/MuntashirAkon) to sign APKs 15 | 16 | * Apache Commons Compress 17 | * PseudoApkSigner by Aefyr for backup signing on older Android versions 18 | * AmbilWarna Color Picker 19 | * android-filepicker by Angad Singh for file picker on older Android versions 20 | 21 | # Todo 22 | * Support v4 signature scheme 23 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | } 4 | 5 | android { 6 | namespace = "io.github.abdurazaaqmohammed.ApkSigner" 7 | compileSdk = 35 8 | 9 | defaultConfig { 10 | applicationId = "io.github.abdurazaaqmohammed.ApkSigner" 11 | minSdk = 1 12 | targetSdk = 35 13 | versionCode = 1 14 | versionName = "1.0" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | isMinifyEnabled = true 20 | isShrinkResources = true 21 | proguardFiles( 22 | getDefaultProguardFile("proguard-android-optimize.txt"), 23 | "proguard-rules.pro" 24 | ) 25 | } 26 | } 27 | compileOptions { 28 | sourceCompatibility = JavaVersion.VERSION_1_8 29 | targetCompatibility = JavaVersion.VERSION_1_8 30 | } 31 | buildFeatures { 32 | viewBinding = false 33 | } 34 | dependencies { 35 | implementation("org.apache.commons:commons-compress:1.24.0") 36 | } 37 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | -dontwarn org.xmlpull.v1.** 15 | -dontwarn org.kxml2.io.** 16 | -dontwarn android.content.res.** 17 | -dontwarn org.** 18 | -keep class * { 19 | *; 20 | } 21 | # Uncomment this to preserve the line number information for 22 | # debugging stack traces. 23 | #-keepattributes SourceFile,LineNumberTable 24 | 25 | # If you keep the line number information, uncomment this to 26 | # hide the original source file name. 27 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main.zip -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/assets/debug23.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/assets/debug23.keystore -------------------------------------------------------------------------------- /app/src/main/assets/testkey.past: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/assets/testkey.past -------------------------------------------------------------------------------- /app/src/main/assets/testkey.pk8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/assets/testkey.pk8 -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/aefyr/pseudoapksigner/Constants.java: -------------------------------------------------------------------------------- 1 | package com.aefyr.pseudoapksigner; 2 | 3 | public class Constants { 4 | static final String LINE_ENDING = "\r\n"; 5 | static final String GENERATOR_NAME = "Android Gradle 8.0.2"; 6 | public static final String UTF8 = "UTF-8"; 7 | public static final String UTF16 = "UTF-16LE"; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/aefyr/pseudoapksigner/IOUtils.java: -------------------------------------------------------------------------------- 1 | package com.aefyr.pseudoapksigner; 2 | 3 | 4 | import android.content.Context; 5 | 6 | import com.starry.FileUtils; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStream; 12 | 13 | public class IOUtils { 14 | 15 | public static void copyFileFromAssets(Context context, String assetFileName, File destination) throws IOException { 16 | try (InputStream inputStream = context.getAssets().open(assetFileName)) { 17 | OutputStream outputStream = FileUtils.getOutputStream(destination); 18 | byte[] buf = new byte[1024 * 1024]; 19 | int len; 20 | while ((len = inputStream.read(buf)) > 0) outputStream.write(buf, 0, len); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/aefyr/pseudoapksigner/ManifestBuilder.java: -------------------------------------------------------------------------------- 1 | package com.aefyr.pseudoapksigner; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedHashMap; 5 | import java.util.List; 6 | 7 | class ManifestBuilder { 8 | 9 | private ArrayList mEntries; 10 | 11 | private long mVersion = 0; 12 | private String mCachedManifest; 13 | private long mCachedVersion = -1; 14 | 15 | ManifestBuilder() { 16 | mEntries = new ArrayList<>(); 17 | } 18 | 19 | String build() { 20 | if (mVersion == mCachedVersion) 21 | return mCachedManifest; 22 | 23 | StringBuilder stringBuilder = new StringBuilder(); 24 | 25 | stringBuilder.append(generateHeader().toString()); 26 | for (ManifestEntry entry : mEntries) { 27 | stringBuilder.append(entry.toString()); 28 | } 29 | 30 | mCachedVersion = mVersion; 31 | mCachedManifest = stringBuilder.toString(); 32 | 33 | return mCachedManifest; 34 | } 35 | 36 | private ManifestEntry generateHeader() { 37 | ManifestEntry header = new ManifestEntry(); 38 | header.setAttribute("Manifest-Version", "1.0"); 39 | header.setAttribute("Created-By", Constants.GENERATOR_NAME); 40 | return header; 41 | } 42 | 43 | static class ManifestEntry { 44 | private LinkedHashMap mAttributes; 45 | 46 | ManifestEntry() { 47 | mAttributes = new LinkedHashMap<>(); 48 | } 49 | 50 | void setAttribute(String attribute, String value) { 51 | mAttributes.put(attribute, value); 52 | } 53 | 54 | String getAttribute(String attribute) { 55 | return mAttributes.get(attribute); 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | StringBuilder stringBuilder = new StringBuilder(); 61 | 62 | for (String key : mAttributes.keySet()) 63 | stringBuilder.append(String.format("%s: %s" + Constants.LINE_ENDING, key, mAttributes.get(key))); 64 | 65 | stringBuilder.append(Constants.LINE_ENDING); 66 | 67 | return stringBuilder.toString(); 68 | } 69 | } 70 | 71 | void addEntry(ManifestEntry entry) { 72 | mEntries.add(entry); 73 | mVersion++; 74 | } 75 | 76 | List getEntries() { 77 | return mEntries; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/aefyr/pseudoapksigner/SignatureFileGenerator.java: -------------------------------------------------------------------------------- 1 | package com.aefyr.pseudoapksigner; 2 | 3 | class SignatureFileGenerator { 4 | 5 | private ManifestBuilder mManifest; 6 | private String mHashingAlgorithm; 7 | 8 | SignatureFileGenerator(ManifestBuilder manifestBuilder, String hashingAlgorithm) { 9 | mManifest = manifestBuilder; 10 | mHashingAlgorithm = hashingAlgorithm; 11 | } 12 | 13 | String generate() throws Exception { 14 | StringBuilder stringBuilder = new StringBuilder(); 15 | stringBuilder.append(generateHeader().toString()); 16 | 17 | for (ManifestBuilder.ManifestEntry manifestEntry : mManifest.getEntries()) { 18 | ManifestBuilder.ManifestEntry sfEntry = new ManifestBuilder.ManifestEntry(); 19 | sfEntry.setAttribute("Name", manifestEntry.getAttribute("Name")); 20 | sfEntry.setAttribute(mHashingAlgorithm + "-Digest", Utils.base64Encode(Utils.hash(manifestEntry.toString().getBytes(Constants.UTF8), mHashingAlgorithm))); 21 | stringBuilder.append(sfEntry.toString()); 22 | } 23 | 24 | return stringBuilder.toString(); 25 | } 26 | 27 | private ManifestBuilder.ManifestEntry generateHeader() throws Exception { 28 | ManifestBuilder.ManifestEntry header = new ManifestBuilder.ManifestEntry(); 29 | header.setAttribute("Signature-Version", "1.0"); 30 | header.setAttribute("Created-By", Constants.GENERATOR_NAME); 31 | header.setAttribute(mHashingAlgorithm + "-Digest-Manifest", Utils.base64Encode(Utils.hash(mManifest.build().getBytes(Constants.UTF8), mHashingAlgorithm))); 32 | return header; 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/aefyr/pseudoapksigner/Utils.java: -------------------------------------------------------------------------------- 1 | package com.aefyr.pseudoapksigner; 2 | 3 | import com.starry.FileUtils; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.io.OutputStream; 9 | import java.security.KeyFactory; 10 | import java.security.MessageDigest; 11 | import java.security.PrivateKey; 12 | import java.security.Signature; 13 | import java.security.interfaces.RSAPrivateKey; 14 | import java.security.spec.PKCS8EncodedKeySpec; 15 | 16 | public class Utils { 17 | 18 | static byte[] hash(byte[] bytes, String hashingAlgorithm) throws Exception { 19 | MessageDigest messageDigest = MessageDigest.getInstance(hashingAlgorithm); 20 | messageDigest.update(bytes); 21 | return messageDigest.digest(); 22 | } 23 | 24 | static String base64Encode(byte[] bytes) { 25 | return Base64.encodeToString(bytes, 0); 26 | } 27 | 28 | static void copyStream(InputStream from, OutputStream to) throws IOException { 29 | byte[] buf = new byte[1024 * 1024]; 30 | int len; 31 | while ((len = from.read(buf)) > 0) { 32 | to.write(buf, 0, len); 33 | } 34 | } 35 | 36 | static byte[] sign(String hashingAlgorithm, PrivateKey privateKey, byte[] message) throws Exception { 37 | Signature sign = Signature.getInstance(hashingAlgorithm + "withRSA"); 38 | sign.initSign(privateKey); 39 | sign.update(message); 40 | return sign.sign(); 41 | } 42 | 43 | static RSAPrivateKey readPrivateKey(File file) throws Exception { 44 | PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(readFile(file)); 45 | return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(keySpec); 46 | } 47 | 48 | static byte[] readFile(File file) throws IOException { 49 | byte[] fileBytes = new byte[(int) file.length()]; 50 | 51 | try(InputStream inputStream = FileUtils.getInputStream(file)){ 52 | inputStream.read(fileBytes); 53 | } 54 | return fileBytes; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/aefyr/pseudoapksigner/ZipAlignZipOutputStream.java: -------------------------------------------------------------------------------- 1 | package com.aefyr.pseudoapksigner; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.util.zip.ZipEntry; 6 | import java.util.zip.ZipOutputStream; 7 | 8 | public class ZipAlignZipOutputStream extends ZipOutputStream { 9 | 10 | private BytesCounterOutputStream mBytesCounter; 11 | private int mAlignment; 12 | 13 | public static ZipAlignZipOutputStream create(OutputStream outputStream, int alignment) { 14 | BytesCounterOutputStream bytesCounterOutputStream = new BytesCounterOutputStream(outputStream); 15 | ZipAlignZipOutputStream zipAlignZipOutputStream = new ZipAlignZipOutputStream(bytesCounterOutputStream, alignment); 16 | zipAlignZipOutputStream.mBytesCounter = bytesCounterOutputStream; 17 | return zipAlignZipOutputStream; 18 | } 19 | 20 | private ZipAlignZipOutputStream(BytesCounterOutputStream outputStream, int alignment) { 21 | super(outputStream); 22 | mAlignment = alignment; 23 | } 24 | 25 | public void setAlignment(int alignment) { 26 | mAlignment = alignment; 27 | } 28 | 29 | public int getAlignment() { 30 | return mAlignment; 31 | } 32 | 33 | @Override 34 | public void putNextEntry(ZipEntry zipEntry) throws IOException { 35 | if (zipEntry.getMethod() == ZipEntry.STORED) { 36 | int headerSize = 30; 37 | headerSize += zipEntry.getName().getBytes().length; 38 | 39 | int requiredPadding = (int) (mAlignment - ((mBytesCounter.getBytesWritten() + headerSize) % mAlignment)); 40 | zipEntry.setExtra(new byte[requiredPadding]); 41 | } 42 | 43 | super.putNextEntry(zipEntry); 44 | } 45 | 46 | private static class BytesCounterOutputStream extends OutputStream { 47 | 48 | private OutputStream mWrappedOutputStream; 49 | private long mBytesWritten = 0; 50 | 51 | private BytesCounterOutputStream(OutputStream outputStream) { 52 | mWrappedOutputStream = outputStream; 53 | } 54 | 55 | @Override 56 | public void write(byte[] b) throws IOException { 57 | mWrappedOutputStream.write(b); 58 | mBytesWritten += b.length; 59 | } 60 | 61 | @Override 62 | public void write(int b) throws IOException { 63 | mWrappedOutputStream.write(b); 64 | mBytesWritten++; 65 | } 66 | 67 | @Override 68 | public void write(byte[] b, int off, int len) throws IOException { 69 | mWrappedOutputStream.write(b, off, len); 70 | mBytesWritten += len; 71 | } 72 | 73 | private long getBytesWritten() { 74 | return mBytesWritten; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig; 18 | 19 | import com.android.apksig.internal.apk.stamp.SourceStampConstants; 20 | import com.android.apksig.internal.apk.v1.V1SchemeConstants; 21 | import com.android.apksig.internal.apk.v2.V2SchemeConstants; 22 | import com.android.apksig.internal.apk.v3.V3SchemeConstants; 23 | 24 | /** 25 | * Exports internally defined constants to allow clients to reference these values without relying 26 | * on internal code. 27 | */ 28 | public class Constants { 29 | private Constants() {} 30 | 31 | public static final int VERSION_SOURCE_STAMP = 0; 32 | public static final int VERSION_JAR_SIGNATURE_SCHEME = 1; 33 | public static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; 34 | public static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; 35 | public static final int VERSION_APK_SIGNATURE_SCHEME_V31 = 31; 36 | public static final int VERSION_APK_SIGNATURE_SCHEME_V4 = 4; 37 | 38 | public static final String MANIFEST_ENTRY_NAME = V1SchemeConstants.MANIFEST_ENTRY_NAME; 39 | 40 | public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 41 | V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID; 42 | 43 | public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 44 | V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID; 45 | public static final int APK_SIGNATURE_SCHEME_V31_BLOCK_ID = 46 | V3SchemeConstants.APK_SIGNATURE_SCHEME_V31_BLOCK_ID; 47 | public static final int PROOF_OF_ROTATION_ATTR_ID = V3SchemeConstants.PROOF_OF_ROTATION_ATTR_ID; 48 | 49 | public static final int V1_SOURCE_STAMP_BLOCK_ID = 50 | SourceStampConstants.V1_SOURCE_STAMP_BLOCK_ID; 51 | public static final int V2_SOURCE_STAMP_BLOCK_ID = 52 | SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID; 53 | 54 | public static final String OID_RSA_ENCRYPTION = "1.2.840.113549.1.1.1"; 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/apk/ApkFormatException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.apk; 18 | 19 | /** 20 | * Indicates that an APK is not well-formed. For example, this may indicate that the APK is not a 21 | * well-formed ZIP archive, in which case {@link #getCause()} will return a 22 | * {@link com.android.apksig.zip.ZipFormatException ZipFormatException}, or that the APK contains 23 | * multiple ZIP entries with the same name. 24 | */ 25 | public class ApkFormatException extends Exception { 26 | private static final long serialVersionUID = 1L; 27 | 28 | public ApkFormatException(String message) { 29 | super(message); 30 | } 31 | 32 | public ApkFormatException(String message, Throwable cause) { 33 | super(message, cause); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/apk/ApkSigningBlockNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.apk; 18 | 19 | /** 20 | * Indicates that no APK Signing Block was found in an APK. 21 | */ 22 | public class ApkSigningBlockNotFoundException extends Exception { 23 | private static final long serialVersionUID = 1L; 24 | 25 | public ApkSigningBlockNotFoundException(String message) { 26 | super(message); 27 | } 28 | 29 | public ApkSigningBlockNotFoundException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/apk/CodenameMinSdkVersionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.apk; 18 | 19 | /** 20 | * Indicates that there was an issue determining the minimum Android platform version supported by 21 | * an APK because the version is specified as a codename, rather than as API Level number, and the 22 | * codename is in an unexpected format. 23 | */ 24 | public class CodenameMinSdkVersionException extends MinSdkVersionException { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | /** Encountered codename. */ 29 | private final String mCodename; 30 | 31 | /** 32 | * Constructs a new {@code MinSdkVersionCodenameException} with the provided message and 33 | * codename. 34 | */ 35 | public CodenameMinSdkVersionException(String message, String codename) { 36 | super(message); 37 | mCodename = codename; 38 | } 39 | 40 | /** 41 | * Returns the codename. 42 | */ 43 | public String getCodename() { 44 | return mCodename; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/apk/MinSdkVersionException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.apk; 18 | 19 | /** 20 | * Indicates that there was an issue determining the minimum Android platform version supported by 21 | * an APK. 22 | */ 23 | public class MinSdkVersionException extends ApkFormatException { 24 | 25 | private static final long serialVersionUID = 1L; 26 | 27 | /** 28 | * Constructs a new {@code MinSdkVersionException} with the provided message. 29 | */ 30 | public MinSdkVersionException(String message) { 31 | super(message); 32 | } 33 | 34 | /** 35 | * Constructs a new {@code MinSdkVersionException} with the provided message and cause. 36 | */ 37 | public MinSdkVersionException(String message, Throwable cause) { 38 | super(message, cause); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk; 18 | 19 | import com.android.apksig.ApkVerificationIssue; 20 | 21 | import java.security.cert.X509Certificate; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * Base implementation of an APK signer. 27 | */ 28 | public class ApkSignerInfo { 29 | public int index; 30 | public long timestamp; 31 | public List certs = new ArrayList<>(); 32 | public List certificateLineage = new ArrayList<>(); 33 | 34 | private final List mWarnings = new ArrayList<>(); 35 | private final List mErrors = new ArrayList<>(); 36 | 37 | /** 38 | * Adds a new {@link ApkVerificationIssue} as an error to this signer using the provided {@code 39 | * issueId} and {@code params}. 40 | */ 41 | public void addError(int issueId, Object... params) { 42 | mErrors.add(new ApkVerificationIssue(issueId, params)); 43 | } 44 | 45 | /** 46 | * Adds a new {@link ApkVerificationIssue} as a warning to this signer using the provided {@code 47 | * issueId} and {@code params}. 48 | */ 49 | public void addWarning(int issueId, Object... params) { 50 | mWarnings.add(new ApkVerificationIssue(issueId, params)); 51 | } 52 | 53 | /** 54 | * Returns {@code true} if any errors were encountered during verification for this signer. 55 | */ 56 | public boolean containsErrors() { 57 | return !mErrors.isEmpty(); 58 | } 59 | 60 | /** 61 | * Returns {@code true} if any warnings were encountered during verification for this signer. 62 | */ 63 | public boolean containsWarnings() { 64 | return !mWarnings.isEmpty(); 65 | } 66 | 67 | /** 68 | * Returns the errors encountered during verification for this signer. 69 | */ 70 | public List getErrors() { 71 | return mErrors; 72 | } 73 | 74 | /** 75 | * Returns the warnings encountered during verification for this signer. 76 | */ 77 | public List getWarnings() { 78 | return mWarnings; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/ApkSupportedSignature.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk; 18 | 19 | /** 20 | * Base implementation of a supported signature for an APK. 21 | */ 22 | public class ApkSupportedSignature { 23 | public final SignatureAlgorithm algorithm; 24 | public final byte[] signature; 25 | 26 | /** 27 | * Constructs a new supported signature using the provided {@code algorithm} and {@code 28 | * signature} bytes. 29 | */ 30 | public ApkSupportedSignature(SignatureAlgorithm algorithm, byte[] signature) { 31 | this.algorithm = algorithm; 32 | this.signature = signature; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/ContentDigestAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk; 18 | 19 | /** APK Signature Scheme v2 content digest algorithm. */ 20 | public enum ContentDigestAlgorithm { 21 | /** SHA2-256 over 1 MB chunks. */ 22 | CHUNKED_SHA256(1, "SHA-256", 256 / 8), 23 | 24 | /** SHA2-512 over 1 MB chunks. */ 25 | CHUNKED_SHA512(2, "SHA-512", 512 / 8), 26 | 27 | /** SHA2-256 over 4 KB chunks for APK verity. */ 28 | VERITY_CHUNKED_SHA256(3, "SHA-256", 256 / 8), 29 | 30 | /** Non-chunk SHA2-256. */ 31 | SHA256(4, "SHA-256", 256 / 8); 32 | 33 | private final int mId; 34 | private final String mJcaMessageDigestAlgorithm; 35 | private final int mChunkDigestOutputSizeBytes; 36 | 37 | private ContentDigestAlgorithm( 38 | int id, String jcaMessageDigestAlgorithm, int chunkDigestOutputSizeBytes) { 39 | mId = id; 40 | mJcaMessageDigestAlgorithm = jcaMessageDigestAlgorithm; 41 | mChunkDigestOutputSizeBytes = chunkDigestOutputSizeBytes; 42 | } 43 | 44 | /** Returns the ID of the digest algorithm used on the APK. */ 45 | public int getId() { 46 | return mId; 47 | } 48 | 49 | /** 50 | * Returns the {@link java.security.MessageDigest} algorithm used for computing digests of 51 | * chunks by this content digest algorithm. 52 | */ 53 | String getJcaMessageDigestAlgorithm() { 54 | return mJcaMessageDigestAlgorithm; 55 | } 56 | 57 | /** Returns the size (in bytes) of the digest of a chunk of content. */ 58 | int getChunkDigestOutputSizeBytes() { 59 | return mChunkDigestOutputSizeBytes; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/NoApkSupportedSignaturesException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk; 18 | 19 | /** 20 | * Base exception that is thrown when there are no signatures that support the full range of 21 | * requested platform versions. 22 | */ 23 | public class NoApkSupportedSignaturesException extends Exception { 24 | public NoApkSupportedSignaturesException(String message) { 25 | super(message); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/SignatureInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * APK Signature Scheme block and additional information relevant to verifying the signatures 23 | * contained in the block against the file. 24 | */ 25 | public class SignatureInfo { 26 | /** Contents of APK Signature Scheme block. */ 27 | public final ByteBuffer signatureBlock; 28 | 29 | /** Position of the APK Signing Block in the file. */ 30 | public final long apkSigningBlockOffset; 31 | 32 | /** Position of the ZIP Central Directory in the file. */ 33 | public final long centralDirOffset; 34 | 35 | /** Position of the ZIP End of Central Directory (EoCD) in the file. */ 36 | public final long eocdOffset; 37 | 38 | /** Contents of ZIP End of Central Directory (EoCD) of the file. */ 39 | public final ByteBuffer eocd; 40 | 41 | public SignatureInfo( 42 | ByteBuffer signatureBlock, 43 | long apkSigningBlockOffset, 44 | long centralDirOffset, 45 | long eocdOffset, 46 | ByteBuffer eocd) { 47 | this.signatureBlock = signatureBlock; 48 | this.apkSigningBlockOffset = apkSigningBlockOffset; 49 | this.centralDirOffset = centralDirOffset; 50 | this.eocdOffset = eocdOffset; 51 | this.eocd = eocd; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/SignatureNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk; 18 | 19 | /** 20 | * Base exception that is thrown when the APK is not signed with the requested signature scheme. 21 | */ 22 | public class SignatureNotFoundException extends Exception { 23 | public SignatureNotFoundException(String message) { 24 | super(message); 25 | } 26 | 27 | public SignatureNotFoundException(String message, Throwable cause) { 28 | super(message, cause); 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk.stamp; 18 | 19 | /** Constants used for source stamp signing and verification. */ 20 | public class SourceStampConstants { 21 | private SourceStampConstants() {} 22 | 23 | public static final int V1_SOURCE_STAMP_BLOCK_ID = 0x2b09189e; 24 | public static final int V2_SOURCE_STAMP_BLOCK_ID = 0x6dff800d; 25 | public static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256"; 26 | public static final int PROOF_OF_ROTATION_ATTR_ID = 0x9d6303f7; 27 | /** 28 | * The source stamp timestamp attribute value is an 8-byte little-endian encoded long 29 | * representing the epoch time in seconds when the stamp block was signed. The first 8 bytes 30 | * of the attribute value buffer will be used to read the timestamp, and any additional buffer 31 | * space will be ignored. 32 | */ 33 | public static final int STAMP_TIME_ATTR_ID = 0xe43c5946; 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/v1/DigestAlgorithm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk.v1; 18 | 19 | import java.util.Comparator; 20 | 21 | /** 22 | * Digest algorithm used with JAR signing (aka v1 signing scheme). 23 | */ 24 | public enum DigestAlgorithm { 25 | /** SHA-1 */ 26 | SHA1("SHA-1"), 27 | 28 | /** SHA2-256 */ 29 | SHA256("SHA-256"); 30 | 31 | private final String mJcaMessageDigestAlgorithm; 32 | 33 | private DigestAlgorithm(String jcaMessageDigestAlgoritm) { 34 | mJcaMessageDigestAlgorithm = jcaMessageDigestAlgoritm; 35 | } 36 | 37 | /** 38 | * Returns the {@link java.security.MessageDigest} algorithm represented by this digest 39 | * algorithm. 40 | */ 41 | String getJcaMessageDigestAlgorithm() { 42 | return mJcaMessageDigestAlgorithm; 43 | } 44 | 45 | public static Comparator BY_STRENGTH_COMPARATOR = new StrengthComparator(); 46 | 47 | private static class StrengthComparator implements Comparator { 48 | @Override 49 | public int compare(DigestAlgorithm a1, DigestAlgorithm a2) { 50 | switch (a1) { 51 | case SHA1: 52 | switch (a2) { 53 | case SHA1: 54 | return 0; 55 | case SHA256: 56 | return -1; 57 | } 58 | throw new RuntimeException("Unsupported algorithm: " + a2); 59 | 60 | case SHA256: 61 | switch (a2) { 62 | case SHA1: 63 | return 1; 64 | case SHA256: 65 | return 0; 66 | } 67 | throw new RuntimeException("Unsupported algorithm: " + a2); 68 | 69 | default: 70 | throw new RuntimeException("Unsupported algorithm: " + a1); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/v1/V1SchemeConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk.v1; 18 | 19 | /** Constants used by the Jar Signing / V1 Signature Scheme signing and verification. */ 20 | public class V1SchemeConstants { 21 | private V1SchemeConstants() {} 22 | 23 | public static final String MANIFEST_ENTRY_NAME = "META-INF/MANIFEST.MF"; 24 | public static final String SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR = 25 | "X-Android-APK-Signed"; 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk.v2; 18 | 19 | /** Constants used by the V2 Signature Scheme signing and verification. */ 20 | public class V2SchemeConstants { 21 | private V2SchemeConstants() {} 22 | 23 | public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; 24 | public static final int STRIPPING_PROTECTION_ATTR_ID = 0xbeeff00d; 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.apk.v3; 18 | 19 | import com.android.apksig.internal.util.AndroidSdkVersion; 20 | 21 | /** Constants used by the V3 Signature Scheme signing and verification. */ 22 | public class V3SchemeConstants { 23 | private V3SchemeConstants() {} 24 | 25 | public static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; 26 | public static final int APK_SIGNATURE_SCHEME_V31_BLOCK_ID = 0x1b93ad61; 27 | public static final int PROOF_OF_ROTATION_ATTR_ID = 0x3ba06f8c; 28 | 29 | public static final int MIN_SDK_WITH_V3_SUPPORT = AndroidSdkVersion.P; 30 | public static final int MIN_SDK_WITH_V31_SUPPORT = AndroidSdkVersion.T; 31 | /** 32 | * By default, APK signing key rotation will target T, but packages that have previously 33 | * rotated can continue rotating on pre-T by specifying an SDK version <= 32 as the 34 | * --rotation-min-sdk-version parameter when using apksigner or when invoking 35 | * {@link com.android.apksig.ApkSigner.Builder#setMinSdkVersionForRotation(int)}. 36 | */ 37 | public static final int DEFAULT_ROTATION_MIN_SDK_VERSION = AndroidSdkVersion.T; 38 | 39 | /** 40 | * This attribute is intended to be written to the V3.0 signer block as an additional attribute 41 | * whose value is the minimum SDK version supported for rotation by the V3.1 signing block. If 42 | * this value is set to X and a v3.1 signing block does not exist, or the minimum SDK version 43 | * for rotation in the v3.1 signing block is not X, then the APK should be rejected. 44 | */ 45 | public static final int ROTATION_MIN_SDK_VERSION_ATTR_ID = 0x559f8b02; 46 | 47 | /** 48 | * This attribute is written to the V3.1 signer block as an additional attribute to signify that 49 | * the rotation-min-sdk-version is targeting a development release. This is required to support 50 | * testing rotation on new development releases as the previous platform release SDK version 51 | * is used as the development release SDK version until the development release SDK is 52 | * finalized. 53 | */ 54 | public static final int ROTATION_ON_DEV_RELEASE_ATTR_ID = 0xc2a6b3ba; 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1Class.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Target({ElementType.TYPE}) 25 | @Retention(RetentionPolicy.RUNTIME) 26 | public @interface Asn1Class { 27 | public Asn1Type type(); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1DecodingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | /** 20 | * Indicates that input could not be decoded into intended ASN.1 structure. 21 | */ 22 | public class Asn1DecodingException extends Exception { 23 | private static final long serialVersionUID = 1L; 24 | 25 | public Asn1DecodingException(String message) { 26 | super(message); 27 | } 28 | 29 | public Asn1DecodingException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1EncodingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | /** 20 | * Indicates that an ASN.1 structure could not be encoded. 21 | */ 22 | public class Asn1EncodingException extends Exception { 23 | private static final long serialVersionUID = 1L; 24 | 25 | public Asn1EncodingException(String message) { 26 | super(message); 27 | } 28 | 29 | public Asn1EncodingException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1Field.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | @Target({ElementType.FIELD}) 25 | @Retention(RetentionPolicy.RUNTIME) 26 | public @interface Asn1Field { 27 | /** Index used to order fields in a container. Required for fields of SEQUENCE containers. */ 28 | public int index() default 0; 29 | 30 | public Asn1TagClass cls() default Asn1TagClass.AUTOMATIC; 31 | 32 | public Asn1Type type(); 33 | 34 | /** Tagging mode. Default: NORMAL. */ 35 | public Asn1Tagging tagging() default Asn1Tagging.NORMAL; 36 | 37 | /** Tag number. Required when IMPLICIT and EXPLICIT tagging mode is used.*/ 38 | public int tagNumber() default -1; 39 | 40 | /** {@code true} if this field is optional. Ignored for fields of CHOICE containers. */ 41 | public boolean optional() default false; 42 | 43 | /** Type of elements. Used only for SET_OF or SEQUENCE_OF. */ 44 | public Asn1Type elementType() default Asn1Type.ANY; 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1OpaqueObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * Opaque holder of encoded ASN.1 stuff. 23 | */ 24 | public class Asn1OpaqueObject { 25 | private final ByteBuffer mEncoded; 26 | 27 | public Asn1OpaqueObject(ByteBuffer encoded) { 28 | mEncoded = encoded.slice(); 29 | } 30 | 31 | public Asn1OpaqueObject(byte[] encoded) { 32 | mEncoded = ByteBuffer.wrap(encoded); 33 | } 34 | 35 | public ByteBuffer getEncoded() { 36 | return mEncoded.slice(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1TagClass.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | public enum Asn1TagClass { 20 | UNIVERSAL, 21 | APPLICATION, 22 | CONTEXT_SPECIFIC, 23 | PRIVATE, 24 | 25 | /** 26 | * Not really an actual tag class: decoder/encoder will attempt to deduce the correct tag class 27 | * automatically. 28 | */ 29 | AUTOMATIC, 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1Tagging.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | public enum Asn1Tagging { 20 | NORMAL, 21 | EXPLICIT, 22 | IMPLICIT, 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/Asn1Type.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1; 18 | 19 | public enum Asn1Type { 20 | ANY, 21 | CHOICE, 22 | INTEGER, 23 | OBJECT_IDENTIFIER, 24 | OCTET_STRING, 25 | SEQUENCE, 26 | SEQUENCE_OF, 27 | SET_OF, 28 | BIT_STRING, 29 | UTC_TIME, 30 | GENERALIZED_TIME, 31 | BOOLEAN, 32 | // This type can be used to annotate classes that encapsulate ASN.1 structures that are not 33 | // classified as a SEQUENCE or SET. 34 | UNENCODED_CONTAINER 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/ber/BerDataValueFormatException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1.ber; 18 | 19 | /** 20 | * Indicates that an ASN.1 data value being read could not be decoded using 21 | * Basic Encoding Rules (BER). 22 | */ 23 | public class BerDataValueFormatException extends Exception { 24 | 25 | private static final long serialVersionUID = 1L; 26 | 27 | public BerDataValueFormatException(String message) { 28 | super(message); 29 | } 30 | 31 | public BerDataValueFormatException(String message, Throwable cause) { 32 | super(message, cause); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/asn1/ber/BerDataValueReader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.asn1.ber; 18 | 19 | /** 20 | * Reader of ASN.1 Basic Encoding Rules (BER) data values. 21 | * 22 | *

BER data value reader returns data values, one by one, from a source. The interpretation of 23 | * data values (e.g., how to obtain a numeric value from an INTEGER data value, or how to extract 24 | * the elements of a SEQUENCE value) is left to clients of the reader. 25 | */ 26 | public interface BerDataValueReader { 27 | 28 | /** 29 | * Returns the next data value or {@code null} if end of input has been reached. 30 | * 31 | * @throws BerDataValueFormatException if the value being read is malformed. 32 | */ 33 | BerDataValue readDataValue() throws BerDataValueFormatException; 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/compat/ClassCompat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 Muntashir Al-Islam 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.compat; 18 | 19 | import java.lang.annotation.Annotation; 20 | import java.util.Objects; 21 | 22 | public class ClassCompat { 23 | public static A getDeclaredAnnotation(Class containerClass, 24 | Class annotationClass) { 25 | Objects.requireNonNull(annotationClass); 26 | Objects.requireNonNull(containerClass); 27 | // Loop over all directly-present annotations looking for a matching one 28 | for (Annotation annotation : containerClass.getDeclaredAnnotations()) { 29 | if (annotationClass.equals(annotation.annotationType())) { 30 | // More robust to do a dynamic cast at runtime instead 31 | // of compile-time only. 32 | return annotationClass.cast(annotation); 33 | } 34 | } 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/compat/IntConsumerCompat.java: -------------------------------------------------------------------------------- 1 | package com.android.apksig.internal.compat; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Represents an operation that accepts a single {@code int}-valued argument and 7 | * returns no result. This is the primitive type specialization of 8 | * {@link Consumer} for {@code int}. Unlike most other functional interfaces, 9 | * {@code IntConsumerCompat} is expected to operate via side-effects. 10 | * 11 | *

This is a functional interface 12 | * whose functional method is {@link #accept(int)}. 13 | * 14 | * @see Consumer 15 | */ 16 | @FunctionalInterface 17 | public interface IntConsumerCompat { 18 | 19 | /** 20 | * Performs this operation on the given argument. 21 | * 22 | * @param value the input argument 23 | */ 24 | void accept(int value); 25 | 26 | /** 27 | * Returns a composed {@code IntConsumerCompat} that performs, in sequence, this 28 | * operation followed by the {@code after} operation. If performing either 29 | * operation throws an exception, it is relayed to the caller of the 30 | * composed operation. If performing this operation throws an exception, 31 | * the {@code after} operation will not be performed. 32 | * 33 | * @param after the operation to perform after this operation 34 | * @return a composed {@code IntConsumerCompat} that performs in sequence this 35 | * operation followed by the {@code after} operation 36 | * @throws NullPointerException if {@code after} is null 37 | */ 38 | default IntConsumerCompat andThen(IntConsumerCompat after) { 39 | Objects.requireNonNull(after); 40 | return (int t) -> { accept(t); after.accept(t); }; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/compat/IntSupplierCompat.java: -------------------------------------------------------------------------------- 1 | package com.android.apksig.internal.compat; 2 | 3 | /** 4 | * Represents a supplier of {@code int}-valued results. This is the 5 | * {@code int}-producing primitive specialization of {@link SupplierCompat}. 6 | * 7 | *

There is no requirement that a distinct result be returned each 8 | * time the supplier is invoked. 9 | * 10 | * @see SupplierCompat 11 | */ 12 | @FunctionalInterface 13 | public interface IntSupplierCompat { 14 | 15 | /** 16 | * Gets a result. 17 | * 18 | * @return a result 19 | */ 20 | int getAsInt(); 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/compat/SupplierCompat.java: -------------------------------------------------------------------------------- 1 | package com.android.apksig.internal.compat; 2 | 3 | /** 4 | * Represents a supplier of results. 5 | * 6 | *

There is no requirement that a new or distinct result be returned each 7 | * time the supplier is invoked. 8 | * 9 | * @param the type of results supplied by this supplier 10 | */ 11 | @FunctionalInterface 12 | public interface SupplierCompat { 13 | 14 | /** 15 | * Gets a result. 16 | * 17 | * @return a result 18 | */ 19 | T get(); 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/jar/SignatureFileWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.jar; 18 | 19 | import java.io.IOException; 20 | import java.io.OutputStream; 21 | import java.util.SortedMap; 22 | import java.util.jar.Attributes; 23 | 24 | /** 25 | * Producer of JAR signature file ({@code *.SF}). 26 | * 27 | * @see JAR Manifest format 28 | */ 29 | public abstract class SignatureFileWriter { 30 | private SignatureFileWriter() {} 31 | 32 | public static void writeMainSection(OutputStream out, Attributes attributes) 33 | throws IOException { 34 | 35 | // Main section must start with the Signature-Version attribute. 36 | // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File. 37 | String signatureVersion = attributes.getValue(Attributes.Name.SIGNATURE_VERSION); 38 | if (signatureVersion == null) { 39 | throw new IllegalArgumentException( 40 | "Mandatory " + Attributes.Name.SIGNATURE_VERSION + " attribute missing"); 41 | } 42 | ManifestWriter.writeAttribute(out, Attributes.Name.SIGNATURE_VERSION, signatureVersion); 43 | 44 | if (attributes.size() > 1) { 45 | SortedMap namedAttributes = 46 | ManifestWriter.getAttributesSortedByName(attributes); 47 | namedAttributes.remove(Attributes.Name.SIGNATURE_VERSION.toString()); 48 | ManifestWriter.writeAttributes(out, namedAttributes); 49 | } 50 | writeSectionDelimiter(out); 51 | } 52 | 53 | public static void writeIndividualSection(OutputStream out, String name, Attributes attributes) 54 | throws IOException { 55 | ManifestWriter.writeIndividualSection(out, name, attributes); 56 | } 57 | 58 | public static void writeSectionDelimiter(OutputStream out) throws IOException { 59 | ManifestWriter.writeSectionDelimiter(out); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/Attribute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1OpaqueObject; 22 | import com.android.apksig.internal.asn1.Asn1Type; 23 | import java.util.List; 24 | 25 | /** 26 | * PKCS #7 {@code Attribute} as specified in RFC 5652. 27 | */ 28 | @Asn1Class(type = Asn1Type.SEQUENCE) 29 | public class Attribute { 30 | 31 | @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) 32 | public String attrType; 33 | 34 | @Asn1Field(index = 1, type = Asn1Type.SET_OF) 35 | public List attrValues; 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/ContentInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1OpaqueObject; 22 | import com.android.apksig.internal.asn1.Asn1Type; 23 | import com.android.apksig.internal.asn1.Asn1Tagging; 24 | 25 | /** 26 | * PKCS #7 {@code ContentInfo} as specified in RFC 5652. 27 | */ 28 | @Asn1Class(type = Asn1Type.SEQUENCE) 29 | public class ContentInfo { 30 | 31 | @Asn1Field(index = 1, type = Asn1Type.OBJECT_IDENTIFIER) 32 | public String contentType; 33 | 34 | @Asn1Field(index = 2, type = Asn1Type.ANY, tagging = Asn1Tagging.EXPLICIT, tagNumber = 0) 35 | public Asn1OpaqueObject content; 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/EncapsulatedContentInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | import com.android.apksig.internal.asn1.Asn1Tagging; 23 | import java.nio.ByteBuffer; 24 | 25 | /** 26 | * PKCS #7 {@code EncapsulatedContentInfo} as specified in RFC 5652. 27 | */ 28 | @Asn1Class(type = Asn1Type.SEQUENCE) 29 | public class EncapsulatedContentInfo { 30 | 31 | @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) 32 | public String contentType; 33 | 34 | @Asn1Field( 35 | index = 1, 36 | type = Asn1Type.OCTET_STRING, 37 | tagging = Asn1Tagging.EXPLICIT, tagNumber = 0, 38 | optional = true) 39 | public ByteBuffer content; 40 | 41 | public EncapsulatedContentInfo() {} 42 | 43 | public EncapsulatedContentInfo(String contentTypeOid) { 44 | contentType = contentTypeOid; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/IssuerAndSerialNumber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1OpaqueObject; 22 | import com.android.apksig.internal.asn1.Asn1Type; 23 | import java.math.BigInteger; 24 | 25 | /** 26 | * PKCS #7 {@code IssuerAndSerialNumber} as specified in RFC 5652. 27 | */ 28 | @Asn1Class(type = Asn1Type.SEQUENCE) 29 | public class IssuerAndSerialNumber { 30 | 31 | @Asn1Field(index = 0, type = Asn1Type.ANY) 32 | public Asn1OpaqueObject issuer; 33 | 34 | @Asn1Field(index = 1, type = Asn1Type.INTEGER) 35 | public BigInteger certificateSerialNumber; 36 | 37 | public IssuerAndSerialNumber() {} 38 | 39 | public IssuerAndSerialNumber(Asn1OpaqueObject issuer, BigInteger certificateSerialNumber) { 40 | this.issuer = issuer; 41 | this.certificateSerialNumber = certificateSerialNumber; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/Pkcs7Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | /** 20 | * Assorted PKCS #7 constants from RFC 5652. 21 | */ 22 | public abstract class Pkcs7Constants { 23 | private Pkcs7Constants() {} 24 | 25 | public static final String OID_DATA = "1.2.840.113549.1.7.1"; 26 | public static final String OID_SIGNED_DATA = "1.2.840.113549.1.7.2"; 27 | public static final String OID_CONTENT_TYPE = "1.2.840.113549.1.9.3"; 28 | public static final String OID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4"; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/Pkcs7DecodingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | /** 20 | * Indicates that an error was encountered while decoding a PKCS #7 structure. 21 | */ 22 | public class Pkcs7DecodingException extends Exception { 23 | private static final long serialVersionUID = 1L; 24 | 25 | public Pkcs7DecodingException(String message) { 26 | super(message); 27 | } 28 | 29 | public Pkcs7DecodingException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/SignedData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1OpaqueObject; 22 | import com.android.apksig.internal.asn1.Asn1Type; 23 | import com.android.apksig.internal.asn1.Asn1Tagging; 24 | import java.nio.ByteBuffer; 25 | import java.util.List; 26 | 27 | /** 28 | * PKCS #7 {@code SignedData} as specified in RFC 5652. 29 | */ 30 | @Asn1Class(type = Asn1Type.SEQUENCE) 31 | public class SignedData { 32 | 33 | @Asn1Field(index = 0, type = Asn1Type.INTEGER) 34 | public int version; 35 | 36 | @Asn1Field(index = 1, type = Asn1Type.SET_OF) 37 | public List digestAlgorithms; 38 | 39 | @Asn1Field(index = 2, type = Asn1Type.SEQUENCE) 40 | public EncapsulatedContentInfo encapContentInfo; 41 | 42 | @Asn1Field( 43 | index = 3, 44 | type = Asn1Type.SET_OF, 45 | tagging = Asn1Tagging.IMPLICIT, tagNumber = 0, 46 | optional = true) 47 | public List certificates; 48 | 49 | @Asn1Field( 50 | index = 4, 51 | type = Asn1Type.SET_OF, 52 | tagging = Asn1Tagging.IMPLICIT, tagNumber = 1, 53 | optional = true) 54 | public List crls; 55 | 56 | @Asn1Field(index = 5, type = Asn1Type.SET_OF) 57 | public List signerInfos; 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/SignerIdentifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | import com.android.apksig.internal.asn1.Asn1Tagging; 23 | import java.nio.ByteBuffer; 24 | 25 | /** 26 | * PKCS #7 {@code SignerIdentifier} as specified in RFC 5652. 27 | */ 28 | @Asn1Class(type = Asn1Type.CHOICE) 29 | public class SignerIdentifier { 30 | 31 | @Asn1Field(type = Asn1Type.SEQUENCE) 32 | public IssuerAndSerialNumber issuerAndSerialNumber; 33 | 34 | @Asn1Field(type = Asn1Type.OCTET_STRING, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0) 35 | public ByteBuffer subjectKeyIdentifier; 36 | 37 | public SignerIdentifier() {} 38 | 39 | public SignerIdentifier(IssuerAndSerialNumber issuerAndSerialNumber) { 40 | this.issuerAndSerialNumber = issuerAndSerialNumber; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/pkcs7/SignerInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.pkcs7; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1OpaqueObject; 22 | import com.android.apksig.internal.asn1.Asn1Type; 23 | import com.android.apksig.internal.asn1.Asn1Tagging; 24 | import java.nio.ByteBuffer; 25 | import java.util.List; 26 | 27 | /** 28 | * PKCS #7 {@code SignerInfo} as specified in RFC 5652. 29 | */ 30 | @Asn1Class(type = Asn1Type.SEQUENCE) 31 | public class SignerInfo { 32 | 33 | @Asn1Field(index = 0, type = Asn1Type.INTEGER) 34 | public int version; 35 | 36 | @Asn1Field(index = 1, type = Asn1Type.CHOICE) 37 | public SignerIdentifier sid; 38 | 39 | @Asn1Field(index = 2, type = Asn1Type.SEQUENCE) 40 | public AlgorithmIdentifier digestAlgorithm; 41 | 42 | @Asn1Field( 43 | index = 3, 44 | type = Asn1Type.SET_OF, 45 | tagging = Asn1Tagging.IMPLICIT, tagNumber = 0, 46 | optional = true) 47 | public Asn1OpaqueObject signedAttrs; 48 | 49 | @Asn1Field(index = 4, type = Asn1Type.SEQUENCE) 50 | public AlgorithmIdentifier signatureAlgorithm; 51 | 52 | @Asn1Field(index = 5, type = Asn1Type.OCTET_STRING) 53 | public ByteBuffer signature; 54 | 55 | @Asn1Field( 56 | index = 6, 57 | type = Asn1Type.SET_OF, 58 | tagging = Asn1Tagging.IMPLICIT, tagNumber = 1, 59 | optional = true) 60 | public List unsignedAttrs; 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/AndroidSdkVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | /** 20 | * Android SDK version / API Level constants. 21 | */ 22 | public abstract class AndroidSdkVersion { 23 | 24 | /** Hidden constructor to prevent instantiation. */ 25 | private AndroidSdkVersion() {} 26 | 27 | /** Android 1.0 */ 28 | public static final int INITIAL_RELEASE = 1; 29 | 30 | /** Android 2.3. */ 31 | public static final int GINGERBREAD = 9; 32 | 33 | /** Android 3.0 */ 34 | public static final int HONEYCOMB = 11; 35 | 36 | /** Android 4.3. The revenge of the beans. */ 37 | public static final int JELLY_BEAN_MR2 = 18; 38 | 39 | /** Android 4.4. KitKat, another tasty treat. */ 40 | public static final int KITKAT = 19; 41 | 42 | /** Android 5.0. A flat one with beautiful shadows. But still tasty. */ 43 | public static final int LOLLIPOP = 21; 44 | 45 | /** Android 6.0. M is for Marshmallow! */ 46 | public static final int M = 23; 47 | 48 | /** Android 7.0. N is for Nougat. */ 49 | public static final int N = 24; 50 | 51 | /** Android O. */ 52 | public static final int O = 26; 53 | 54 | /** Android P. */ 55 | public static final int P = 28; 56 | 57 | /** Android Q. */ 58 | public static final int Q = 29; 59 | 60 | /** Android R. */ 61 | public static final int R = 30; 62 | 63 | /** Android S. */ 64 | public static final int S = 31; 65 | 66 | /** Android Sv2. */ 67 | public static final int Sv2 = 32; 68 | 69 | /** Android T. */ 70 | public static final int T = 33; 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/ByteBufferSink.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | import com.android.apksig.util.DataSink; 20 | import java.io.IOException; 21 | import java.nio.BufferOverflowException; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * Data sink which stores all received data into the associated {@link ByteBuffer}. 26 | */ 27 | public class ByteBufferSink implements DataSink { 28 | 29 | private final ByteBuffer mBuffer; 30 | 31 | public ByteBufferSink(ByteBuffer buffer) { 32 | mBuffer = buffer; 33 | } 34 | 35 | public ByteBuffer getBuffer() { 36 | return mBuffer; 37 | } 38 | 39 | @Override 40 | public void consume(byte[] buf, int offset, int length) throws IOException { 41 | try { 42 | mBuffer.put(buf, offset, length); 43 | } catch (BufferOverflowException e) { 44 | throw new RuntimeException( 45 | "Insufficient space in output buffer for " + length + " bytes", e); 46 | } 47 | } 48 | 49 | @Override 50 | public void consume(ByteBuffer buf) throws IOException { 51 | int length = buf.remaining(); 52 | try { 53 | mBuffer.put(buf); 54 | } catch (BufferOverflowException e) { 55 | throw new RuntimeException( 56 | "Insufficient space in output buffer for " + length + " bytes", e); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/ByteBufferUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | public final class ByteBufferUtils { 22 | private ByteBufferUtils() {} 23 | 24 | /** 25 | * Returns the remaining data of the provided buffer as a new byte array and advances the 26 | * position of the buffer to the buffer's limit. 27 | */ 28 | public static byte[] toByteArray(ByteBuffer buf) { 29 | byte[] result = new byte[buf.remaining()]; 30 | buf.get(result); 31 | return result; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/ByteStreams.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | 23 | /** 24 | * Utilities for byte arrays and I/O streams. 25 | */ 26 | public final class ByteStreams { 27 | private ByteStreams() {} 28 | 29 | /** 30 | * Returns the data remaining in the provided input stream as a byte array 31 | */ 32 | public static byte[] toByteArray(InputStream in) throws IOException { 33 | ByteArrayOutputStream result = new ByteArrayOutputStream(); 34 | byte[] buf = new byte[16384]; 35 | int chunkSize; 36 | while ((chunkSize = in.read(buf)) != -1) { 37 | result.write(buf, 0, chunkSize); 38 | } 39 | return result.toByteArray(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/GuaranteedEncodedFormX509Certificate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | import java.security.cert.CertificateEncodingException; 20 | import java.security.cert.X509Certificate; 21 | import java.util.Arrays; 22 | 23 | /** 24 | * {@link X509Certificate} whose {@link #getEncoded()} returns the data provided at construction 25 | * time. 26 | */ 27 | public class GuaranteedEncodedFormX509Certificate extends DelegatingX509Certificate { 28 | private static final long serialVersionUID = 1L; 29 | 30 | private final byte[] mEncodedForm; 31 | private int mHash = -1; 32 | 33 | public GuaranteedEncodedFormX509Certificate(X509Certificate wrapped, byte[] encodedForm) { 34 | super(wrapped); 35 | this.mEncodedForm = (encodedForm != null) ? encodedForm.clone() : null; 36 | } 37 | 38 | @Override 39 | public byte[] getEncoded() throws CertificateEncodingException { 40 | return (mEncodedForm != null) ? mEncodedForm.clone() : null; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) return true; 46 | if (!(o instanceof X509Certificate)) return false; 47 | 48 | try { 49 | byte[] a = this.getEncoded(); 50 | byte[] b = ((X509Certificate) o).getEncoded(); 51 | return Arrays.equals(a, b); 52 | } catch (CertificateEncodingException e) { 53 | return false; 54 | } 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | if (mHash == -1) { 60 | try { 61 | mHash = Arrays.hashCode(this.getEncoded()); 62 | } catch (CertificateEncodingException e) { 63 | mHash = 0; 64 | } 65 | } 66 | return mHash; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/InclusiveIntRange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collections; 21 | import java.util.List; 22 | 23 | /** 24 | * Inclusive interval of integers. 25 | */ 26 | public class InclusiveIntRange { 27 | private final int min; 28 | private final int max; 29 | 30 | private InclusiveIntRange(int min, int max) { 31 | this.min = min; 32 | this.max = max; 33 | } 34 | 35 | public int getMin() { 36 | return min; 37 | } 38 | 39 | public int getMax() { 40 | return max; 41 | } 42 | 43 | public static InclusiveIntRange fromTo(int min, int max) { 44 | return new InclusiveIntRange(min, max); 45 | } 46 | 47 | public static InclusiveIntRange from(int min) { 48 | return new InclusiveIntRange(min, Integer.MAX_VALUE); 49 | } 50 | 51 | public List getValuesNotIn( 52 | List sortedNonOverlappingRanges) { 53 | if (sortedNonOverlappingRanges.isEmpty()) { 54 | return Collections.singletonList(this); 55 | } 56 | 57 | int testValue = min; 58 | List result = null; 59 | for (InclusiveIntRange range : sortedNonOverlappingRanges) { 60 | int rangeMax = range.max; 61 | if (testValue > rangeMax) { 62 | continue; 63 | } 64 | int rangeMin = range.min; 65 | if (testValue < range.min) { 66 | if (result == null) { 67 | result = new ArrayList<>(); 68 | } 69 | result.add(fromTo(testValue, rangeMin - 1)); 70 | } 71 | if (rangeMax >= max) { 72 | return (result != null) ? result : Collections.emptyList(); 73 | } 74 | testValue = rangeMax + 1; 75 | } 76 | if (testValue <= max) { 77 | if (result == null) { 78 | result = new ArrayList<>(1); 79 | } 80 | result.add(fromTo(testValue, max)); 81 | } 82 | return (result != null) ? result : Collections.emptyList(); 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return "[" + min + ", " + ((max < Integer.MAX_VALUE) ? (max + "]") : "\u221e)"); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/MessageDigestSink.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.apksig.internal.util; 17 | 18 | import com.android.apksig.util.DataSink; 19 | import java.nio.ByteBuffer; 20 | import java.security.MessageDigest; 21 | 22 | /** 23 | * Data sink which feeds all received data into the associated {@link MessageDigest} instances. Each 24 | * {@code MessageDigest} instance receives the same data. 25 | */ 26 | public class MessageDigestSink implements DataSink { 27 | 28 | private final MessageDigest[] mMessageDigests; 29 | 30 | public MessageDigestSink(MessageDigest[] digests) { 31 | mMessageDigests = digests; 32 | } 33 | 34 | @Override 35 | public void consume(byte[] buf, int offset, int length) { 36 | for (MessageDigest md : mMessageDigests) { 37 | md.update(buf, offset, length); 38 | } 39 | } 40 | 41 | @Override 42 | public void consume(ByteBuffer buf) { 43 | int originalPosition = buf.position(); 44 | for (MessageDigest md : mMessageDigests) { 45 | // Reset the position back to the original because the previous iteration's 46 | // MessageDigest.update set the buffer's position to the buffer's limit. 47 | buf.position(originalPosition); 48 | md.update(buf); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/OutputStreamDataSink.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | import com.android.apksig.util.DataSink; 20 | import java.io.IOException; 21 | import java.io.OutputStream; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * {@link DataSink} which outputs received data into the associated {@link OutputStream}. 26 | */ 27 | public class OutputStreamDataSink implements DataSink { 28 | 29 | private static final int MAX_READ_CHUNK_SIZE = 65536; 30 | 31 | private final OutputStream mOut; 32 | 33 | /** 34 | * Constructs a new {@code OutputStreamDataSink} which outputs received data into the provided 35 | * {@link OutputStream}. 36 | */ 37 | public OutputStreamDataSink(OutputStream out) { 38 | if (out == null) { 39 | throw new NullPointerException("out == null"); 40 | } 41 | mOut = out; 42 | } 43 | 44 | /** 45 | * Returns {@link OutputStream} into which this data sink outputs received data. 46 | */ 47 | public OutputStream getOutputStream() { 48 | return mOut; 49 | } 50 | 51 | @Override 52 | public void consume(byte[] buf, int offset, int length) throws IOException { 53 | mOut.write(buf, offset, length); 54 | } 55 | 56 | @Override 57 | public void consume(ByteBuffer buf) throws IOException { 58 | if (!buf.hasRemaining()) { 59 | return; 60 | } 61 | 62 | if (buf.hasArray()) { 63 | mOut.write( 64 | buf.array(), 65 | buf.arrayOffset() + buf.position(), 66 | buf.remaining()); 67 | buf.position(buf.limit()); 68 | } else { 69 | byte[] tmp = new byte[Math.min(buf.remaining(), MAX_READ_CHUNK_SIZE)]; 70 | while (buf.hasRemaining()) { 71 | int chunkSize = Math.min(buf.remaining(), tmp.length); 72 | buf.get(tmp, 0, chunkSize); 73 | mOut.write(tmp, 0, chunkSize); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/Pair.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | /** 20 | * Pair of two elements. 21 | */ 22 | public final class Pair { 23 | private final A mFirst; 24 | private final B mSecond; 25 | 26 | private Pair(A first, B second) { 27 | mFirst = first; 28 | mSecond = second; 29 | } 30 | 31 | public static Pair of(A first, B second) { 32 | return new Pair(first, second); 33 | } 34 | 35 | public A getFirst() { 36 | return mFirst; 37 | } 38 | 39 | public B getSecond() { 40 | return mSecond; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | final int prime = 31; 46 | int result = 1; 47 | result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode()); 48 | result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode()); 49 | return result; 50 | } 51 | 52 | @Override 53 | public boolean equals(Object obj) { 54 | if (this == obj) { 55 | return true; 56 | } 57 | if (obj == null) { 58 | return false; 59 | } 60 | if (getClass() != obj.getClass()) { 61 | return false; 62 | } 63 | @SuppressWarnings("rawtypes") 64 | Pair other = (Pair) obj; 65 | if (mFirst == null) { 66 | if (other.mFirst != null) { 67 | return false; 68 | } 69 | } else if (!mFirst.equals(other.mFirst)) { 70 | return false; 71 | } 72 | if (mSecond == null) { 73 | if (other.mSecond != null) { 74 | return false; 75 | } 76 | } else if (!mSecond.equals(other.mSecond)) { 77 | return false; 78 | } 79 | return true; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/util/TeeDataSink.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.util; 18 | 19 | import com.android.apksig.util.DataSink; 20 | import java.io.IOException; 21 | import java.nio.ByteBuffer; 22 | 23 | /** 24 | * {@link DataSink} which copies provided input into each of the sinks provided to it. 25 | */ 26 | public class TeeDataSink implements DataSink { 27 | 28 | private final DataSink[] mSinks; 29 | 30 | public TeeDataSink(DataSink[] sinks) { 31 | mSinks = sinks; 32 | } 33 | 34 | @Override 35 | public void consume(byte[] buf, int offset, int length) throws IOException { 36 | for (DataSink sink : mSinks) { 37 | sink.consume(buf, offset, length); 38 | } 39 | } 40 | 41 | @Override 42 | public void consume(ByteBuffer buf) throws IOException { 43 | int originalPosition = buf.position(); 44 | for (int i = 0; i < mSinks.length; i++) { 45 | if (i > 0) { 46 | buf.position(originalPosition); 47 | } 48 | mSinks[i].consume(buf); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/AttributeTypeAndValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1OpaqueObject; 22 | import com.android.apksig.internal.asn1.Asn1Type; 23 | 24 | /** 25 | * {@code AttributeTypeAndValue} as specified in RFC 5280. 26 | */ 27 | @Asn1Class(type = Asn1Type.SEQUENCE) 28 | public class AttributeTypeAndValue { 29 | 30 | @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) 31 | public String attrType; 32 | 33 | @Asn1Field(index = 1, type = Asn1Type.ANY) 34 | public Asn1OpaqueObject attrValue; 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/Extension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | 23 | import java.nio.ByteBuffer; 24 | 25 | /** 26 | * X509 {@code Extension} as specified in RFC 5280. 27 | */ 28 | @Asn1Class(type = Asn1Type.SEQUENCE) 29 | public class Extension { 30 | @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) 31 | public String extensionID; 32 | 33 | @Asn1Field(index = 1, type = Asn1Type.BOOLEAN, optional = true) 34 | public boolean isCritial = false; 35 | 36 | @Asn1Field(index = 2, type = Asn1Type.OCTET_STRING) 37 | public ByteBuffer extensionValue; 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/Name.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * X501 {@code Name} as specified in RFC 5280. 27 | */ 28 | @Asn1Class(type = Asn1Type.CHOICE) 29 | public class Name { 30 | 31 | // This field is the RDNSequence specified in RFC 5280. 32 | @Asn1Field(index = 0, type = Asn1Type.SEQUENCE_OF) 33 | public List relativeDistinguishedNames; 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/RSAPublicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | 23 | import java.math.BigInteger; 24 | 25 | /** 26 | * {@code RSAPublicKey} as specified in RFC 3279. 27 | */ 28 | @Asn1Class(type = Asn1Type.SEQUENCE) 29 | public class RSAPublicKey { 30 | @Asn1Field(index = 0, type = Asn1Type.INTEGER) 31 | public BigInteger modulus; 32 | 33 | @Asn1Field(index = 1, type = Asn1Type.INTEGER) 34 | public BigInteger publicExponent; 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/RelativeDistinguishedName.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * {@code RelativeDistinguishedName} as specified in RFC 5280. 27 | */ 28 | @Asn1Class(type = Asn1Type.UNENCODED_CONTAINER) 29 | public class RelativeDistinguishedName { 30 | 31 | @Asn1Field(index = 0, type = Asn1Type.SET_OF) 32 | public List attributes; 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/SubjectPublicKeyInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; 23 | 24 | import java.nio.ByteBuffer; 25 | 26 | /** 27 | * {@code SubjectPublicKeyInfo} as specified in RFC 5280. 28 | */ 29 | @Asn1Class(type = Asn1Type.SEQUENCE) 30 | public class SubjectPublicKeyInfo { 31 | @Asn1Field(index = 0, type = Asn1Type.SEQUENCE) 32 | public AlgorithmIdentifier algorithmIdentifier; 33 | 34 | @Asn1Field(index = 1, type = Asn1Type.BIT_STRING) 35 | public ByteBuffer subjectPublicKey; 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/TBSCertificate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | import com.android.apksig.internal.asn1.Asn1Tagging; 23 | import com.android.apksig.internal.pkcs7.AlgorithmIdentifier; 24 | 25 | import java.math.BigInteger; 26 | import java.nio.ByteBuffer; 27 | import java.util.List; 28 | 29 | /** 30 | * To Be Signed Certificate as specified in RFC 5280. 31 | */ 32 | @Asn1Class(type = Asn1Type.SEQUENCE) 33 | public class TBSCertificate { 34 | 35 | @Asn1Field( 36 | index = 0, 37 | type = Asn1Type.INTEGER, 38 | tagging = Asn1Tagging.EXPLICIT, tagNumber = 0) 39 | public int version; 40 | 41 | @Asn1Field(index = 1, type = Asn1Type.INTEGER) 42 | public BigInteger serialNumber; 43 | 44 | @Asn1Field(index = 2, type = Asn1Type.SEQUENCE) 45 | public AlgorithmIdentifier signatureAlgorithm; 46 | 47 | @Asn1Field(index = 3, type = Asn1Type.CHOICE) 48 | public Name issuer; 49 | 50 | @Asn1Field(index = 4, type = Asn1Type.SEQUENCE) 51 | public Validity validity; 52 | 53 | @Asn1Field(index = 5, type = Asn1Type.CHOICE) 54 | public Name subject; 55 | 56 | @Asn1Field(index = 6, type = Asn1Type.SEQUENCE) 57 | public SubjectPublicKeyInfo subjectPublicKeyInfo; 58 | 59 | @Asn1Field(index = 7, 60 | type = Asn1Type.BIT_STRING, 61 | tagging = Asn1Tagging.IMPLICIT, 62 | optional = true, 63 | tagNumber = 1) 64 | public ByteBuffer issuerUniqueID; 65 | 66 | @Asn1Field(index = 8, 67 | type = Asn1Type.BIT_STRING, 68 | tagging = Asn1Tagging.IMPLICIT, 69 | optional = true, 70 | tagNumber = 2) 71 | public ByteBuffer subjectUniqueID; 72 | 73 | @Asn1Field(index = 9, 74 | type = Asn1Type.SEQUENCE_OF, 75 | tagging = Asn1Tagging.EXPLICIT, 76 | optional = true, 77 | tagNumber = 3) 78 | public List extensions; 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/Time.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | 23 | /** 24 | * {@code Time} as specified in RFC 5280. 25 | */ 26 | @Asn1Class(type = Asn1Type.CHOICE) 27 | public class Time { 28 | 29 | @Asn1Field(type = Asn1Type.UTC_TIME) 30 | public String utcTime; 31 | 32 | @Asn1Field(type = Asn1Type.GENERALIZED_TIME) 33 | public String generalizedTime; 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/x509/Validity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.x509; 18 | 19 | import com.android.apksig.internal.asn1.Asn1Class; 20 | import com.android.apksig.internal.asn1.Asn1Field; 21 | import com.android.apksig.internal.asn1.Asn1Type; 22 | 23 | /** 24 | * {@code Validity} as specified in RFC 5280. 25 | */ 26 | @Asn1Class(type = Asn1Type.SEQUENCE) 27 | public class Validity { 28 | 29 | @Asn1Field(index = 0, type = Asn1Type.CHOICE) 30 | public Time notBefore; 31 | 32 | @Asn1Field(index = 1, type = Asn1Type.CHOICE) 33 | public Time notAfter; 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/internal/zip/EocdRecord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.internal.zip; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.nio.ByteOrder; 21 | 22 | /** 23 | * ZIP End of Central Directory record. 24 | */ 25 | public class EocdRecord { 26 | private static final int CD_RECORD_COUNT_ON_DISK_OFFSET = 8; 27 | private static final int CD_RECORD_COUNT_TOTAL_OFFSET = 10; 28 | private static final int CD_SIZE_OFFSET = 12; 29 | private static final int CD_OFFSET_OFFSET = 16; 30 | 31 | public static ByteBuffer createWithModifiedCentralDirectoryInfo( 32 | ByteBuffer original, 33 | int centralDirectoryRecordCount, 34 | long centralDirectorySizeBytes, 35 | long centralDirectoryOffset) { 36 | ByteBuffer result = ByteBuffer.allocate(original.remaining()); 37 | result.order(ByteOrder.LITTLE_ENDIAN); 38 | result.put(original.slice()); 39 | result.flip(); 40 | ZipUtils.setUnsignedInt16( 41 | result, CD_RECORD_COUNT_ON_DISK_OFFSET, centralDirectoryRecordCount); 42 | ZipUtils.setUnsignedInt16( 43 | result, CD_RECORD_COUNT_TOTAL_OFFSET, centralDirectoryRecordCount); 44 | ZipUtils.setUnsignedInt32(result, CD_SIZE_OFFSET, centralDirectorySizeBytes); 45 | ZipUtils.setUnsignedInt32(result, CD_OFFSET_OFFSET, centralDirectoryOffset); 46 | return result; 47 | } 48 | 49 | public static ByteBuffer createWithPaddedComment(ByteBuffer original, int padding) { 50 | ByteBuffer result = ByteBuffer.allocate((int) original.remaining() + padding); 51 | result.order(ByteOrder.LITTLE_ENDIAN); 52 | result.put(original.slice()); 53 | result.rewind(); 54 | ZipUtils.updateZipEocdCommentLen(result); 55 | return result; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/util/DataSink.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.util; 18 | 19 | import java.io.IOException; 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * Consumer of input data which may be provided in one go or in chunks. 24 | */ 25 | public interface DataSink { 26 | 27 | /** 28 | * Consumes the provided chunk of data. 29 | * 30 | *

This data sink guarantees to not hold references to the provided buffer after this method 31 | * terminates. 32 | * 33 | * @throws IndexOutOfBoundsException if {@code offset} or {@code length} are negative, or if 34 | * {@code offset + length} is greater than {@code buf.length}. 35 | */ 36 | void consume(byte[] buf, int offset, int length) throws IOException; 37 | 38 | /** 39 | * Consumes all remaining data in the provided buffer and advances the buffer's position 40 | * to the buffer's limit. 41 | * 42 | *

This data sink guarantees to not hold references to the provided buffer after this method 43 | * terminates. 44 | */ 45 | void consume(ByteBuffer buf) throws IOException; 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/util/DataSinks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.util; 18 | 19 | import com.android.apksig.internal.util.ByteArrayDataSink; 20 | import com.android.apksig.internal.util.MessageDigestSink; 21 | import com.android.apksig.internal.util.OutputStreamDataSink; 22 | import com.android.apksig.internal.util.RandomAccessFileDataSink; 23 | import java.io.OutputStream; 24 | import java.io.RandomAccessFile; 25 | import java.security.MessageDigest; 26 | 27 | /** 28 | * Utility methods for working with {@link DataSink} abstraction. 29 | */ 30 | public abstract class DataSinks { 31 | private DataSinks() {} 32 | 33 | /** 34 | * Returns a {@link DataSink} which outputs received data into the provided 35 | * {@link OutputStream}. 36 | */ 37 | public static DataSink asDataSink(OutputStream out) { 38 | return new OutputStreamDataSink(out); 39 | } 40 | 41 | /** 42 | * Returns a {@link DataSink} which outputs received data into the provided file, sequentially, 43 | * starting at the beginning of the file. 44 | */ 45 | public static DataSink asDataSink(RandomAccessFile file) { 46 | return new RandomAccessFileDataSink(file); 47 | } 48 | 49 | /** 50 | * Returns a {@link DataSink} which forwards data into the provided {@link MessageDigest} 51 | * instances via their {@code update} method. Each {@code MessageDigest} instance receives the 52 | * same data. 53 | */ 54 | public static DataSink asDataSink(MessageDigest... digests) { 55 | return new MessageDigestSink(digests); 56 | } 57 | 58 | /** 59 | * Returns a new in-memory {@link DataSink} which exposes all data consumed so far via the 60 | * {@link DataSource} interface. 61 | */ 62 | public static ReadableDataSink newInMemoryDataSink() { 63 | return new ByteArrayDataSink(); 64 | } 65 | 66 | /** 67 | * Returns a new in-memory {@link DataSink} which exposes all data consumed so far via the 68 | * {@link DataSource} interface. 69 | * 70 | * @param initialCapacity initial capacity in bytes 71 | */ 72 | public static ReadableDataSink newInMemoryDataSink(int initialCapacity) { 73 | return new ByteArrayDataSink(initialCapacity); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/util/DataSources.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.util; 18 | 19 | import com.android.apksig.internal.util.ByteBufferDataSource; 20 | import com.android.apksig.internal.util.FileChannelDataSource; 21 | import java.io.RandomAccessFile; 22 | import java.nio.ByteBuffer; 23 | import java.nio.channels.FileChannel; 24 | 25 | /** 26 | * Utility methods for working with {@link DataSource} abstraction. 27 | */ 28 | public abstract class DataSources { 29 | private DataSources() {} 30 | 31 | /** 32 | * Returns a {@link DataSource} backed by the provided {@link ByteBuffer}. The data source 33 | * represents the data contained between the position and limit of the buffer. Changes to the 34 | * buffer's contents will be visible in the data source. 35 | */ 36 | public static DataSource asDataSource(ByteBuffer buffer) { 37 | if (buffer == null) { 38 | throw new NullPointerException(); 39 | } 40 | return new ByteBufferDataSource(buffer); 41 | } 42 | 43 | /** 44 | * Returns a {@link DataSource} backed by the provided {@link RandomAccessFile}. Changes to the 45 | * file, including changes to size of file, will be visible in the data source. 46 | */ 47 | public static DataSource asDataSource(RandomAccessFile file) { 48 | return asDataSource(file.getChannel()); 49 | } 50 | 51 | /** 52 | * Returns a {@link DataSource} backed by the provided region of the {@link RandomAccessFile}. 53 | * Changes to the file will be visible in the data source. 54 | */ 55 | public static DataSource asDataSource(RandomAccessFile file, long offset, long size) { 56 | return asDataSource(file.getChannel(), offset, size); 57 | } 58 | 59 | /** 60 | * Returns a {@link DataSource} backed by the provided {@link FileChannel}. Changes to the 61 | * file, including changes to size of file, will be visible in the data source. 62 | */ 63 | public static DataSource asDataSource(FileChannel channel) { 64 | if (channel == null) { 65 | throw new NullPointerException(); 66 | } 67 | return new FileChannelDataSource(channel); 68 | } 69 | 70 | /** 71 | * Returns a {@link DataSource} backed by the provided region of the {@link FileChannel}. 72 | * Changes to the file will be visible in the data source. 73 | */ 74 | public static DataSource asDataSource(FileChannel channel, long offset, long size) { 75 | if (channel == null) { 76 | throw new NullPointerException(); 77 | } 78 | return new FileChannelDataSource(channel, offset, size); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/util/ReadableDataSink.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.util; 18 | 19 | /** 20 | * {@link DataSink} which exposes all data consumed so far as a {@link DataSource}. This abstraction 21 | * offers append-only write access and random read access. 22 | */ 23 | public interface ReadableDataSink extends DataSink, DataSource { 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/util/RunnablesExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.util; 18 | 19 | import static java.util.concurrent.TimeUnit.MILLISECONDS; 20 | 21 | import android.annotation.TargetApi; 22 | import android.os.Build; 23 | 24 | import java.util.concurrent.ArrayBlockingQueue; 25 | import java.util.concurrent.ExecutorService; 26 | import java.util.concurrent.Phaser; 27 | import java.util.concurrent.ThreadPoolExecutor; 28 | 29 | public interface RunnablesExecutor { 30 | static final RunnablesExecutor SINGLE_THREADED = p -> p.createRunnable().run(); 31 | 32 | static final RunnablesExecutor MULTI_THREADED = new RunnablesExecutor() { 33 | private final int PARALLELISM = Math.min(32, Runtime.getRuntime().availableProcessors()); 34 | private final int QUEUE_SIZE = 4; 35 | 36 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 37 | @Override 38 | public void execute(RunnablesProvider provider) { 39 | final ExecutorService mExecutor = 40 | new ThreadPoolExecutor(PARALLELISM, PARALLELISM, 41 | 0L, MILLISECONDS, 42 | new ArrayBlockingQueue<>(QUEUE_SIZE), 43 | new ThreadPoolExecutor.CallerRunsPolicy()); 44 | 45 | //not used 46 | Phaser tasks = new Phaser(1); 47 | 48 | for (int i = 0; i < PARALLELISM; ++i) { 49 | Runnable task = () -> { 50 | Runnable r = provider.createRunnable(); 51 | r.run(); 52 | tasks.arriveAndDeregister(); 53 | }; 54 | tasks.register(); 55 | mExecutor.execute(task); 56 | } 57 | 58 | // Waiting for the tasks to complete. 59 | tasks.arriveAndAwaitAdvance(); 60 | 61 | mExecutor.shutdownNow(); 62 | } 63 | }; 64 | 65 | void execute(RunnablesProvider provider); 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/util/RunnablesProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.util; 18 | 19 | public interface RunnablesProvider { 20 | Runnable createRunnable(); 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/zip/ZipFormatException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.zip; 18 | 19 | /** 20 | * Indicates that a ZIP archive is not well-formed. 21 | */ 22 | public class ZipFormatException extends Exception { 23 | private static final long serialVersionUID = 1L; 24 | 25 | public ZipFormatException(String message) { 26 | super(message); 27 | } 28 | 29 | public ZipFormatException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/android/apksig/zip/ZipSections.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.apksig.zip; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * Base representation of an APK's zip sections containing the central directory's offset, the size 23 | * of the central directory in bytes, the number of records in the central directory, the offset 24 | * of the end of central directory, and a ByteBuffer containing the end of central directory 25 | * contents. 26 | */ 27 | public class ZipSections { 28 | private final long mCentralDirectoryOffset; 29 | private final long mCentralDirectorySizeBytes; 30 | private final int mCentralDirectoryRecordCount; 31 | private final long mEocdOffset; 32 | private final ByteBuffer mEocd; 33 | 34 | public ZipSections( 35 | long centralDirectoryOffset, 36 | long centralDirectorySizeBytes, 37 | int centralDirectoryRecordCount, 38 | long eocdOffset, 39 | ByteBuffer eocd) { 40 | mCentralDirectoryOffset = centralDirectoryOffset; 41 | mCentralDirectorySizeBytes = centralDirectorySizeBytes; 42 | mCentralDirectoryRecordCount = centralDirectoryRecordCount; 43 | mEocdOffset = eocdOffset; 44 | mEocd = eocd; 45 | } 46 | 47 | /** 48 | * Returns the start offset of the ZIP Central Directory. This value is taken from the 49 | * ZIP End of Central Directory record. 50 | */ 51 | public long getZipCentralDirectoryOffset() { 52 | return mCentralDirectoryOffset; 53 | } 54 | 55 | /** 56 | * Returns the size (in bytes) of the ZIP Central Directory. This value is taken from the 57 | * ZIP End of Central Directory record. 58 | */ 59 | public long getZipCentralDirectorySizeBytes() { 60 | return mCentralDirectorySizeBytes; 61 | } 62 | 63 | /** 64 | * Returns the number of records in the ZIP Central Directory. This value is taken from the 65 | * ZIP End of Central Directory record. 66 | */ 67 | public int getZipCentralDirectoryRecordCount() { 68 | return mCentralDirectoryRecordCount; 69 | } 70 | 71 | /** 72 | * Returns the start offset of the ZIP End of Central Directory record. The record extends 73 | * until the very end of the APK. 74 | */ 75 | public long getZipEndOfCentralDirectoryOffset() { 76 | return mEocdOffset; 77 | } 78 | 79 | /** 80 | * Returns the contents of the ZIP End of Central Directory. 81 | */ 82 | public ByteBuffer getZipEndOfCentralDirectory() { 83 | return mEocd; 84 | } 85 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/controller/DialogSelectionListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.controller; 18 | 19 | /*

20 | * Created by Angad Singh on 10-07-2016. 21 | *

22 | */ 23 | 24 | /** 25 | * Interface definition for a callback to be invoked 26 | * when dialog selects files. 27 | */ 28 | public interface DialogSelectionListener { 29 | 30 | /** 31 | * The method is called when files or directories are selected. 32 | * 33 | * @param files The array of String containing selected file paths. 34 | */ 35 | void onSelectedFilePaths(String[] files); 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/controller/NotifyItemChecked.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.controller; 18 | 19 | /**

20 | * Created by Angad Singh on 11-07-2016. 21 | *

22 | */ 23 | 24 | /** 25 | * Interface definition for a callback to be invoked 26 | * when a checkbox is checked. 27 | */ 28 | public interface NotifyItemChecked { 29 | 30 | /** 31 | * Called when a checkbox is checked. 32 | */ 33 | void notifyCheckBoxIsClicked(); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/model/DialogConfigs.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.model; 18 | 19 | /**

20 | * Created by Angad Singh on 11-07-2016. 21 | *

22 | */ 23 | 24 | /* Helper class for setting properties of Dialog. 25 | */ 26 | public abstract class DialogConfigs { 27 | /* SELECTION_MODES*/ 28 | 29 | /* SINGLE_MODE specifies that a single File/Directory has to be selected 30 | * from the list of Files/Directories. It is the default Selection Mode. 31 | */ 32 | public static final int SINGLE_MODE = 0; 33 | 34 | /* MULTI_MODE specifies that multiple Files/Directories has to be selected 35 | * from the list of Files/Directories. 36 | */ 37 | public static final int MULTI_MODE = 1; 38 | 39 | /* SELECTION_TYPES*/ 40 | 41 | /* FILE_SELECT specifies that from list of Files/Directories a File has to 42 | * be selected. It is the default Selection Type. 43 | */ 44 | public static final int FILE_SELECT = 0; 45 | 46 | /* DIR_SELECT specifies that from list of Files/Directories a Directory has to 47 | * be selected. 48 | */ 49 | public static final int DIR_SELECT = 1; 50 | 51 | /* FILE_AND_DIR_SELECT specifies that from list of Files/Directories both 52 | * can be selected. 53 | */ 54 | public static final int FILE_AND_DIR_SELECT = 2; 55 | 56 | /* PARENT_DIRECTORY*/ 57 | public static final String DIRECTORY_SEPERATOR = "/"; 58 | public static final String STORAGE_DIR = "mnt"; 59 | 60 | /* DEFAULT_DIR is the default mount point of the SDCARD. It is the default 61 | * mount point. 62 | */ 63 | public static final String DEFAULT_DIR = DIRECTORY_SEPERATOR + STORAGE_DIR; 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/model/DialogProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.model; 18 | 19 | import java.io.File; 20 | 21 | /**

22 | * Created by Angad Singh on 11-07-2016. 23 | *

24 | */ 25 | 26 | /* Descriptor class to define properties of the Dialog. Actions are performed upon 27 | * these Properties 28 | */ 29 | public class DialogProperties { 30 | /** Selection Mode defines whether a single of multiple Files/Directories 31 | * have to be selected. 32 | * 33 | * SINGLE_MODE and MULTI_MODE are the two selection modes, See DialogConfigs 34 | * for more info. Set to SINGLE_MODE as default value by constructor. 35 | */ 36 | public int selection_mode; 37 | 38 | /** Selection Type defines that whether a File/Directory or both of these has 39 | * to be selected. 40 | * 41 | * FILE_SELECT ,DIR_SELECT, FILE_AND_DIR_SELECT are the three selection modes, 42 | * See DialogConfigs for more info. Set to FILE_SELECT as default value by constructor. 43 | */ 44 | public int selection_type; 45 | 46 | /** The Parent/Root Directory. List of Files are populated from here. Can be set 47 | * to any readable directory. /sdcard is the default location. 48 | * 49 | * Eg. /sdcard 50 | * Eg. /mnt 51 | */ 52 | public File root; 53 | 54 | /** The Directory is used when Root Directory is not readable or accessible. / 55 | * sdcard is the default location. 56 | * 57 | * Eg. /sdcard 58 | * Eg. /mnt 59 | */ 60 | public File error_dir; 61 | 62 | /** The Directory can be used as an offset. It is the first directory that is 63 | * shown in dialog. Consider making it Root's sub-directory. 64 | * 65 | * Eg. Root: /sdcard 66 | * Eg. Offset: /sdcard/Music/Country 67 | * 68 | */ 69 | public File offset; 70 | 71 | /** An Array of String containing extensions, Files with only that will be shown. 72 | * Others will be ignored. Set to null as default value by constructor. 73 | * Eg. String ext={"jpg","jpeg","png","gif"}; 74 | */ 75 | public String[] extensions; 76 | 77 | public DialogProperties() { 78 | selection_mode = DialogConfigs.SINGLE_MODE; 79 | selection_type = DialogConfigs.FILE_SELECT; 80 | root = new File(DialogConfigs.DEFAULT_DIR); 81 | error_dir = new File(DialogConfigs.DEFAULT_DIR); 82 | offset = new File(DialogConfigs.DEFAULT_DIR); 83 | extensions = null; 84 | } 85 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/model/FileListItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.model; 18 | 19 | /*

20 | * Created by Angad Singh on 09-07-2016. 21 | *

22 | */ 23 | 24 | import java.util.Locale; 25 | 26 | /** 27 | * The model/container class holding file list data. 28 | */ 29 | public class FileListItem implements Comparable { 30 | private String filename,location; 31 | private boolean directory,marked; 32 | private long time; 33 | 34 | public String getFilename() { 35 | return filename; 36 | } 37 | 38 | public void setFilename(String filename) { 39 | this.filename = filename; 40 | } 41 | 42 | public String getLocation() { 43 | return location; 44 | } 45 | 46 | public void setLocation(String location) { 47 | this.location = location; 48 | } 49 | 50 | public boolean isDirectory() { 51 | return directory; 52 | } 53 | 54 | public void setDirectory(boolean directory) { 55 | this.directory = directory; 56 | } 57 | 58 | public long getTime() { 59 | return time; 60 | } 61 | 62 | public void setTime(long time) { 63 | this.time = time; 64 | } 65 | 66 | public boolean isMarked() { 67 | return marked; 68 | } 69 | 70 | public void setMarked(boolean marked) { 71 | this.marked = marked; 72 | } 73 | 74 | @Override 75 | public int compareTo(FileListItem fileListItem) { 76 | if(fileListItem.isDirectory()&&isDirectory()) 77 | { //If the comparison is between two directories, return the directory with 78 | //alphabetic order first. 79 | return filename.toLowerCase().compareTo(fileListItem.getFilename().toLowerCase(Locale.getDefault())); 80 | } 81 | else if(!fileListItem.isDirectory()&&!isDirectory()) 82 | { //If the comparison is not between two directories, return the file with 83 | //alphabetic order first. 84 | return filename.toLowerCase().compareTo(fileListItem.getFilename().toLowerCase(Locale.getDefault())); 85 | } 86 | else if(fileListItem.isDirectory()&&!isDirectory()) 87 | { //If the comparison is between a directory and a file, return the directory. 88 | return 1; 89 | } 90 | else 91 | { //Same as above but order of occurence is different. 92 | return -1; 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/model/MarkedItemList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.model; 18 | 19 | import java.util.HashMap; 20 | import java.util.Set; 21 | 22 | /**

23 | * Created by Angad Singh on 11-07-2016. 24 | *

25 | */ 26 | 27 | /* SingleTon containing pair of all the selected files. 28 | * Key: Directory/File path. 29 | * Value: FileListItem Object. 30 | */ 31 | public class MarkedItemList { 32 | private static HashMap ourInstance = new HashMap<>(); 33 | 34 | public static void addSelectedItem(FileListItem item) { 35 | ourInstance.put(item.getLocation(),item); 36 | } 37 | 38 | public static void removeSelectedItem(String key) { 39 | ourInstance.remove(key); 40 | } 41 | 42 | public static boolean hasItem(String key) { 43 | return ourInstance.containsKey(key); 44 | } 45 | 46 | public static void clearSelectionList() { 47 | ourInstance = new HashMap<>(); 48 | } 49 | 50 | public static void addSingleFile(FileListItem item) { 51 | ourInstance = new HashMap<>(); 52 | ourInstance.put(item.getLocation(),item); 53 | } 54 | 55 | public static String[] getSelectedPaths() { 56 | Set paths = ourInstance.keySet(); 57 | String[] fpaths =new String[paths.size()]; 58 | int i=0; 59 | for(String path:paths) 60 | { fpaths[i++]=path; 61 | } 62 | return fpaths; 63 | } 64 | 65 | public static int getFileCount() { 66 | return ourInstance.size(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/utils/ExtensionFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.utils; 18 | 19 | import com.github.angads25.filepicker.model.DialogConfigs; 20 | import com.github.angads25.filepicker.model.DialogProperties; 21 | 22 | import java.io.File; 23 | import java.io.FileFilter; 24 | import java.util.Locale; 25 | 26 | /**

27 | * Created by Angad Singh on 11-07-2016. 28 | *

29 | */ 30 | 31 | /* Class to filter the list of files. 32 | */ 33 | public class ExtensionFilter implements FileFilter { 34 | private final String[] validExtensions; 35 | private final DialogProperties properties; 36 | 37 | public ExtensionFilter(DialogProperties properties) { 38 | if(properties.extensions!=null) { 39 | this.validExtensions = properties.extensions; 40 | } 41 | else { 42 | this.validExtensions = new String[]{""}; 43 | } 44 | this.properties=properties; 45 | } 46 | 47 | /**Function to filter files based on defined rules. 48 | */ 49 | @Override 50 | public boolean accept(File file) { 51 | //All directories are added in the least that can be read by the Application 52 | if (file.isDirectory()&&file.canRead()) 53 | { return true; 54 | } 55 | else if(properties.selection_type==DialogConfigs.DIR_SELECT) 56 | { /* True for files, If the selection type is Directory type, ie. 57 | * Only directory has to be selected from the list, then all files are 58 | * ignored. 59 | */ 60 | return false; 61 | } 62 | else 63 | { /* Check whether name of the file ends with the extension. Added if it 64 | * does. 65 | */ 66 | String name = file.getName().toLowerCase(Locale.getDefault()); 67 | for (String ext : validExtensions) { 68 | if (name.endsWith(ext)) { 69 | return true; 70 | } 71 | } 72 | } 73 | return false; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/angads25/filepicker/widget/OnCheckedChangeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 Angad Singh 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.angads25.filepicker.widget; 18 | 19 | /** 20 | *

21 | * Created by Angad on 20-05-2017. 22 | *

23 | */ 24 | 25 | public interface OnCheckedChangeListener { 26 | void onCheckedChanged(MaterialCheckbox checkbox, boolean isChecked); 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/paul035/LocaleHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.paul035; 2 | import android.annotation.TargetApi; 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.content.res.Configuration; 6 | import android.content.res.Resources; 7 | import android.os.Build; 8 | import android.preference.PreferenceManager; 9 | 10 | import java.util.Locale; 11 | 12 | // https://www.geeksforgeeks.org/how-to-change-the-whole-app-language-in-android-programmatically/ 13 | public class LocaleHelper { 14 | private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language"; 15 | 16 | // Method to set the language at runtime 17 | public static Context setLocale(Context context, String language) { 18 | persist(context, language); 19 | 20 | // Updating the language for devices above Android Nougat 21 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 22 | return updateResources(context, language); 23 | } 24 | // For devices with lower versions of Android OS 25 | return updateResourcesLegacy(context, language); 26 | } 27 | 28 | private static void persist(Context context, String language) { 29 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); 30 | SharedPreferences.Editor editor = preferences.edit(); 31 | editor.putString(SELECTED_LANGUAGE, language); 32 | if(Build.VERSION.SDK_INT > 8) editor.apply(); 33 | else editor.commit(); 34 | } 35 | 36 | // Method to update the language of the application by creating 37 | // an object of the inbuilt Locale class and passing the language argument to it 38 | @TargetApi(Build.VERSION_CODES.N) 39 | private static Context updateResources(Context context, String language) { 40 | String[] codes = language.split("-"); 41 | Locale locale = codes.length > 1 ? new Locale(codes[0], codes[1]) : new Locale(language); 42 | Locale.setDefault(locale); 43 | 44 | Configuration configuration = context.getResources().getConfiguration(); 45 | configuration.setLocale(locale); 46 | configuration.setLayoutDirection(locale); 47 | 48 | return context.createConfigurationContext(configuration); 49 | } 50 | 51 | private static Context updateResourcesLegacy(Context context, String language) { 52 | String[] codes = language.split("-"); 53 | Locale locale = codes.length > 1 ? new Locale(codes[0], codes[1]) : new Locale(language); 54 | Locale.setDefault(locale); 55 | 56 | Resources resources = context.getResources(); 57 | Configuration configuration = resources.getConfiguration(); 58 | configuration.locale = locale; 59 | 60 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 61 | configuration.setLayoutDirection(locale); 62 | } 63 | 64 | resources.updateConfiguration(configuration, resources.getDisplayMetrics()); 65 | 66 | return context; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/abdurazaaqmohammed/apksigner/CustomArrayAdapter.java: -------------------------------------------------------------------------------- 1 | package io.github.abdurazaaqmohammed.apksigner; 2 | import android.content.Context; 3 | import android.text.Html; 4 | import android.text.TextUtils; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ArrayAdapter; 9 | import android.widget.TextView; 10 | 11 | import java.util.Objects; 12 | 13 | public class CustomArrayAdapter extends ArrayAdapter { 14 | private final Context context; 15 | private final String[] values; 16 | private final int textColor; 17 | private String highlight; 18 | 19 | public CustomArrayAdapter(Context context, String[] values, int textColor, String highlight) { 20 | super(context, android.R.layout.simple_list_item_multiple_choice, values); 21 | this.context = context; 22 | this.values = values; 23 | this.textColor = textColor; 24 | if(!TextUtils.isEmpty(highlight)) this.highlight = highlight; 25 | } 26 | 27 | @Override 28 | public View getView(int position, View convertView, ViewGroup parent) { 29 | if (convertView == null) { 30 | convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, false); 31 | } 32 | 33 | TextView textView = convertView.findViewById(android.R.id.text1); 34 | String curr = values[position]; 35 | textView.setText(Objects.equals(curr, highlight) ? Html.fromHtml("" + curr + "") : curr); 36 | textView.setTextColor(textColor); 37 | 38 | return convertView; 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/io/github/abdurazaaqmohammed/apksigner/LegacyUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.abdurazaaqmohammed.apksigner; 2 | 3 | import android.os.Build; 4 | 5 | public class LegacyUtils { 6 | 7 | public static final boolean supportsFileChannel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; 8 | public static final boolean supportsEditorApply = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; 9 | public static final boolean supportsWriteExternalStorage = Build.VERSION.SDK_INT < 30; 10 | public final static boolean doesNotSupportInbuiltAndroidFilePicker = Build.VERSION.SDK_INT < 19; 11 | public final static boolean supportsActionBar = Build.VERSION.SDK_INT > 10; 12 | public final static boolean canSetNotificationBarTransparent = Build.VERSION.SDK_INT > 20; 13 | public final static boolean supportsAsyncTask = Build.VERSION.SDK_INT > 2; 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/java/io/github/abdurazaaqmohammed/apksigner/MismatchedSplitsException.java: -------------------------------------------------------------------------------- 1 | package io.github.abdurazaaqmohammed.apksigner; 2 | 3 | public class MismatchedSplitsException extends Exception { 4 | public MismatchedSplitsException(String cancelled) { 5 | super(cancelled); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/io/github/abdurazaaqmohammed/apksigner/SignWrapper.java: -------------------------------------------------------------------------------- 1 | package io.github.abdurazaaqmohammed.apksigner; 2 | 3 | import com.android.apksig.ApkSigner; 4 | import com.starry.FileUtils; 5 | 6 | import java.io.File; 7 | import java.io.InputStream; 8 | import java.security.KeyStore; 9 | import java.security.cert.X509Certificate; 10 | import java.util.Collections; 11 | import java.util.Objects; 12 | 13 | public class SignWrapper { 14 | 15 | public void setKey(File key) { 16 | this.key = key; 17 | } 18 | 19 | File key; 20 | char[] pw; 21 | boolean v1; 22 | boolean v2; 23 | boolean v3; 24 | boolean v4; 25 | public SignWrapper(String signPath, String password, boolean v1Enabled, boolean v2Enabled, boolean v3Enabled, boolean v4Enabled) { 26 | this.key = new File(signPath); 27 | this.pw = password.toCharArray(); 28 | this.v1 = v1Enabled; 29 | this.v2 = v2Enabled; 30 | this.v3 = v3Enabled; 31 | this.v4 = v4Enabled; 32 | } 33 | 34 | public SignWrapper(String signPath, String password) { 35 | this(signPath, password, true, true, true, false); 36 | } 37 | 38 | public void signApk(File inputApk, File output, boolean v1, boolean v2, boolean v3, boolean v4) throws Exception { 39 | KeyStore keystore = null; 40 | String[] types = {"JKS", "PKCS12", "BKS"}; 41 | for (int i = 0; i < types.length; i++) { 42 | String type = types[i]; 43 | try (InputStream fis = FileUtils.getInputStream(key)) { 44 | keystore = KeyStore.getInstance(type); 45 | keystore.load(fis, pw); 46 | } catch (Exception e) { 47 | if(i == types.length - 1) throw (e); 48 | } 49 | } 50 | String alias = keystore.aliases().nextElement(); 51 | 52 | ApkSigner.Builder b = new ApkSigner.Builder(Collections.singletonList(new ApkSigner.SignerConfig.Builder("CERT", 53 | ((KeyStore.PrivateKeyEntry) keystore.getEntry(alias, new KeyStore.PasswordProtection(pw))).getPrivateKey(), 54 | Collections.singletonList((X509Certificate) keystore.getCertificate(alias))).build())) 55 | .setInputApk(inputApk) 56 | .setOutputApk(output) 57 | .setCreatedBy("Android Gradle 8.0.2") 58 | .setV1SigningEnabled(v1) 59 | .setV2SigningEnabled(v2) 60 | .setV3SigningEnabled(v3) 61 | .setV4SigningEnabled(v4); 62 | if(v4) { 63 | String fileName = inputApk.getName(); 64 | String formattedName = fileName.replaceFirst("\\.(xapk|aspk|apk[sm]|apk)", ".idsig"); 65 | int lastDotIndex; 66 | b.setV4SignatureOutputFile(new File(output.getParentFile(), Objects.equals(fileName, formattedName) ? (lastDotIndex = fileName.lastIndexOf('.')) == -1 ? 67 | fileName + "_signed" : fileName.substring(0, lastDotIndex) + "_signed." + fileName.substring(lastDotIndex + 1) : formattedName)); 68 | } 69 | b.build().sign(); 70 | } 71 | 72 | public void signApk(File inputApk, File output) throws Exception { 73 | signApk(inputApk, output, v1, v2, v3, v4); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/yuku/ambilwarna/AmbilWarnaSquare.java: -------------------------------------------------------------------------------- 1 | package yuku.ambilwarna; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.graphics.ComposeShader; 8 | import android.graphics.LinearGradient; 9 | import android.graphics.Paint; 10 | import android.graphics.PorterDuff; 11 | import android.graphics.Shader; 12 | import android.graphics.Shader.TileMode; 13 | import android.util.AttributeSet; 14 | import android.view.View; 15 | 16 | public class AmbilWarnaSquare extends View { 17 | Paint paint; 18 | Shader luar; 19 | final float[] color = { 1.f, 1.f, 1.f }; 20 | 21 | public AmbilWarnaSquare(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public AmbilWarnaSquare(Context context, AttributeSet attrs, int defStyle) { 26 | super(context, attrs, defStyle); 27 | } 28 | 29 | @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { 30 | super.onDraw(canvas); 31 | if (paint == null) { 32 | paint = new Paint(); 33 | luar = new LinearGradient(0.f, 0.f, 0.f, this.getMeasuredHeight(), 0xffffffff, 0xff000000, TileMode.CLAMP); 34 | } 35 | int rgb = Color.HSVToColor(color); 36 | Shader dalam = new LinearGradient(0.f, 0.f, this.getMeasuredWidth(), 0.f, 0xffffffff, rgb, TileMode.CLAMP); 37 | ComposeShader shader = new ComposeShader(luar, dalam, PorterDuff.Mode.MULTIPLY); 38 | paint.setShader(shader); 39 | canvas.drawRect(0.f, 0.f, this.getMeasuredWidth(), this.getMeasuredHeight(), paint); 40 | } 41 | 42 | void setHue(float hue) { 43 | color[0] = hue; 44 | invalidate(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/res/anim/loading.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/marked_item_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/unmarked_item_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ambilwarna_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-hdpi/ambilwarna_arrow_down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ambilwarna_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-hdpi/ambilwarna_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ambilwarna_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-hdpi/ambilwarna_cursor.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ambilwarna_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-hdpi/ambilwarna_target.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/ambilwarna_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-ldpi/ambilwarna_arrow_down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/ambilwarna_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-ldpi/ambilwarna_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/ambilwarna_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-ldpi/ambilwarna_cursor.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/ambilwarna_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-ldpi/ambilwarna_target.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ambilwarna_alphacheckered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-mdpi/ambilwarna_alphacheckered.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ambilwarna_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-mdpi/ambilwarna_arrow_down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ambilwarna_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-mdpi/ambilwarna_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ambilwarna_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-mdpi/ambilwarna_cursor.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ambilwarna_hue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-mdpi/ambilwarna_hue.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ambilwarna_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-mdpi/ambilwarna_target.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ambilwarna_arrow_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-xhdpi/ambilwarna_arrow_down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ambilwarna_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-xhdpi/ambilwarna_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ambilwarna_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-xhdpi/ambilwarna_cursor.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ambilwarna_target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable-xhdpi/ambilwarna_target.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ambilwarna_alphacheckered_tiled.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottom_shadow.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbdurazaaqMohammed/APKSigner/2c54aaf57b485b9231b4458482d80ef535b3a8fe/app/src/main/res/drawable/bottom_shadow.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/reload.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v11/dialog_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 |