おじゃまぷよ系エンジニアメモ

主にスマホネイティブ関連Tips。たまにWebも書きたい。お腹はぷよぷよ

queryパラメーターをPHPで暗号化して渡してJavaで復元したい

とあるPHPで書かれたWebサービスAから、とあるJavaで書かれたWebサービスBにパラメーターを暗号化してqueryパラメーターで渡したいときがあったのでその時のメモ

PHP側の暗号・復号化を行うクラス

<?php
class MyCipher {
    const SECRET_KEY = "1234567890123456"; //Javaと共通の秘密鍵。ランダウな文字列16桁
    const IV_PARAMETER = "1234567890123456"; //Javaと共通のIV。ランダムな文字列16桁
    const METHOD = "AES-128-CBC"; //256のほうが安全だが、Java側がデフォルトで256使えないので128にしてる

    /**
     * 受け取った文字列を暗号化しBase64+URLエンコードして返す
     * @param $originalStr
     * @return string
     */
    public static function encrypt($originalStr) {
        return urlencode(openssl_encrypt($originalStr, self::METHOD, self::SECRET_KEY, false, self::IV_PARAMETER));
    }

    /**
     * Base64+URLEncodeされている暗号化文字列を元の文字列に復号する
     * @param $encryptStr
     * @return string
     */
    public static function decrypt($encryptStr){
        return openssl_decrypt(urldecode($encryptStr), self::METHOD, self::SECRET_KEY, false, self::IV_PARAMETER);
    }
}

$encrypt = MyCipher::encrypt("abcdef"); //JxdRLkrOq5SDY9YMcUSUhg%3D%3D
$decrypt = MyCipher::decrypt($encrypt); //abcdef
echo $decrypt === "abcdef"; //復号される

Javaの暗号・復号化を行うクラス

public final class MyCipher {

    private static final String SECRET_KEY = "1234567890123456";// 秘密鍵は16文字で。PHP側と同じものを指定する
    private static final byte[] IV_Parameter = "1234567890123456".getBytes();//IVの値。PHP側と同じものを指定する
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";// 暗号化方式

    /**
     * 文字列を16文字の秘密鍵でAES暗号化してBase64+URLEncodeした文字列で返す
     */
    public static String encrypt(String originalSource) throws Exception {
        Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);
        //文字列をバイト列に変換
        byte[] originalBytes = originalSource.getBytes();
        //暗号化後のバイト列取得
        byte[] encryptBytes = cipher.doFinal(originalBytes);
        //バイト列を文字列にして返す
        return URLEncoder.encode(Base64.getEncoder().encodeToString(encryptBytes),"UTF-8");
    }

    /**
     * Base64+URLEncodeされたAES暗号化文字列を元の文字列に復元する
     */
    public static String decrypt(String encryptStr) throws Exception {

        //暗号化されたmd文字列からバイト列を取得
        //JavaのUrlDecoderは「+」を半角スペースに変えてしまうので、「%2B」に置換する
        byte[] encryptBytes = Base64.getDecoder().decode(URLDecoder.decode(encryptStr.replace("+", "%2B"),"UTF-8"));
        Cipher cipher = initCipher(Cipher.DECRYPT_MODE);
        //復号化後にバイト列取得
        byte[] originalBytes = cipher.doFinal(encryptBytes);
        //文字列にして返却
        return new String(originalBytes);
    }

    private static Cipher initCipher(int method) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(method,new SecretKeySpec(SECRET_KEY.getBytes(),"AES"),new IvParameterSpec(IV_Parameter));
        return cipher;
    }
}
public class Sample {
    public static void main(String[] args) throws Exception {
        String encrypt = MyCipher.encrypt("abcdef");
        System.out.println(encrypt); //JxdRLkrOq5SDY9YMcUSUhg%3D%3D
        System.out.println(MyCipher.decrypt(encrypt)); //abcdef
        //復号化されてる
    }
}

これでPHPJavaで相互に値を受け渡しできるようになりました。 秘密鍵と初期ベクトルが外部に漏れない限りは復号できないと思われるので、まぁ十分に安全かと思います。
ただ不安はつきものなのであまり重要なデータは別の方法で受け渡ししたほうが良いかもしれない。

参考URL : stackoverflow.com

http://itmemo.net-luck.com/java-aes/

日々是妄想 : 初期化ベクトルってなに?(解決編)

kotlinでワンソースのDIコンテナ試しに書いた

