Detect no valid certificates and allow to store new ones

parent 0d579f10
......@@ -101,6 +101,11 @@
android:configChanges="orientation|screenSize"
android:label="@string/trust_anchor_title"
android:parentActivityName=".view.devicelist.DeviceListActivity" />
<activity
android:name=".view.trustanchor.CertificateActivity"
android:configChanges="orientation|screenSize"
android:label="@string/certificate_title"
android:parentActivityName=".view.trustanchor.TrustAnchorActivity" />
<activity
android:name=".view.logviewer.LogViewerActivity"
android:configChanges="orientation|screenSize"
......
......@@ -217,6 +217,26 @@ public class CmsRepository {
});
}
public Completable addIntermediateCertificate(Integer credid, byte[] cert) {
return Completable.create(emitter -> {
if (OCPki.addMfgIntermediateCert(0 /* First device */, credid, cert) == -1) {
emitter.onError(new Exception("Add intermediate certificate error"));
}
emitter.onComplete();
});
}
public Completable addEndEntityCertificate(byte[] cert, byte[] key) {
return Completable.create(emitter -> {
if (OCPki.addMfgCert(0 /* First device */, cert, key) == -1) {
emitter.onError(new Exception("Add end entity certificate error"));
}
emitter.onComplete();
});
}
public Completable removeTrustAnchor(long credid) {
return Completable.create(emitter -> {
int ret = OCObt.deleteOwnCredByCredId((int)credid);
......
......@@ -178,6 +178,17 @@ public class IORepository {
});
}
public Single<byte[]> getBytesFromFile(InputStream is) {
return Single.fromCallable(() -> {
int numBytes = is.available() + 1;
byte[] fileBytes = new byte[numBytes];
is.read(fileBytes);
fileBytes[numBytes - 1] = '\0';
return fileBytes;
});
}
public Single<CBORObject> getAssetSvrAsCbor(String resource, long device) {
return Single.create(emitter -> {
try (FileInputStream stream = new FileInputStream(mContext.getFilesDir() +
......
......@@ -22,6 +22,8 @@
package org.openconnectivity.otgc.domain.usecase;
import android.content.Context;
import org.iotivity.OCFactoryPresetsHandler;
import org.iotivity.OCObt;
import org.iotivity.OCPki;
......@@ -30,6 +32,11 @@ import org.openconnectivity.otgc.data.repository.IotivityRepository;
import org.openconnectivity.otgc.data.repository.PreferencesRepository;
import org.openconnectivity.otgc.utils.constant.OtgcConstant;
import org.openconnectivity.otgc.utils.constant.OtgcMode;
import org.openconnectivity.otgc.utils.handler.DisplayNotValidCertificateHandler;
import java.io.InputStream;
import java.security.cert.X509Certificate;
import java.util.Date;
import javax.inject.Inject;
......@@ -40,6 +47,9 @@ public class InitializeIotivityUseCase {
private final IORepository ioRepository;
private final PreferencesRepository settingRepository;
private Context context;
private DisplayNotValidCertificateHandler displayNotValidCertificateHandler;
@Inject
public InitializeIotivityUseCase(IotivityRepository iotivityRepository,
IORepository ioRepository,
......@@ -49,7 +59,10 @@ public class InitializeIotivityUseCase {
this.settingRepository = settingRepository;
}
public Completable execute() {
public Completable execute(Context context, DisplayNotValidCertificateHandler displayNotValidCertificateHandler) {
this.context = context;
this.displayNotValidCertificateHandler = displayNotValidCertificateHandler;
Completable initOic = iotivityRepository.initOICStack();
Completable completable = iotivityRepository.setFactoryResetHandler(factoryReset);
......@@ -76,31 +89,58 @@ public class InitializeIotivityUseCase {
}
});
private void factoryResetHandler(long device) throws Exception {
/* Current date */
Date date = new Date();
/* Kyrio end-entity cert */
byte[] kyrioEeCertificate = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_EE_CERTIFICATE).blockingGet();
/* private key of Kyrio end-entity cert */
byte[] kyrioEeKey = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_EE_KEY).blockingGet();
int credid = OCPki.addMfgCert(device, kyrioEeCertificate, kyrioEeKey);
if (credid == -1) {
throw new Exception("Add identity certificate error");
}
InputStream inputStream = context.getAssets().open(OtgcConstant.KYRIO_EE_CERTIFICATE);
X509Certificate eeCert = ioRepository.getFileAsX509Certificate(inputStream).blockingGet();
if (date.after(eeCert.getNotBefore()) && date.before(eeCert.getNotAfter())) {
byte[] kyrioEeCertificate = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_EE_CERTIFICATE).blockingGet();
/* private key of Kyrio end-entity cert */
byte[] kyrioEeKey = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_EE_KEY).blockingGet();
int credid = OCPki.addMfgCert(device, kyrioEeCertificate, kyrioEeKey);
if (credid == -1) {
throw new Exception("Add identity certificate error");
}
/* Kyrio intermediate cert */
byte[] kyrioSubcaCertificate = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_SUBCA_CERTIFICATE).blockingGet();
if (OCPki.addMfgIntermediateCert(device, credid, kyrioSubcaCertificate) == -1) {
throw new Exception("Add intermediate certificate error");
/* Kyrio intermediate cert */
inputStream = context.getAssets().open(OtgcConstant.KYRIO_SUBCA_CERTIFICATE);
X509Certificate subCaCert = ioRepository.getFileAsX509Certificate(inputStream).blockingGet();
if (date.after(subCaCert.getNotBefore()) && date.before(subCaCert.getNotAfter())) {
byte[] kyrioSubcaCertificate = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_SUBCA_CERTIFICATE).blockingGet();
if (OCPki.addMfgIntermediateCert(device, credid, kyrioSubcaCertificate) == -1) {
throw new Exception("Add intermediate certificate error");
}
} else {
this.displayNotValidCertificateHandler.handler("Kyrio intermediate certificate is not valid");
}
} else {
this.displayNotValidCertificateHandler.handler("Kyrio end entity certificate is not valid");
}
/* Kyrio root cert */
byte[] kyrioRootcaCertificate = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_ROOT_CERTIFICATE).blockingGet();
if (OCPki.addMfgTrustAnchor(device, kyrioRootcaCertificate) == -1) {
throw new Exception("Add root certificate error");
inputStream = context.getAssets().open(OtgcConstant.KYRIO_ROOT_CERTIFICATE);
X509Certificate caCert = ioRepository.getFileAsX509Certificate(inputStream).blockingGet();
if (date.after(caCert.getNotBefore()) && date.before(caCert.getNotAfter())) {
byte[] kyrioRootcaCertificate = ioRepository.getBytesFromFile(OtgcConstant.KYRIO_ROOT_CERTIFICATE).blockingGet();
if (OCPki.addMfgTrustAnchor(device, kyrioRootcaCertificate) == -1) {
throw new Exception("Add root certificate error");
}
} else {
this.displayNotValidCertificateHandler.handler("Kyrio root certificate is not valid");
}
/* EonTi root cert */
byte[] eontiRootcaCertificate = ioRepository.getBytesFromFile(OtgcConstant.EONTI_ROOT_CERTIFICATE).blockingGet();
if (OCPki.addMfgTrustAnchor(device, eontiRootcaCertificate) == -1) {
throw new Exception("Add root certificate error");
inputStream = context.getAssets().open(OtgcConstant.EONTI_ROOT_CERTIFICATE);
caCert = ioRepository.getFileAsX509Certificate(inputStream).blockingGet();
if (date.after(caCert.getNotBefore()) && date.before(caCert.getNotAfter())) {
byte[] eontiRootcaCertificate = ioRepository.getBytesFromFile(OtgcConstant.EONTI_ROOT_CERTIFICATE).blockingGet();
if (OCPki.addMfgTrustAnchor(device, eontiRootcaCertificate) == -1) {
throw new Exception("Add root certificate error");
}
} else {
this.displayNotValidCertificateHandler.handler("EonTi root certificate is not valid");
}
OCObt.shutdown();
......
......@@ -45,7 +45,9 @@ public class GetTrustAnchorUseCase {
for (OcCredential cred : creds.getCredList()) {
if (cred.getCredusage() != null && !cred.getCredusage().isEmpty()
&& (OCCredUtil.parseCredUsage(cred.getCredusage()) == OCCredUsage.OC_CREDUSAGE_MFG_TRUSTCA
|| OCCredUtil.parseCredUsage(cred.getCredusage()) == OCCredUsage.OC_CREDUSAGE_TRUSTCA)) {
|| OCCredUtil.parseCredUsage(cred.getCredusage()) == OCCredUsage.OC_CREDUSAGE_TRUSTCA
|| OCCredUtil.parseCredUsage(cred.getCredusage()) == OCCredUsage.OC_CREDUSAGE_IDENTITY_CERT
|| OCCredUtil.parseCredUsage(cred.getCredusage()) == OCCredUsage.OC_CREDUSAGE_MFG_CERT)) {
trustAnchorList.add(cred);
}
}
......
/*
* Copyright 2018 DEKRA Testing and Certification, S.A.U. All Rights Reserved.
*
* *****************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openconnectivity.otgc.domain.usecase.trustanchor;
import org.openconnectivity.otgc.data.repository.CmsRepository;
import org.openconnectivity.otgc.data.repository.IORepository;
import java.io.InputStream;
import javax.inject.Inject;
import io.reactivex.Completable;
import io.reactivex.Single;
public class SaveEndEntityCertificateUseCase {
private final IORepository ioRepository;
private final CmsRepository cmsRepository;
@Inject
public SaveEndEntityCertificateUseCase(IORepository ioRepository,
CmsRepository cmsRepository) {
this.ioRepository = ioRepository;
this.cmsRepository = cmsRepository;
}
public Completable execute(InputStream certIs, InputStream keyIs) {
Single<byte[]> pemCertObservable = ioRepository.getBytesFromFile(certIs);
Single<byte[]> pemKeyCertObservable = ioRepository.getBytesFromFile(keyIs);
return pemCertObservable.flatMapCompletable(
cert -> pemKeyCertObservable.flatMapCompletable(
keyCert -> cmsRepository.addEndEntityCertificate(cert, keyCert)
));
}
}
\ No newline at end of file
/*
* Copyright 2018 DEKRA Testing and Certification, S.A.U. All Rights Reserved.
*
* *****************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openconnectivity.otgc.domain.usecase.trustanchor;
import org.openconnectivity.otgc.data.repository.CmsRepository;
import org.openconnectivity.otgc.data.repository.IORepository;
import java.io.InputStream;
import javax.inject.Inject;
import io.reactivex.Completable;
public class SaveIntermediateCertificateUseCase {
private final IORepository ioRepository;
private final CmsRepository cmsRepository;
@Inject
public SaveIntermediateCertificateUseCase(IORepository ioRepository,
CmsRepository cmsRepository) {
this.ioRepository = ioRepository;
this.cmsRepository = cmsRepository;
}
public Completable execute(Integer credid, InputStream is) {
return ioRepository.getBytesFromFile(is)
.flatMapCompletable(cert -> cmsRepository.addIntermediateCertificate(credid, cert));
}
}
\ No newline at end of file
......@@ -32,6 +32,7 @@ import org.openconnectivity.otgc.view.devicelist.DeviceListActivity;
import org.openconnectivity.otgc.view.link.LinkedRolesActivity;
import org.openconnectivity.otgc.view.login.LoginActivity;
import org.openconnectivity.otgc.view.splash.SplashActivity;
import org.openconnectivity.otgc.view.trustanchor.CertificateActivity;
import org.openconnectivity.otgc.view.trustanchor.TrustAnchorActivity;
import org.openconnectivity.otgc.view.wlanscan.WlanScanActivity;
......@@ -73,4 +74,7 @@ public interface BuildersModule {
@ContributesAndroidInjector
abstract TrustAnchorActivity bindTrustAnchorActivity();
@ContributesAndroidInjector
abstract CertificateActivity bindCertificateActivity();
}
......@@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModelProvider;
import org.openconnectivity.otgc.viewmodel.AccessControlViewModel;
import org.openconnectivity.otgc.viewmodel.AceViewModel;
import org.openconnectivity.otgc.viewmodel.CertificateViewModel;
import org.openconnectivity.otgc.viewmodel.ResourceViewModel;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelFactory;
import org.openconnectivity.otgc.viewmodel.GenericClientViewModel;
......@@ -119,4 +120,9 @@ abstract class ViewModelModule {
@IntoMap
@ViewModelKey(TrustAnchorViewModel.class)
abstract ViewModel bindTrustAnchorsViewModel(TrustAnchorViewModel trustAnchorViewModel);
@Binds
@IntoMap
@ViewModelKey(CertificateViewModel.class)
abstract ViewModel bindCertificateViewModel(CertificateViewModel certificateViewModel);
}
/*
* *****************************************************************
*
* Copyright 2018 DEKRA Testing and Certification, S.A.U. All Rights Reserved.
*
* *****************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* *****************************************************************
*/
package org.openconnectivity.otgc.utils.handler;
public interface DisplayNotValidCertificateHandler {
public void handler(String content);
}
......@@ -39,6 +39,7 @@ import android.widget.Toast;
import org.iotivity.OCRandomPinHandler;
import org.openconnectivity.otgc.utils.constant.OtgcMode;
import org.openconnectivity.otgc.utils.handler.DisplayNotValidCertificateHandler;
import org.openconnectivity.otgc.utils.handler.OCSetRandomPinHandler;
import org.openconnectivity.otgc.utils.view.RecyclerWithSwipeFragment;
import org.openconnectivity.otgc.utils.viewmodel.CommonError;
......@@ -131,6 +132,18 @@ public class DeviceListActivity extends AppCompatActivity implements HasSupportF
});
};
DisplayNotValidCertificateHandler displayNotValidCertificateHandler = content -> {
runOnUiThread(() -> {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(new ContextThemeWrapper(DeviceListActivity.this, R.style.AppTheme));
alertDialog.setTitle(DeviceListActivity.this.getString(R.string.devices_dialog_show_not_valid_certificate_title));
alertDialog.setMessage(content);
alertDialog.setCancelable(false);
alertDialog.setPositiveButton(
DeviceListActivity.this.getString(R.string.devices_dialog_show_not_valid_certificate_ok_option),
(dialog, which) -> dialog.dismiss()).show();
});
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -169,7 +182,7 @@ public class DeviceListActivity extends AppCompatActivity implements HasSupportF
}
}
mViewModel.initializeIotivityStack();
mViewModel.initializeIotivityStack(getApplicationContext(), displayNotValidCertificateHandler);
return true;
}
......
package org.openconnectivity.otgc.view.trustanchor;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProviders;
import org.openconnectivity.otgc.R;
import org.openconnectivity.otgc.domain.model.resource.secure.cred.OcCredential;
import org.openconnectivity.otgc.utils.di.Injectable;
import org.openconnectivity.otgc.utils.viewmodel.ViewModelError;
import org.openconnectivity.otgc.viewmodel.CertificateViewModel;
import java.io.InputStream;
import java.util.ArrayList;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class CertificateActivity extends AppCompatActivity implements Injectable {
@Inject
ViewModelProvider.Factory mViewModelFactory;
@BindView(R.id.toolbar) Toolbar mToolbar;
@BindView(R.id.trust_anchor_select_end_entity_layout) LinearLayout selectEndEntityLayout;
@BindView(R.id.spinner_select_end_entity_certificate) Spinner selectEndEntityCert;
@BindView(R.id.button_select_certificate) Button selectCertificate;
@BindView(R.id.trust_anchor_selected_certificate_text) TextView selectedCertificateText;
@BindView(R.id.trust_anchor_select_key_layout) LinearLayout selectKeyLayout;
@BindView(R.id.button_select_key) Button selectKey;
@BindView(R.id.trust_anchor_selected_key_text) TextView selectKeyText;
@BindView(R.id.radio_root_certificate) RadioButton rootRadioButton;
@BindView(R.id.radio_intermediate_certificate) RadioButton intermediateRadioButton;
@BindView(R.id.radio_end_entity_certificate) RadioButton endEntityRadioButton;
@OnClick({ R.id.radio_root_certificate, R.id.radio_intermediate_certificate, R.id.radio_end_entity_certificate})
public void onRadioButtonClicked(RadioButton rb) {
// Is the button now checked?
boolean checked = rb.isChecked();
// Check which radio button was clicked
switch (rb.getId()) {
case R.id.radio_root_certificate:
if (checked) {
selectEndEntityLayout.setVisibility(View.GONE);
selectKeyLayout.setVisibility(View.GONE);
}
break;
case R.id.radio_intermediate_certificate:
if (checked) {
selectEndEntityLayout.setVisibility(View.VISIBLE);
selectKeyLayout.setVisibility(View.GONE);
}
break;
case R.id.radio_end_entity_certificate:
if (checked) {
selectEndEntityLayout.setVisibility(View.GONE);
selectKeyLayout.setVisibility(View.VISIBLE);
}
break;
}
}
private static final int READ_REQUEST_CODE = 42;
@OnClick({ R.id.button_select_certificate, R.id.button_select_key })
public void onSelectFileClicked(Button button) {
if (button.getId() == R.id.button_select_certificate) {
isForCert = true;
isForKey = false;
} else if (button.getId() == R.id.button_select_key) {
isForCert = false;
isForKey = true;
}
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
startActivityForResult(intent, READ_REQUEST_CODE);
}
private boolean isForCert = false;
private boolean isForKey = false;
private InputStream fileIs;
private InputStream keyIs;
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
// The ACTION_OPEN_DOCUMENT intent was sent with the request code
// READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
// response to some other intent, and the code below shouldn't run at all.
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
// The document selected by the user won't be returned in the intent.
// Instead, a URI to that document will be contained in the return intent
// provided to this method as a parameter.
// Pull that URI using resultData.getData().
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
try {
InputStream is = getContentResolver().openInputStream(uri);
if (isForCert) {
fileIs = is;
isForCert = false;
selectedCertificateText.setText(uri.getPath());
} else if (isForKey) {
keyIs = is;
isForKey = false;
selectKeyText.setText(uri.getPath());
}
} catch (Exception e) {
int errorId = R.string.trust_anchor_create_error;
Toast.makeText(this, errorId, Toast.LENGTH_SHORT).show();
}
}
}
}
@OnClick(R.id.floating_button_cert_save)
protected void onSavePressed() {
if (rootRadioButton.isChecked()) {
mViewModel.saveTrustAnchor(fileIs);
} else if (intermediateRadioButton.isChecked()) {
if (selectEndEntityCert.getSelectedItem() != null) {
int index = selectEndEntityCert.getSelectedItemPosition();
Integer credid = (Integer)selectEndEntityCert.getAdapter().getItem(index);
mViewModel.saveIntermediateCertificate(credid, fileIs);
}
} else if (endEntityRadioButton.isChecked()) {
mViewModel.saveEndEntityCertificate(fileIs, keyIs);
}
}
private CertificateViewModel mViewModel;
private ArrayList<Integer> mCertList;
private ArrayAdapter<Integer> mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_certificate);
Intent intent = getIntent();
mCertList = (ArrayList<Integer>) intent.getSerializableExtra("certs");
ButterKnife.bind(this);
initViews();
initViewModel();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
private void initViews() {
setSupportActionBar(mToolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}
selectedCertificateText.setText(R.string.trust_anchor_no_selected_certificate_text);