2011-05-25 5 views
13

Como todo lo que hacemos, tengo la aplicación que está firmada por debug.keystore (de forma predeterminada) cuando está en modo de desarrollo (compilación). Cuando se publica, lo firmamos con nuestra clave privada. ¿Hay alguna manera de determinar en tiempo de ejecución que el paquete actual está firmado con debug.keystore (está en modo de desarrollo) o está firmado con nuestra clave privada (está en modo de producción).Android compare la firma del paquete actual con debug.keystore

He intentado algo así como

PackageManager packageManager = getPackageManager(); 
    try { 
     Signature[] signs = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures; 
     for (Signature signature : signs) { 
      Log.d(TAG, "sign = " + signature.toCharsString()); 
     } 
    } catch (NameNotFoundException e) { 
     e.printStackTrace(); 
    } 

no sé qué hacer a continuación? ¿Es esta la forma correcta de hacer esto? ¿Cómo obtener una firma de debug.keystore comparable?

Sé que existe la huella dactilar MD5 keytool -list -keystore ~/.android/debug.keystore pero en la clase Signature no existe el método "md5 de huella dactilar". Quiero hacer esto debido a MapView Key, Logging, LicenseChecker y cosas como esta.

+0

Hoy en día utilizo 1 valor booleano estático que tengo que cambiar cada vez que hago una versión de producción. Ha sucedido que lo olvidé. :( – zmeda

Respuesta

15

La firma en PackageInfo no parece estar bien nombrada ya que ese campo no contiene la firma del paquete sino la cadena del certificado del firmante X509. Tenga en cuenta que (la mayoría de las veces) esta cadena parece estar limitada a un solo certificado autofirmado.

De acuerdo con la página de desarrolladores de Android Signing Your Applications se genera el certificado de firma de depuración con este DN: CN=Android Debug,O=Android,C=US

Por lo tanto es fácil de probar si la aplicación se ha firmado en el modo de depuración:

private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US"); 
/* ... */ 
Signature raw = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures[0]; 
CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray())); 
boolean debug = cert.getSubjectX500Principal().equals(DEBUG_DN); 
+0

Supongo que 'DEBUG_DN' es tipo de' X509Principal' ¿verdad? – zmeda

+0

'DEBUG_DN' es del tipo' X500Principal'. Que es un contenedor para nombres X.500 – Jcs

+0

es lo suficientemente eficiente para ejecutarse en el onCreate del clase de aplicación? –

3

Based en Jcs' answer, usamos esto para averiguar en tiempo de ejecución quién creó el paquete en ejecución:

private enum BuildSigner { 
    unknown, 
    Joe, 
    Carl, 
    Linda 
} 

private BuildSigner whoBuiltThis() { 
    try { 
     PackageManager packageManager = getPackageManager(); 
     PackageInfo info = packageManager.getPackageInfo(getPackageName(), 
       PackageManager.GET_SIGNATURES); 
     Signature[] signs = info.signatures; 
     CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
     X509Certificate cert = (X509Certificate)cf.generateCertificate(
       new ByteArrayInputStream(signs[0].toByteArray())); 
     PublicKey key = cert.getPublicKey(); 
     int modulusHash = ((RSAPublicKey)key).getModulus().hashCode(); 
     switch (modulusHash) { 
      case 123456789: 
       return BuildSigner.Joe; 
      case 424242424: 
       return BuildSigner.Carl; 
      case -975318462: 
       return BuildSigner.Linda; 
     } 
    } catch (Exception e) { 
    } 

    return BuildSigner.unknown; 
} 

Para cualquier certificado involucrado, solo tiene que encontrar el hash una vez y agregarlo a la lista.

La forma más simple de "encontrar el hash una vez" puede ser simplemente agregar un popup brindis antes de la instrucción switch que muestra modulusHash, compilar su aplicación, ejecutarla, anotar el hash, eliminar el código toast y agregar el hash a la lista.

Alternativamente, cuando he implementado esto, he creado una pequeña aplicación repetitivo con una sola actividad y una sola TextView con el ID de tv en el diseño principal, poner esto en la actividad:

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    int hash = 0; 
    try{ 
     PackageManager packageManager = getPackageManager(); 
     PackageInfo info = packageManager.getPackageInfo(
       "com.stackexchange.marvin", PackageManager.GET_SIGNATURES); 
     Signature[] signs = info.signatures; 
     CertificateFactory cf = CertificateFactory.getInstance("X.509"); 
     X509Certificate cert = (X509Certificate) cf.generateCertificate(
       new ByteArrayInputStream(signs[0].toByteArray())); 
     PublicKey key = cert.getPublicKey(); 
     hash = ((RSAPublicKey) key).getModulus().hashCode(); 
    }catch(Exception e){} 

    TextView tv = ((TextView)findViewById(R.id.tv)); 
    tv.setText("The Stack Exchange app's signature hash is " + hash + "."); 
    tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24); 
} 

(cambiar com.stackexchange.marvin al nombre de su aplicación), compiló esta mini aplicación y envió el APK a todos los desarrolladores involucrados, pidiéndoles que lo ejecuten en su dispositivo dev y me hagan saber el hash que se muestra.

+0

Gracias por la información ... pero ¿cómo encontrar el hash del certificado? ¿Es el hash de la cadena 'clave alias' en el certificado? –

+0

@VijayC He ampliado mi respuesta, vea la versión actualizada. – balpha

Cuestiones relacionadas