やっと仕事落ち着いてきたので軽く何かkotlin書きたいなあと思ってモヤモヤして、そういえば以前からDagger2も取り入れてAndroidアプリ開発したいなぁと思っていたが
なんかDagger2の使い方がややこしくて、逆に複雑度が増す感じがして個人で小規模な開発には正直メリットを見いだせなかった。純粋に勉強不足というものもあるがもっとゆるーいDIコンテナ欲しいなと考えたり他のDIコンテナってどんな感じなんだろうと思って色々調べてるうちに、PHPの軽量なDIコンテナのPimpleに出会った
なるほど……オブジェクトを生成する関数をDIコンテナに配列で保存しておいて必要になったらそのコンテナに保存しておいた関数でオブジェクトを生成するという感じか
これぐらいゆるい感じで軽量なら同じようなことがkotlinでもできそうだなと思い立ってとりあえず書いた

GitHub - masahide318/Kontainer

koRegister(String,()->Any)でオブジェクトの生成方法を保存しておき、koInject(String)でオブジェクトを生成して返す koInjectSingleton(String)で取得すれば、singletonとしてオブジェクトを取得できる 使い方はReadmeにも書いてある通り以下の様な感じ

class SampleObj(val id: Int, val name: String) {
}

koRegister("sample",{SampleObj(1,"hoge")})

//inject with new Object
val sampleObj1:SampleObj = koInject("sample")
val sampleObj2:SampleObj = koInject("sample")
//別のインタンスとして取得できる
println(sampleObj1 === sampleObj2) //false

//inject with singleton
val singleton1:SampleObj = koInjectSingleton("sample")
val singleton2:SampleObj = koInjectSingleton("sample")
//シングルトンとして同一オブジェクトが帰る
println(singleton1 === singleton2) // true

Androidの開発で果たして使えるのか、恐らくApplicationクラスのonCreateでサービスクラス系をこいつにぶち込む感じになるんだろうが
まぁ試しに使って見ながら余裕があれば色々改良してみよう

knockout.jsでajaxでAPI叩いてモーダル表示分け

とあるqueryパラメーターによって表示するモーダルの内容を変えるのをどうしようかと考えた結果
jQueryajaxAPIを叩いて、受け取ったレスポンスをKnockout.jsを使ってデータバインディングする形に落ち着いた。 モーダルを1つふやすのに、api,js,htmlの3つのファイル修正加えないといけないけど、基本的にif文なり加えて増やせばいいだけなので
そんなに複雑にならないであろうと思っている。 表示、非表示をもう少しうまく切り替えたいけど…

続きを読む

DialogFragmentのshowとdismiss方法はコレに落ち着いた

ちょっと仕事でアプリのクラッシュログがすごい量出てきて調べてほしいと言われてとりあえずFabricのスタックトレースをみてみたら
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState」で落ちまくっている。なんとなく予想はしていたが Fragmentの貼り付けているWebViewのonPageStartedでDialogFragmentをshow()して、onPageFinished()でdismiss()している箇所があり
ここの部分が半端無く落ちてる。まぁそうだろうな…WebViewのロードが終わる前に画面回転とかしたらこのExceptionが発生してそのまま即死コース一直線だ

続きを読む

kotlinでAndroidアプリを試しに作った

よくお世話になるクローディアのアプリのAndroid版をkotlinで書き直し中 最近あまりネイティブアプリを書くことがなくなってきたのでリハビリを兼ねて作ってみました。

とにかく最近のトレンドを追いかけるために

  • retrofit
  • databinding
  • realm
  • rxJava

この辺りは入れてみようと思いましたが、realmだけまだ入れてない。。。
Dagger2もまだ使ったことないので使ってみたかったが後回し
設計もいまトレンドのMVPでActivityを極力Viewの操作のみに専念し
PresenterがRepository層のクラスを使ってAPI通信を行ったり、PreferenceFileに書き込みを行ったりということをしています。少々雑ですが…
更に細かくやると、UseCase層も作ってそこにRepositoryとのやりとりをまとめたほうが抽象度は上がるのでしょうが、個人でやるにはそこまではやりすぎ感があったので
Presenterが色んなRepositoryとやりとりして結果をViewに知らせる形にしました。
テストコードもビジネスロジックはPresenter側に押し込めるのでPresenterを抑えておけばとりあえず大丈夫なはず。
View側のテストはInstrumentテストで頑張ればいいかなぁという感じ。
RepositoryとViewはInterfaceで実装を抽出してPresenterに渡しているのでmockを差し込むのもやりやすくて良かったです。

続きを読む

Androidのアレ!iOSでいうと

メモ書き程度のもの そのうち追加したり、もう少し詳しく書く

