Android verifica la firma del archivo con la clave pública.der
sign view android (1)
Estoy intentando verificar la firma de un archivo. Seguí estas instrucciones para generar un certificado:
// generate a private key with size of 2048 bits
openssl genrsa -out private_key.pem 2048
// derive a public key from the above private key
openssl rsa -in private_key.pem -out public_key.pem -outform PEM -pubout
// iOS will not import PEM encoded data so it needs to be converted to DER encoded data
openssl rsa -pubin -inform PEM -outform DER -in public_key.pem -out public_key.der
// generate a self-signed certificate for testing
openssl req -new -x509 -key private_key.pem -out test_cert.pem -days 1095
// show the content of the original certificate
openssl x509 -in test_cert.pem -text -noout
// convert the certificate to DER format
openssl x509 -in test_cert.pem -outform der -out test_cert.der
// show the content of the new certificate
openssl x509 -in test_cert.der -inform der -text -noout
Usé el ejemplo en este enlace para copiar algunos datos y verificar que todo estaba bien (hice los pasos 2 a 5).
Ahora estoy tratando de poner el archivo .der, el archivo de firma y el archivo de datos en una aplicación de Android y, básicamente, verificar que todo esté bien de nuevo. No recibo ningún error, pero no estoy obteniendo ningún error. A continuación se muestra el código que he escrito:
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Base64;
import android.util.Log;
import android.widget.TextView;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.Certificate;
// Most of the below is taken from:
// http://www.herongyang.com/JDK/Digital-Signature-JcaVerify-Signature-Verification-Program.html
public class MyActivity extends Activity {
String input = Environment.getExternalStorageDirectory() + "/data.txt";
String signFile = Environment.getExternalStorageDirectory() + "/signature";
String signAlgo = "SHA1withRSA";
int keyFile = R.raw.test_cert_josh;
String TAG = "VERIFY";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView textView = (TextView) findViewById(R.id.textView);
try {
PublicKey pubKey = readPublicKey(keyFile);
byte[] sign = readSignature(signFile);
textView.setText(verify(input, signAlgo, sign, pubKey) + "");
} catch (Exception e) {
e.printStackTrace();
}
}
PublicKey readPublicKey(int cert_id) throws Exception {
InputStream in = MyActivity.this.getResources().openRawResource(cert_id);
byte[] buff = new byte[4000];
int bytesRead;
ByteArrayOutputStream out = new ByteArrayOutputStream();
while((bytesRead = in.read(buff)) != -1) {
out.write(buff, 0, bytesRead);
Log.i(TAG, "bytes read: " + bytesRead);
}
byte[] publicKeyBytes = out.toByteArray();
CertificateFactory cf = CertificateFactory.getInstance("X509");
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(publicKeyBytes));
PublicKey pubKey = cert.getPublicKey();
Log.i(TAG, "Public Key Info: ");
Log.i(TAG, "Algorithm = " + pubKey.getAlgorithm());
Log.i(TAG, "toString = " + pubKey.toString());
return pubKey;
}
byte[] readSignature(String input) throws Exception {
FileInputStream signStream = new FileInputStream(input);
byte[] signBytes = new byte[signStream.available()];
signStream.read(signBytes);
signStream.close();
return signBytes;
}
boolean verify(String input, String algorithm, byte[] sign, PublicKey pubKey) throws Exception {
Signature sg = Signature.getInstance(algorithm);
sg.initVerify(pubKey);
Log.i(TAG, "Signature Object Info: ");
Log.i(TAG, "Algorithm = "+sg.getAlgorithm());
Log.i(TAG, "Provider = "+sg.getProvider());
FileInputStream in = new FileInputStream(input);
byte[] buff = new byte[in.available()];
in.read(buff);
sg.update(buff);
boolean ok = sg.verify(sign);
Log.i(TAG, "Verify Processing Info: ");
Log.i(TAG, "Verification result = "+ok);
return ok;
}
}
Todo lo que he leído dice que mi código es correcto. Creo que la parte sobre sacar la clave pública es correcta porque el módulo coincide con el del archivo .der real.
¡Por favor ayuda!
EDITAR: Creo que tiene que ver con cómo estoy leyendo el archivo de firma o el archivo de datos. Estoy bastante seguro de que estoy obteniendo todo sobre la clave pública correctamente porque imprimí información adicional al respecto y coincide a la salida de openssl
EDIT por sugerencia de Nikolay a continuación, traté de firmar los mismos datos dentro de la aplicación de Android que estaba iniciando sesión en openssl usando este código:
// testing code to sign it myself
// read the data file in
FileInputStream dataStream = new FileInputStream(input);
byte[] dataBytes = new byte[dataStream.available()];
dataStream.read(dataBytes);
dataStream.close();
// hash the data file, like i do with openssl
// per Nikolay''s comments, this is not needed
MessageDigest digest = MessageDigest.getInstance("SHA-1");
// digest.update(dataBytes, 0, dataBytes.length);
// dataBytes = digest.digest();
Log.i(TAG, "data from file: " + new String(dataBytes));
// read the private key in
FileInputStream fis = new FileInputStream(Environment.getExternalStorageDirectory() + "/phaero/private_key.pem");
byte[] keyBytes = new byte[fis.available()];
fis.read(keyBytes);
fis.close();
// clean up the private key and decode it
String temp = new String(keyBytes);
Log.i(TAG, "private key: " + temp);
String privKeyPEM = temp.replace("-----BEGIN RSA PRIVATE KEY-----/n", "");
privKeyPEM = privKeyPEM.replace("-----END RSA PRIVATE KEY-----", "");
byte[] decoded = Base64.decode(privKeyPEM.getBytes(), Base64.DEFAULT);
// create the private key object from the private key data
PKCS8EncodedKeySpec private_spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(private_spec);
// set up for signing
Signature signer = Signature.getInstance(signAlgo);
signer.initSign(privateKey);
signer.update(dataBytes, 0, dataBytes.length);
// sign, and hash again so i can compare against openssl output
byte[] signed = signer.sign();
digest.update(signed, 0, signed.length);
Log.i(TAG, new BigInteger(1, digest.digest()).toString(16));
Nota rápida: utilizando el código anterior en Jelly Bean, debe especificar "BC" al crear KeyFactory.
Luego compré el resultado de la firma para ver si coincidía con el hash del archivo de firma generado con openssl, y no coinciden ... ¡así que no sé lo que eso significa, pero sé que significa algo! ¿Asumo que necesito especificar algún tipo de opción adicional al crear el archivo de firma en openssl?
EDITAR:
según el comentario de Nikolay a continuación, actualicé mi código de prueba para no firmar, pero el hash de la firma creada en java aún no coincide con el hash del archivo de firma creado con openssl ... no estoy seguro de lo que está mal, Java, OpenSSL o yo.
Después de MUCHA ayuda de @NikolayElenkov, finalmente descubrí qué estaba mal. Probé una búsqueda diferente en Google, me encontré con esta pregunta de , donde el tipo dice que hay dos comandos de firma diferentes que puedes ejecutar. Cuando estaba creando toda mi firma, estaba usando las cosas a las que me he vinculado anteriormente:
// create a hash
echo ''data to sign'' > data.txt
openssl dgst -sha1 < data.txt > hash
// sign it
openssl rsautl -sign -inkey private.pem -keyform PEM -in hash > signature
// verify it
openssl rsautl -verify -inkey public.pem -keyform PEM -pubin -in signature > verified
diff -s verified hash
y de la publicación que encontré hoy, probé:
openssl dgst -sha1 -sign privateKey.pem -out signature1 someInputFile
que, como dice el tipo, crea un archivo de firma diferente. ¡Este es el que necesitaba mi código Android! Entonces, la respuesta para verificar esto es que necesito cambiar cómo estaba generando mi archivo de firma. (No hubiera llegado tan lejos sin @NikolayElenkov, ¡muchas gracias!)