Android: descifra texto RSA utilizando una clave pública almacenada en un archivo

He estado varios días tratando de hacerlo sin éxito.

Hay un montón de preguntas similares aquí en StackOverflow e incluso dos de ellos son exactamente lo mismo que el mío pero sin respuesta y sin resolver: 1) Convertir PHP RSA PublicKey en Android PublicKey 2) Android: cómo descifrar un archivo cifrado openssl con clave RSA?

Mi escenario: Tengo texto cifrado usando RSA (no cifrado por mí). Tengo un archivo "public.key" en mi carpeta res / raw con la clave pública necesaria para descifrarla (la clave pública relacionada con la clave privada usada para cifrar el mensaje), con un formato como el siguiente ejemplo: Introduzca aquí la descripción de la imagen

Veo muchos ejemplos de cómo descifrar un texto RSA, como el siguiente:

public static byte[] decryptRSA( PublicKey key, byte[] text) throws Exception { byte[] dectyptedText = null; Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, key); dectyptedText = cipher.doFinal(text); return dectyptedText; } 

Pero mi pregunta es, ¿cómo obtener la instancia adecuada PublicKey del archivo? No hay ejemplos de esto.

Si simplemente intento:

  InputStream is = getResources().openRawResource(R.raw.public); DataInputStream dis = new DataInputStream(is); byte [] keyBytes = new byte [(int) is.available()]; dis.readFully(keyBytes); dis.close(); X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(spec); 

Recibo una InvalidKeyException en la oración de retorno. ¿Tendría que decodificar Hex o Base64? ¿No es la primera y última línea del archivo de clave pública un problema (los que tienen "—- BEGIN PUBLIC KEY —-" y así)?

Tal vez podríamos obtener la respuesta de esto correctamente por primera vez en StackOverflow 🙂

Finalmente resuelto !!! Tambores, trompetas y una sinfonía de sonidos encantadores !!!

 public static byte[] decryptRSA(Context mContext, byte[] message) throws Exception { // reads the public key stored in a file InputStream is = mContext.getResources().openRawResource(R.raw.sm_public); BufferedReader br = new BufferedReader(new InputStreamReader(is)); List<String> lines = new ArrayList<String>(); String line = null; while ((line = br.readLine()) != null) lines.add(line); // removes the first and last lines of the file (comments) if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size()-1).startsWith("-----")) { lines.remove(0); lines.remove(lines.size()-1); } // concats the remaining lines to a single String StringBuilder sb = new StringBuilder(); for (String aLine: lines) sb.append(aLine); String keyString = sb.toString(); Log.d("log", "keyString:"+keyString); // converts the String to a PublicKey instance byte[] keyBytes = Base64.decodeBase64(keyString.getBytes("utf-8")); X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey key = keyFactory.generatePublic(spec); // decrypts the message byte[] dectyptedText = null; Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, key); dectyptedText = cipher.doFinal(Base64.decodeBase64(message)); return dectyptedText; } 

La solución era descodificar Base64 no sólo la clave pública leer desde el archivo, sino también el propio mensaje encriptado!

Por cierto, leí la clave pública del archivo de la manera @ Nikolay sugirió (tnx de nuevo hombre).

Muchas gracias a todos por su ayuda. ¡Rocas de StackOverflow!

Falta un punto clave: las claves públicas y privadas están separadas y no se puede calcular una basada en la otra. Ese es el tipo de encriptación de clave pública. Problemas con el uso de RSA sin procesar a un lado, si tiene algo cifrado con la clave pública, necesita tener la clave privada correspondiente para descifrarlo. Y viceversa. Así que si tiene archivos de clave pública, sólo puede obtener una clave pública de ella. Esto sólo sería útil si los datos se cifraron con la clave privada correspondiente.

En cuanto a la excepción real: quite las líneas '—' al principio y al final, use Base64.decode () para obtener una matriz de bytes y use esto para crear su X509EncodedKeySpec . Una forma de hacerlo – usar algo como un BufferedReader para leer línea por línea, ignorar las líneas '—' y concat el resto en una String grande.

Las claves públicas sólo pueden cifrar datos. Las claves públicas no pueden descifrar datos. Sólo puede descifrar los datos con la clave privada. Todo el punto es que puede entregar la clave pública a todos y cada uno y pueden enviar mensajes cifrados que sólo el titular de la clave privada puede ver.

Usted realmente necesita ser muy cuidadoso con el uso de la tecnología de cifrado. Me temo que simplemente va a distribuir la clave privada a todos sus dispositivos que debilitarán su seguridad ya que todos tendrán la misma clave privada. Así que si quiero romper tu seguridad, solo iré a google play y descargaré tu aplicación y sacaré la clave privada de tu aplicación. Viola Puedo verlo todo.

Así que usted tiene su respuesta por qué no va a funcionar, pero usted necesita asesoramiento ahora sobre el diseño que no puedo darle con saber por qué está utilizando el cifrado. ¿Qué estás escondiendo?

Actualizar:

Suena como si estuviera tratando de realizar el cifrado Y verificación de firma como la forma en que funciona RSA, pero está confundido cómo funciona realmente. Para eso necesitas DOS conjuntos de claves privadas / públicas. Un conjunto de claves para el cliente y un conjunto de claves para el servidor.

El servidor web enviaría su clave pública al cliente. El cliente podría enviar un mensaje autenticado y encriptado al servidor utilizando la clave pública del servidor y luego firmar ese mensaje utilizando la clave privada del cliente. Y viceversa para el servidor. El servidor utilizará la clave pública del cliente para cifrar un mensaje y firmarlo con su clave privada para enviar un mensaje al cliente. El cliente podría descifrar el mensaje con la clave privada del cliente y verificar la firma utilizando la clave pública del servidor.

¿Ahora está reimplementando SSL? Para. Utilice SSL.

Así es como SSL logra un canal seguro. El cliente recibe la clave PUBLIC del servidor web y 1 o más nombres para los algoritmos de cifrado simétrico. Elige un algoritmo que comparten en común, luego genera una clave secreta para usar en todos los mensajes que vayan adelante. ENCRYPTS esa clave secreta con la clave pública del servidor web y envía eso junto con el algoritmo que seleccionó. El servidor web DECRYPTS usando la tecla PRIVATE para obtener la clave secreta compartida. Después de que todo el cifrado es cifrado simétrico utilizando el secreto compartido que es mucho más rápido que el cifrado asimétrico.

Para generar una clave pública RSA desde un formato PEM como el proporcionado (openssl gen. Rsa key)

 -----BEGIN PUBLIC KEY----- SOMEDES3UNREADABLETEXT+PADDING== -----END PUBLIC KEY 

Y usarlo para leer algún contenido firmado con él?

– echa un vistazo a mi respuesta en una pregunta similar aquí: https://stackoverflow.com/a/12101100/546054

  • Proporcionar autenticación segura de Facebook con mi servidor
  • Clave de String en Java RSA
  • Cargar datos de clave pública del archivo
  • PEM a PublicKey en Android
  • Encriptación RSA en Android
  • Práctica recomendada para descifrar archivos grandes con menos memoria
  • Compartir y almacenar RSA - clave pública en el servidor java y viceversa
  • ¿Cómo crear una llave RSA keystore Android con una validez infinita?
  • Cómo almacenar la contraseña en Android
  • SHA256 con firma RSA devuelve salidas diferentes en varios dispositivos Android
  • Descodificación de cifrado RSA en Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.