画面遷移(startActivity)

Android

Intent intent = new Intent(this,NextActivity.class);
startActivity(intent);

iOS

stroyboardでViewControllerを「Control + ドラッグ」でつなげて SegueのIdentifierを定義する「toNextViewController」

self.performSegueWithIdentifier("toNextViewController",sender:nil)

画面遷移with値(intent.putExtra)

Android

Intent intent = new Intent(this,NextActivity.class);
intent.putExtra("param","hoge");
startActivity(intent);

//NextActivityで
getIntent().getExtras().getString("param") //hoge

iOS

prepareForSegueを使います

class NextViewController:UIViewController{
    var value1:Int = 0
    var value2:String = ""
}
func hoge(){
    self.performSegueWithIdentifier("toNextViewController",sender:nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        let nextViewController = segue.destinationViewController as! NextViewController
        nextViewController.value1 = 123
        nextViewController.value2 = "hogehoge"
}

画面から戻ってきたとき(onActivityResult)

Android

public class FugaActivity extends Activity {

    private static final int REQUEST_CODE = 1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this,NextActivity.class);
        startActivityForResult(intent, REQUEST_CODE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode == RESULT_OK && requestCode == REQUEST_CODE){
            //doSomething
        }
    }
}
        

iOS

StoryBorad Unwind segue でググる

pull refresh

Android

AndroidではSwipeRefreshLayoutの中にListViewを入れるのが一般的です

http://developer.android.com/intl/ja/samples/SwipeRefreshListFragment/index.html より

<android.support.v4.widget.SwipeRefreshLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/swiperefresh"
      android:layout_width="match_parent"
      android:layout_height="match_parent">

    <ListView
          android:id="@android:id/list"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />

</android.support.v4.widget.SwipeRefreshLayout>
mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swiperefresh);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
        //必要に応じてtrue,falseを設定してあげる
        mSwipeRefreshLayout.setRefreshing(true);
        //何かしらの処理。多くの場合はAPI叩く
    }
});

iOS

tableViewControllerを使わない場合

@IBOutlet weak var tableView: UITableView!
let refreshControl:UIRefreshControl = UIRefreshControl()
override func viewDidLoad() {
    super.viewDidLoad() 
    //pull to refreshで実行するメソッド名を指定します
    refreshControl.addTarget(self, action: Selector("refresh"), forControlEvents: UIControlEvents.ValueChanged)
    tableView.addSubview(refreshControl)
}

func refresh(){
//doSomething
self.refreshControl.endRefreshing()
}

tableViewControllerを使っている場合 storyboardからTableViewのRefreshを「enable」にして

override func viewDidLoad() {
    super.viewDidLoad() 
    self.refreshControl?.addTarget(self, action: "refresh:", forControlEvents: UIControlEvents.ValueChanged)
}

func refresh(){
//doSomething
self.refreshControl.endRefreshing()
}

これでいける模様

バックグラウンドから復帰したとき

Android

onResumeやonRestartなど、その時の用途にあったライフサイクルを選ぶ

iOS

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/

override func viewDidLoad() {
    let notificationCenter = NSNotificationCenter.defaultCenter()
    //アプリがアクティブになったとき
    notificationCenter.addObserver(
            self,
            selector: "doHoge",
            name:UIApplicationDidBecomeActiveNotification,
            object: nil)
}
func doHoge(){
    //doSomething
}

そのほかにバックグラウンドに移行したときなど、notificaitonのtypeがいろいろあります

リストビューの最下部までスクロールしたときをハンドリング

Android

listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (totalItemCount == firstVisibleItem + visibleItemCount) {
            //doSomething
        }
    }
});

iOS

func scrollViewDidScroll(scrollView: UIScrollView) {
   //一番下までスクロールしたかどうか
   if(tableView.contentOffset.y >= (tableView.contentSize.height - tableView.bounds.size.height) && !nowLoading)
    {
        //doSomething
    }
}

アップルの審査との100日戦争

CroudiaというマイナーSNSiOSアプリを作りました。処女作です。
処女作という響きにはロマンが詰まってますね?
初回リリースまでにおよそ8回リジェクトされ、初申請からリリースまで3ヶ月近くかかりました。

ちなみにアプリはこちら

Icros

Icros

  • Masahide Takahata
  • Social Networking
  • Free

その戦いの記録を振り返ってまとめてみます。
ただ、問題解決センターで実際にやりとりした内容が見えなくなってしまったので思い出しながら書くので 多少情報が不明瞭な部分もありますが悪しからず……

続きを読む