Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use apksigner to sign and extract keys from apks instead of keytool #5

Open
wants to merge 4 commits into
base: uiautomator2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions ruby-gem/lib/calabash-android/dependencies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ def self.zipalign_path
android_dependencies(:zipalign_path)
end

def self.apksigner_path
android_dependencies(:apksigner_path)
end

def self.android_jar_path
android_dependencies(:android_jar_path)
end
Expand Down Expand Up @@ -229,6 +233,12 @@ def self.locate_android_dependencies(android_sdk_location)
aapt_path = scan_for_path(android_sdk_location, aapt_executable, tools_directories(android_sdk_location))
zipalign_path = scan_for_path(android_sdk_location, zipalign_executable, tools_directories(android_sdk_location))

if ENV['APKSIGNER_PATH']
apksigner_path = ENV['APKSIGNER_PATH']
else
apksigner_path = scan_for_path(android_sdk_location, apksigner_executable, tools_directories(android_sdk_location))
end

if adb_path.nil?
raise Environment::InvalidEnvironmentError,
"Could not find '#{adb_executable}' in '#{android_sdk_location}'"
Expand All @@ -244,8 +254,14 @@ def self.locate_android_dependencies(android_sdk_location)
"Could not find '#{zipalign_executable}' in '#{android_sdk_location}'"
end

if apksigner_path.nil?
raise Environment::InvalidEnvironmentError,
"Could not find '#{apksigner_executable}' in '#{android_sdk_location}'"
end

Logging.log_debug("Set aapt path to '#{aapt_path}'")
Logging.log_debug("Set zipalign path to '#{zipalign_path}'")
Logging.log_debug("Set apksigner path to '#{apksigner_path}'")
Logging.log_debug("Set adb path to '#{adb_path}'")

android_jar_path = scan_for_path(File.join(android_sdk_location, 'platforms'), 'android.jar', [File.basename(platform_directory(android_sdk_location))])
Expand All @@ -260,6 +276,7 @@ def self.locate_android_dependencies(android_sdk_location)
{
aapt_path: aapt_path,
zipalign_path: zipalign_path,
apksigner_path: apksigner_path,
adb_path: adb_path,
android_jar_path: android_jar_path
}
Expand Down Expand Up @@ -486,6 +503,10 @@ def self.zipalign_executable
is_windows? ? 'zipalign.exe' : 'zipalign'
end

def self.apksigner_executable
is_windows? ? 'apksigner.exe' : 'apksigner'
end

def self.jarsigner_executable
is_windows? ? 'jarsigner.exe' : 'jarsigner'
end
Expand Down
59 changes: 21 additions & 38 deletions ruby-gem/lib/calabash-android/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ def checksum(file_path)
end

def test_server_path(apk_file_path)
"test_servers/#{checksum(apk_file_path)}_#{Calabash::Android::VERSION}.apk"
if ENV['TEST_APP_PATH']
ENV['TEST_APP_PATH']
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this we can supply prebuilt Calabash servers and don't built anything at all

else
"test_servers/#{checksum(apk_file_path)}_#{Calabash::Android::VERSION}.apk"
end
end

def build_test_server_if_needed(app_path)
Expand Down Expand Up @@ -166,46 +170,25 @@ def sign_apk(app_path, dest_path)

def fingerprint_from_apk(app_path)
app_path = File.expand_path(app_path)
Dir.mktmpdir do |tmp_dir|
Dir.chdir(tmp_dir) do
FileUtils.cp(app_path, "app.apk")
FileUtils.mkdir("META-INF")

Calabash::Utils.with_silent_zip do
Zip::File.foreach("app.apk") do |z|
z.extract if /^META-INF\/\w+\.(rsa|dsa)$/i =~ z.name
end
end

signature_files = Dir["#{tmp_dir}/META-INF/*"]

log 'Signature files:'

signature_files.each do |signature_file|
log signature_file
end

raise "No signature files found in META-INF. Cannot proceed." if signature_files.empty?
raise "More than one signature file (DSA or RSA) found in META-INF. Cannot proceed." if signature_files.length > 1

cmd = "\"#{Calabash::Android::Dependencies.keytool_path}\" -v -printcert -J\"-Dfile.encoding=utf-8\" -file \"#{signature_files.first}\""
log cmd
fingerprints = `#{cmd}`
md5_fingerprint = extract_sha1_fingerprint(fingerprints)
log "SHA1 fingerprint for signing cert (#{app_path}): #{md5_fingerprint}"
md5_fingerprint
end
end
end

def extract_md5_fingerprint(fingerprints)
m = fingerprints.scan(/MD5.*((?:[a-fA-F\d]{2}:){15}[a-fA-F\d]{2})/).flatten
raise "No MD5 fingerprint found:\n #{fingerprints}" if m.empty?
m.first
cmd = "\"#{Calabash::Android::Dependencies.apksigner_path}\" verify --print-certs --verbose \"#{app_path}\""
log cmd
fingerprints = `#{cmd}`
sha1_fingerprint = extract_sha1_fingerprint(fingerprints)
log "SHA1 fingerprint for signing cert (#{app_path}): #{sha1_fingerprint}"
sha1_fingerprint
end

def extract_sha1_fingerprint(fingerprints)
m = fingerprints.scan(/SHA1.*((?:[a-fA-F\d]{2}:){15}[a-fA-F\d]{2})/).flatten
# Try to parse keytool data
m = fingerprints.scan(/SHA1.*((?:[a-fA-F\d]{2}:){19}[a-fA-F\d]{2})/).flatten
if m.empty?
# Try to parse apksigner data
m = fingerprints.scan(/SHA-1.*\b([a-f0-9]{40})\b/).flatten
# Transform it to keytool compatible format
unless m.empty?
return m.first.scan(/../).map(&:upcase).join(':')
end
end
raise "No SHA1 fingerprint found:\n #{fingerprints}" if m.empty?
m.first
end
Expand Down
16 changes: 7 additions & 9 deletions ruby-gem/lib/calabash-android/java_keystore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,21 @@ def sign_apk(apk_path, dest_path)
log "Signing using the digest algorithm: '#{digest_algorithm}'"

cmd_args = {
'-sigfile' => 'CERT',
'-sigalg' => signing_algorithm,
'-digestalg' => digest_algorithm,
'-signedjar' => dest_path,
'-storepass' => password,
'-keystore' => location,
'--out' => dest_path,
'--ks-key-alias' => keystore_alias,
'--ks-pass' => "pass:#{password}",
'--ks' => location,
}

unless @key_password.nil?
cmd_args['-keypass'] = @key_password
cmd_args['--key-pass'] = "pass:#{@key_password}"
end

cmd_args = cmd_args.flatten
cmd_args.unshift('sign')
cmd_args << apk_path
cmd_args << keystore_alias

result = system_with_stdout_on_success(Calabash::Android::Dependencies.jarsigner_path, *cmd_args)
result = system_with_stdout_on_success(Calabash::Android::Dependencies.apksigner_path, *cmd_args)

unless result
raise "Could not sign app: #{apk_path}"
Expand Down
2 changes: 1 addition & 1 deletion ruby-gem/lib/calabash-android/version.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Calabash
module Android

VERSION = "0.9.12.uia10"
VERSION = "0.9.12.uia11"
# Server Commit 6b8a1ad9b645df91a297d4c85ff202e6f0cdda27 patch AccessibilityNodeInfoDumper to include all the non-visible elements and dump everything


Expand Down