Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
9
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Open sidebar
bitfire web engineering
cert4android
Commits
c52e8395
Commit
c52e8395
authored
Mar 12, 2018
by
Ricki Hirner
🐑
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update libraries, Kotlin tests, minor refactoring
parent
0d12bf44
Pipeline
#18765988
passed with stages
Changes
10
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
226 additions
and
236 deletions
+226
-236
build.gradle
build.gradle
+3
-3
src/androidTest/java/at/bitfire/cert4android/CustomCertManagerTest.java
...t/java/at/bitfire/cert4android/CustomCertManagerTest.java
+0
-160
src/androidTest/java/at/bitfire/cert4android/CustomCertManagerTest.kt
...est/java/at/bitfire/cert4android/CustomCertManagerTest.kt
+153
-0
src/main/java/at/bitfire/cert4android/CertUtils.kt
src/main/java/at/bitfire/cert4android/CertUtils.kt
+0
-2
src/main/java/at/bitfire/cert4android/Constants.kt
src/main/java/at/bitfire/cert4android/Constants.kt
+2
-4
src/main/java/at/bitfire/cert4android/CustomCertManager.kt
src/main/java/at/bitfire/cert4android/CustomCertManager.kt
+11
-18
src/main/java/at/bitfire/cert4android/CustomCertService.kt
src/main/java/at/bitfire/cert4android/CustomCertService.kt
+24
-11
src/main/java/at/bitfire/cert4android/NotificationUtils.kt
src/main/java/at/bitfire/cert4android/NotificationUtils.kt
+5
-4
src/main/java/at/bitfire/cert4android/TrustCertificateActivity.kt
.../java/at/bitfire/cert4android/TrustCertificateActivity.kt
+13
-14
src/test/java/at/bitfire/cert4android/CertUtilsTest.kt
src/test/java/at/bitfire/cert4android/CertUtilsTest.kt
+15
-20
No files found.
build.gradle
View file @
c52e8395
buildscript
{
ext
.
kotlin_version
=
'1.2.
21
'
ext
.
kotlin_version
=
'1.2.
30
'
ext
.
dokka_version
=
'0.9.15'
repositories
{
...
...
@@ -46,8 +46,8 @@ android {
dependencies
{
compile
"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile
'com.android.support:appcompat-v7:27.
0.2
'
compile
'com.android.support:cardview-v7:27.
0.2
'
compile
'com.android.support:appcompat-v7:27.
1.0
'
compile
'com.android.support:cardview-v7:27.
1.0
'
androidTestCompile
'com.android.support.test:runner:1.0.1'
androidTestCompile
'com.android.support.test:rules:1.0.1'
...
...
src/androidTest/java/at/bitfire/cert4android/CustomCertManagerTest.java
deleted
100644 → 0
View file @
0d12bf44
/*
* Copyright © Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package
at.bitfire.cert4android
;
import
android.content.Intent
;
import
android.os.IBinder
;
import
android.os.Messenger
;
import
android.support.test.rule.ServiceTestRule
;
import
android.support.test.runner.AndroidJUnit4
;
import
org.junit.After
;
import
org.junit.Before
;
import
org.junit.Rule
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
java.io.IOException
;
import
java.net.URL
;
import
java.security.cert.Certificate
;
import
java.security.cert.CertificateException
;
import
java.security.cert.X509Certificate
;
import
java.util.concurrent.TimeoutException
;
import
javax.net.ssl.HttpsURLConnection
;
import
static
android
.
support
.
test
.
InstrumentationRegistry
.
getContext
;
import
static
android
.
support
.
test
.
InstrumentationRegistry
.
getTargetContext
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
@RunWith
(
AndroidJUnit4
.
class
)
public
class
CustomCertManagerTest
{
CustomCertManager
certManager
,
paranoidCertManager
;
static
{
CustomCertManager
.
SERVICE_TIMEOUT
=
1000
;
}
@Rule
public
ServiceTestRule
serviceTestRule
=
new
ServiceTestRule
();
Messenger
service
;
private
static
X509Certificate
[]
siteCerts
;
static
{
try
{
siteCerts
=
getSiteCertificates
(
new
URL
(
"https://www.davdroid.com"
));
}
catch
(
IOException
ignored
)
{
}
assertNotNull
(
siteCerts
);
}
@Before
public
void
initCertManager
()
throws
TimeoutException
,
InterruptedException
{
// prepare a bound and ready service for testing
// loop required because of https://code.google.com/p/android/issues/detail?id=180396
IBinder
binder
=
bindService
(
CustomCertService
.
class
);
assertNotNull
(
binder
);
CustomCertManager
.
resetCertificates
(
getContext
());
certManager
=
new
CustomCertManager
(
getContext
(),
false
);
assertNotNull
(
certManager
);
paranoidCertManager
=
new
CustomCertManager
(
getContext
(),
false
,
false
);
assertNotNull
(
paranoidCertManager
);
}
@After
public
void
closeCertManager
()
{
paranoidCertManager
.
close
();
certManager
.
close
();
}
@Test
(
expected
=
CertificateException
.
class
)
public
void
testCheckClientCertificate
()
throws
CertificateException
{
certManager
.
checkClientTrusted
(
null
,
null
);
}
@Test
public
void
testTrustedCertificate
()
throws
CertificateException
,
TimeoutException
{
certManager
.
checkServerTrusted
(
siteCerts
,
"RSA"
);
}
@Test
(
expected
=
CertificateException
.
class
)
public
void
testParanoidCertificate
()
throws
CertificateException
{
paranoidCertManager
.
checkServerTrusted
(
siteCerts
,
"RSA"
);
}
@Test
public
void
testAddCustomCertificate
()
throws
CertificateException
,
TimeoutException
,
InterruptedException
{
addCustomCertificate
();
paranoidCertManager
.
checkServerTrusted
(
siteCerts
,
"RSA"
);
}
// fails randomly for unknown reason:
@Test
(
expected
=
CertificateException
.
class
)
public
void
testRemoveCustomCertificate
()
throws
CertificateException
,
TimeoutException
,
InterruptedException
{
addCustomCertificate
();
// remove certificate and check again
// should now be rejected for the whole session, i.e. no timeout anymore
Intent
intent
=
new
Intent
(
getContext
(),
CustomCertService
.
class
);
intent
.
setAction
(
CustomCertService
.
CMD_CERTIFICATION_DECISION
);
intent
.
putExtra
(
CustomCertService
.
EXTRA_CERTIFICATE
,
siteCerts
[
0
].
getEncoded
());
intent
.
putExtra
(
CustomCertService
.
EXTRA_TRUSTED
,
false
);
startService
(
intent
,
CustomCertService
.
class
);
paranoidCertManager
.
checkServerTrusted
(
siteCerts
,
"RSA"
);
}
private
void
addCustomCertificate
()
throws
CertificateException
,
TimeoutException
,
InterruptedException
{
// add certificate and check again
Intent
intent
=
new
Intent
(
getContext
(),
CustomCertService
.
class
);
intent
.
setAction
(
CustomCertService
.
CMD_CERTIFICATION_DECISION
);
intent
.
putExtra
(
CustomCertService
.
EXTRA_CERTIFICATE
,
siteCerts
[
0
].
getEncoded
());
intent
.
putExtra
(
CustomCertService
.
EXTRA_TRUSTED
,
true
);
startService
(
intent
,
CustomCertService
.
class
);
}
private
IBinder
bindService
(
Class
clazz
)
throws
TimeoutException
,
InterruptedException
{
IBinder
binder
=
null
;
int
it
=
0
;
while
((
binder
=
serviceTestRule
.
bindService
(
new
Intent
(
getTargetContext
(),
clazz
)))
==
null
&&
it
++
<
100
)
{
System
.
err
.
println
(
"Waiting for ServiceTestRule.bindService"
);
Thread
.
sleep
(
50
);
}
if
(
binder
==
null
)
throw
new
IllegalStateException
(
"Couldn't bind to service"
);
return
binder
;
}
private
void
startService
(
Intent
intent
,
Class
clazz
)
throws
TimeoutException
,
InterruptedException
{
serviceTestRule
.
startService
(
intent
);
bindService
(
clazz
);
}
private
static
X509Certificate
[]
getSiteCertificates
(
URL
url
)
throws
IOException
{
HttpsURLConnection
conn
=
(
HttpsURLConnection
)
url
.
openConnection
();
try
{
conn
.
getInputStream
().
read
();
Certificate
[]
certs
=
conn
.
getServerCertificates
();
X509Certificate
[]
x509
=
new
X509Certificate
[
certs
.
length
];
System
.
arraycopy
(
certs
,
0
,
x509
,
0
,
certs
.
length
);
return
x509
;
}
finally
{
conn
.
disconnect
();
}
}
}
src/androidTest/java/at/bitfire/cert4android/CustomCertManagerTest.kt
0 → 100644
View file @
c52e8395
/*
* Copyright © Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package
at.bitfire.cert4android
import
android.app.Service
import
android.content.Intent
import
android.os.IBinder
import
android.support.test.InstrumentationRegistry.getContext
import
android.support.test.InstrumentationRegistry.getTargetContext
import
android.support.test.rule.ServiceTestRule
import
org.junit.After
import
org.junit.Assert.assertNotNull
import
org.junit.Assume.assumeNotNull
import
org.junit.Before
import
org.junit.Rule
import
org.junit.Test
import
java.io.IOException
import
java.net.URL
import
java.security.cert.CertificateException
import
java.security.cert.X509Certificate
import
javax.net.ssl.HttpsURLConnection
class
CustomCertManagerTest
{
companion
object
{
private
fun
getSiteCertificates
(
url
:
URL
):
List
<
X509Certificate
>
{
val
conn
=
url
.
openConnection
()
as
HttpsURLConnection
try
{
conn
.
inputStream
.
read
()
val
certs
=
mutableListOf
<
X509Certificate
>()
conn
.
serverCertificates
.
forEach
{
certs
+=
it
as
X509Certificate
}
return
certs
}
finally
{
conn
.
disconnect
()
}
}
}
lateinit
var
certManager
:
CustomCertManager
lateinit
var
paranoidCertManager
:
CustomCertManager
init
{
CustomCertManager
.
SERVICE_TIMEOUT
=
1000
}
@JvmField
@Rule
val
serviceTestRule
=
ServiceTestRule
()
var
siteCerts
:
List
<
X509Certificate
>?
=
null
init
{
try
{
siteCerts
=
getSiteCertificates
(
URL
(
"https://www.davdroid.com"
))
}
catch
(
e
:
IOException
)
{
}
assumeNotNull
(
siteCerts
)
}
@Before
fun
initCertManager
()
{
// prepare a bound and ready service for testing
// loop required because of https://code.google.com/p/android/issues/detail?id=180396
val
binder
=
bindService
(
CustomCertService
::
class
.
java
)
assertNotNull
(
binder
)
CustomCertManager
.
resetCertificates
(
getContext
())
certManager
=
CustomCertManager
(
getContext
(),
false
)
assertNotNull
(
certManager
)
paranoidCertManager
=
CustomCertManager
(
getContext
(),
false
,
false
)
assertNotNull
(
paranoidCertManager
)
}
@After
fun
closeCertManager
()
{
paranoidCertManager
.
close
()
certManager
.
close
()
}
@Test
(
expected
=
CertificateException
::
class
)
fun
testCheckClientCertificate
()
{
certManager
.
checkClientTrusted
(
null
,
null
)
}
@Test
fun
testTrustedCertificate
()
{
certManager
.
checkServerTrusted
(
siteCerts
!!
.
toTypedArray
(),
"RSA"
)
}
@Test
(
expected
=
CertificateException
::
class
)
fun
testParanoidCertificate
()
{
paranoidCertManager
.
checkServerTrusted
(
siteCerts
!!
.
toTypedArray
(),
"RSA"
)
}
@Test
fun
testAddCustomCertificate
()
{
addCustomCertificate
()
paranoidCertManager
.
checkServerTrusted
(
siteCerts
!!
.
toTypedArray
(),
"RSA"
)
}
// fails randomly for unknown reason:
@Test
(
expected
=
CertificateException
::
class
)
fun
testRemoveCustomCertificate
()
{
addCustomCertificate
()
// remove certificate and check again
// should now be rejected for the whole session, i.e. no timeout anymore
val
intent
=
Intent
(
getContext
(),
CustomCertService
::
class
.
java
)
intent
.
action
=
CustomCertService
.
CMD_CERTIFICATION_DECISION
intent
.
putExtra
(
CustomCertService
.
EXTRA_CERTIFICATE
,
siteCerts
!!
.
first
().
encoded
)
intent
.
putExtra
(
CustomCertService
.
EXTRA_TRUSTED
,
false
)
startService
(
intent
,
CustomCertService
::
class
.
java
)
paranoidCertManager
.
checkServerTrusted
(
siteCerts
!!
.
toTypedArray
(),
"RSA"
)
}
private
fun
addCustomCertificate
()
{
// add certificate and check again
val
intent
=
Intent
(
getContext
(),
CustomCertService
::
class
.
java
)
intent
.
action
=
CustomCertService
.
CMD_CERTIFICATION_DECISION
intent
.
putExtra
(
CustomCertService
.
EXTRA_CERTIFICATE
,
siteCerts
!!
.
first
().
encoded
)
intent
.
putExtra
(
CustomCertService
.
EXTRA_TRUSTED
,
true
)
startService
(
intent
,
CustomCertService
::
class
.
java
)
}
private
fun
bindService
(
clazz
:
Class
<
out
Service
>):
IBinder
{
var
binder
=
serviceTestRule
.
bindService
(
Intent
(
getTargetContext
(),
clazz
))
var
it
=
0
while
(
binder
==
null
&&
it
++
<
100
)
{
binder
=
serviceTestRule
.
bindService
(
Intent
(
getTargetContext
(),
clazz
))
System
.
err
.
println
(
"Waiting for ServiceTestRule.bindService"
)
Thread
.
sleep
(
50
)
}
if
(
binder
==
null
)
throw
IllegalStateException
(
"Couldn't bind to service"
)
return
binder
}
private
fun
startService
(
intent
:
Intent
,
clazz
:
Class
<
out
Service
>)
{
serviceTestRule
.
startService
(
intent
)
bindService
(
clazz
)
}
}
src/main/java/at/bitfire/cert4android/CertUtils.kt
View file @
c52e8395
...
...
@@ -17,7 +17,6 @@ import javax.net.ssl.X509TrustManager
object
CertUtils
{
@JvmStatic
fun
getTrustManager
(
keyStore
:
KeyStore
?):
X509TrustManager
?
{
try
{
val
tmf
=
TrustManagerFactory
.
getInstance
(
"X509"
)
...
...
@@ -31,7 +30,6 @@ object CertUtils {
return
null
}
@JvmStatic
fun
getTag
(
cert
:
X509Certificate
):
String
{
val
str
=
StringBuilder
()
for
(
b
in
cert
.
signature
)
...
...
src/main/java/at/bitfire/cert4android/Constants.kt
View file @
c52e8395
...
...
@@ -14,9 +14,8 @@ import java.util.logging.Logger
object
Constants
{
val
TAG
=
"cert4android"
const
val
TAG
=
"cert4android"
@JvmField
var
log
:
Logger
=
Logger
.
getLogger
(
TAG
)
init
{
log
.
level
=
if
(
Log
.
isLoggable
(
TAG
,
Log
.
VERBOSE
))
...
...
@@ -25,7 +24,6 @@ object Constants {
Level
.
INFO
}
@JvmField
val
NOTIFICATION_CERT_DECISION
=
88809
const
val
NOTIFICATION_CERT_DECISION
=
88809
}
src/main/java/at/bitfire/cert4android/CustomCertManager.kt
View file @
c52e8395
...
...
@@ -30,9 +30,17 @@ import javax.net.ssl.X509TrustManager
* each of them with an own [CustomCertManager], want to access a synchronized central
* certificate trust store + UI (for accepting certificates etc.).
*
* @param context used to bind to [CustomCertService]
* @param interactive true: users will be notified in case of unknown certificates;
* false: unknown certificates will be rejected (only uses custom certificate key store)
* @param trustSystemCerts whether system certificates will be trusted
* @param trustSystemCerts whether system certificates will be trusted
*
* @constructor Creates a new instance, using a certain [CustomCertService] messenger (for testing).
* Must not be run from the main thread because this constructor may request binding to [CustomCertService].
* The actual binding code is called by the looper in the main thread, so waiting for the
* service would block forever.
*
* @throws IllegalStateException if run from main thread
*/
class
CustomCertManager
@JvmOverloads
constructor
(
val
context
:
Context
,
...
...
@@ -43,10 +51,8 @@ class CustomCertManager @JvmOverloads constructor(
companion
object
{
/** how long to wait for a decision from [CustomCertService] before giving up temporarily */
@JvmField
var
SERVICE_TIMEOUT
:
Long
=
3
*
60
*
1000
@JvmStatic
fun
resetCertificates
(
context
:
Context
):
Boolean
{
val
intent
=
Intent
(
context
,
CustomCertService
::
class
.
java
)
intent
.
action
=
CustomCertService
.
CMD_RESET_CERTIFICATES
...
...
@@ -64,22 +70,10 @@ class CustomCertManager @JvmOverloads constructor(
if
(
trustSystemCerts
)
CertUtils
.
getTrustManager
(
null
)
else
null
/** Whether to launch {@link TrustCertificateActivity} directly. The notification will always be shown. */
@JvmField
/** Whether to launch [TrustCertificateActivity] directly. The notification will always be shown. */
var
appInForeground
=
false
/**
* Creates a new instance, using a certain [CustomCertService] messenger (for testing).
* Must not be run from the main thread because this constructor may request binding to [CustomCertService].
* The actual binding code is called by the looper in the main thread, so waiting for the
* service would block forever.
*
* @param context used to bind to [CustomCertService]
* @param interactive whether calls to [CustomCertService] are flagged as interactive (which allows the user to accept/deny certificates)
* @param trustSystemCerts whether to trust system/user-installed CAs (default trust store)
* @throws IllegalStateException if run from main thread
*/
init
{
serviceConnection
=
object
:
ServiceConnection
{
override
fun
onServiceConnected
(
className
:
ComponentName
,
binder
:
IBinder
)
{
...
...
@@ -180,7 +174,6 @@ class CustomCertManager @JvmOverloads constructor(
}
}
val
id
:
Int
try
{
svc
.
checkTrusted
(
cert
.
encoded
,
interactive
,
appInForeground
,
callback
)
synchronized
(
lock
)
{
...
...
@@ -222,7 +215,7 @@ class CustomCertManager @JvmOverloads constructor(
):
HostnameVerifier
{
override
fun
verify
(
host
:
String
,
sslSession
:
SSLSession
):
Boolean
{
Constants
.
log
.
fine
(
"Verifying certificate for
"
+
host
)
Constants
.
log
.
fine
(
"Verifying certificate for
$
host
"
)
if
(
defaultVerifier
?.
verify
(
host
,
sslSession
)
==
true
)
return
true
...
...
src/main/java/at/bitfire/cert4android/CustomCertService.kt
View file @
c52e8395
...
...
@@ -26,21 +26,34 @@ import java.util.*
import
java.util.logging.Level
import
javax.net.ssl.X509TrustManager
/**
* The service which manages the certificates. Communications with
* the [CustomCertManager]s over IPC.
*
* This services is both a started and a bound service.
*/
class
CustomCertService
:
Service
()
{
companion
object
{
// started service
@JvmField
val
CMD_CERTIFICATION_DECISION
=
"certificateDecision"
@JvmField
val
CMD_RESET_CERTIFICATES
=
"resetCertificates"
@JvmField
val
EXTRA_CERTIFICATE
=
"certificate"
@JvmField
val
EXTRA_TRUSTED
=
"trusted"
val
KEYSTORE_DIR
=
"KeyStore"
val
KEYSTORE_NAME
=
"KeyStore.bks"
/**
* Command when used as started service to accept/reject an open certificate decision.
* Usually sent by a notification action or [TrustCertificateActivity].
*/
const
val
CMD_CERTIFICATION_DECISION
=
"certificateDecision"
/**
* Command when used as a started service to remove all known certificates.
* Resets the state of all previously accepted and rejected certificates.
*/
const
val
CMD_RESET_CERTIFICATES
=
"resetCertificates"
const
val
EXTRA_CERTIFICATE
=
"certificate"
const
val
EXTRA_TRUSTED
=
"trusted"
const
val
KEYSTORE_DIR
=
"KeyStore"
const
val
KEYSTORE_NAME
=
"KeyStore.bks"
}
private
var
keyStoreFile
:
File
?
=
null
private
lateinit
var
keyStoreFile
:
File
private
val
certFactory
=
CertificateFactory
.
getInstance
(
"X.509"
)
private
val
trustedKeyStore
=
KeyStore
.
getInstance
(
KeyStore
.
getDefaultType
())
!!
...
...
@@ -87,7 +100,7 @@ class CustomCertService: Service() {
// started service
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
id
:
Int
):
Int
{
Constants
.
log
.
fine
(
"Received command:
"
+
intent
)
Constants
.
log
.
fine
(
"Received command:
$
intent
"
)
when
(
intent
?.
action
)
{
CMD_CERTIFICATION_DECISION
->
{
...
...
src/main/java/at/bitfire/cert4android/NotificationUtils.kt
View file @
c52e8395
...
...
@@ -12,19 +12,20 @@ import android.app.NotificationChannel
import
android.app.NotificationManager
import
android.content.Context
import
android.os.Build
import
android.support.v4.app.NotificationManagerCompat
object
NotificationUtils
{
val
CHANNEL_CERTIFICATES
=
"cert4android"
const
val
CHANNEL_CERTIFICATES
=
"cert4android"
fun
createChannels
(
context
:
Context
):
NotificationManager
{
fun
createChannels
(
context
:
Context
):
NotificationManager
Compat
{
val
nm
=
context
.
getSystemService
(
Context
.
NOTIFICATION_SERVICE
)
as
NotificationManager
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
nm
.
createNotificationChannel
(
NotificationChannel
(
CHANNEL_CERTIFICATES
,
context
.
getString
(
R
.
string
.
certificate_notification_connection_security
),
NotificationManager
.
IMPORTANCE_
DEFAULT
))
context
.
getString
(
R
.
string
.
certificate_notification_connection_security
),
NotificationManager
.
IMPORTANCE_
HIGH
))
return
nm
return
NotificationManagerCompat
.
from
(
context
)
}
}
\ No newline at end of file
src/main/java/at/bitfire/cert4android/TrustCertificateActivity.kt
View file @
c52e8395
...
...
@@ -27,7 +27,7 @@ import java.util.logging.Level
class
TrustCertificateActivity
:
AppCompatActivity
()
{
companion
object
{
val
EXTRA_CERTIFICATE
=
"certificate"
const
val
EXTRA_CERTIFICATE
=
"certificate"
val
certFactory
=
CertificateFactory
.
getInstance
(
"X.509"
)
!!
}
...
...
@@ -65,18 +65,18 @@ class TrustCertificateActivity: AppCompatActivity() {
var
tv
=
findViewById
<
TextView
>(
R
.
id
.
issuedFor
)
tv
.
text
=
subject
tv
=
findViewById
<
TextView
>
(
R
.
id
.
issuedBy
)
tv
=
findViewById
(
R
.
id
.
issuedBy
)
tv
.
text
=
cert
.
issuerDN
.
toString
()
val
formatter
=
DateFormat
.
getDateInstance
(
DateFormat
.
LONG
)
tv
=
findViewById
<
TextView
>
(
R
.
id
.
validity_period
)
tv
=
findViewById
(
R
.
id
.
validity_period
)
tv
.
text
=
getString
(
R
.
string
.
trust_certificate_validity_period_value
,
formatter
.
format
(
cert
.
notBefore
),
formatter
.
format
(
cert
.
notAfter
))
tv
=
findViewById
<
TextView
>
(
R
.
id
.
fingerprint_sha1
)
tv
=
findViewById
(
R
.
id
.
fingerprint_sha1
)
tv
.
text
=
fingerprint
(
cert
,
"SHA-1"
)
tv
=
findViewById
<
TextView
>
(
R
.
id
.
fingerprint_sha256
)
tv
=
findViewById
(
R
.
id
.
fingerprint_sha256
)
tv
.
text
=
fingerprint
(
cert
,
"SHA-256"
)
}
catch
(
e
:
CertificateParsingException
)
{
Constants
.
log
.
log
(
Level
.
WARNING
,
"Couldn't parse certificate"
,
e
)
...
...
@@ -110,17 +110,16 @@ class TrustCertificateActivity: AppCompatActivity() {
}
private
fun
fingerprint
(
cert
:
X509Certificate
,
algorithm
:
String
):
String
{
try
{
val
md
=
MessageDigest
.
getInstance
(
algorithm
)
return
"$algorithm: ${hexString(md.digest(cert.encoded))}"
}
catch
(
e
:
Exception
)
{
return
e
.
message
?:
"Couldn't create message digest"
}
}
private
fun
fingerprint
(
cert
:
X509Certificate
,
algorithm
:
String
)
=
try
{
val
md
=
MessageDigest
.
getInstance
(
algorithm
)
"$algorithm: ${hexString(md.digest(cert.encoded))}"
}
catch
(
e
:
Exception
)
{
e
.
message
?:
"Couldn't create message digest"
}
private
fun
hexString
(
data
:
ByteArray
):
String
{
val
str
=
data
.
mapTo
(
LinkedList
<
String
>
())
{
String
.
format
(
"%02x"
,
it
)
}
val
str
=
data
.
mapTo
(
LinkedList
())
{
String
.
format
(
"%02x"
,
it
)
}
return
str
.
joinToString
(
":"
)
}
...
...
src/test/java/at/bitfire/cert4android/CertUtilsTest.
java
→
src/test/java/at/bitfire/cert4android/CertUtilsTest.
kt