読者です 読者をやめる 読者になる 読者になる

AndroidのCipher.java問題

こんにちは丸山@です

最近AndroidのCipher.javaで問題に遭遇して解決したので、そのメモです。

問題

AndroidでCipher.javaを使ってデータを暗号化するときに暗号化処理方式として[AES/CTR/PKCS5Padding]を使用するとAndroid4.4とそれ以外のAndroid4.x/5.0で互換性がありません。Android4.2 → Android4.4やAndroid4.4 → Android5.0というOSアップデートを行うとデータを復号できなくなってしまいます。

これはAndroid4.4とそれ以外のAndroid4.x/5.0では[AES/CTR/PKCS5Padding]という暗号化方式に互換性がないからです。実はJavaでは暗号化方式の指定以外にもSecurityProviderというものも指定できます。これはJavaでは暗号化処理の実装を複数もっており、そのうちのどれを使うかということを指定するものです。このSecurityProviderを指定しない場合、自動的にシステムがSecurityProviderを決定します。この時にAndroid4.4とそれ以外では使用されるSecurityProviderが異なります。よって暗号化されたデータをOSをまたぐと復号できなくなってしまいます。

SecurityProviderを指定せずに[AES/CTR/PKCS5Padding]を使用する場合

  • Android4.0: BC Provider
  • Android4.1: BC Provider
  • Android4.2: BC Provider
  • Android4.3: 未確認
  • Android4.4: AndroidOpenSSL Provider
  • Android5.0: BC Provider

という結果になりました。つまり[AES/CTR/PKCS5Padding BC][AES/CTR/PKCS5Padding AndroidOpenSSL]が使われます。そしてこの2つの暗号化処理に互換性がなく、一方で暗号化したデータをもう一方で復号化することができませんでした。

何故互換性が無いかというとCTRというのはストリームモードで暗号化するという指定なのですが、この場合パディングは本来不要です。ですがPKCS5Paddingという方式を指定したことで、BCとAndroidOpenSSLでパディングの扱いに違いで出てしまい(一方はパディングを無視し、一方はパディングを行った)、結果として互換性がなくなってしまいました(と理解していますが間違っていたらごめんなさい)。

解決

まず[AES/CTR/PKCS5Padding BC]で復号してみて、できなかった場合は[AES/CTR/NoPadding AndroidOpenSSL]で復号するという二段階で乗り切ることにしました。

ここでなぜ[AES/CTR/PKCS5Padding AndroidOpenSSL]ではなく[AES/CTR/NoPadding AndroidOpenSSL]を使用したかというと、Android4.4以外では[AES/CTR/PKCS5Padding AndroidOpenSSL]という処理方式を使えないからです。

もう1点ポイントが有り、かならず[AES/CTR/PKCS5Padding BC]から試行するということです。何故かと言うと[AES/CTR/PKCS5Padding BC]で暗号化したデータは[AES/CTR/NoPadding AndroidOpenSSL]で復号できてしまうのです。しかし、複合されたデータは不正な値になっています(パッディングの残骸?)。つまり[AES/CTR/NoPadding AndroidOpenSSL]を先に試行してしまうと、不正な値として復号されてしまいます。


それぞれの処理方式の関係をまとめると

  • [AES/CTR/PKCS5Padding AndroidOpenSSL]のデータを[AES/CTR/PKCS5Padding BC]で復号すると例外がでる
  • [AES/CST/NoPadding AndroidOpenSSL]のデータは[AES/CTR/PKCS5Padding BC]で復号すると例外がでる
  • [AES/CTR/PKCS5Padding AndroidOpenSSL]のデータは[AES/CTR/NoPadding AndroidOpenSSL]で復号することができる
  • [AES/CTR/PKCS5Padding BC]のデータは[AES/CST/NoPadding AndroidOpenSSL]で復号すると例外はでないが、不正な値が取得されてしまう

暗号処理難しい。