it-swarm.com.de

Verwenden von SHA1 und RSA mit Java.security.Signature vs. MessageDigest und Cipher

Ich versuche zu verstehen, was die Java-Klasse Java.security.Signature tut. Wenn ich einen SHA1-Message-Digest berechne und dann diesen Digest mit RSA verschlüssele, erhalte ich ein anderes Ergebnis, als die Klasse Signature zu bitten, dasselbe zu signieren:

// Generate new key
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
String plaintext = "This is the message being signed";

// Compute signature
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(privateKey);
instance.update((plaintext).getBytes());
byte[] signature = instance.sign();

// Compute digest
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
byte[] digest = sha1.digest((plaintext).getBytes());

// Encrypt digest
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] cipherText = cipher.doFinal(digest);

// Display results
System.out.println("Input data: " + plaintext);
System.out.println("Digest: " + bytes2String(digest));
System.out.println("Cipher text: " + bytes2String(cipherText));
System.out.println("Signature: " + bytes2String(signature));

Ergebnisse in (zum Beispiel):

Eingabedaten: Dies ist die Nachricht, die signiert wird
Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067
Chiffretext: 057dc0d2f7f54acc95d3cf5cba9f944619394711003bdd12 ...
Unterschrift: 7177c74bbbb871cc0af92e30d2808ebae146f25d3fd8ba1622 ...

Ich muss ein grundlegendes Missverständnis darüber haben, was Signature tut - ich habe es nachverfolgt, und es scheint, dass es update für ein MessageDigest -Objekt aufruft, wobei der Algorithmus wie erwartet auf SHA1 gesetzt ist. dann den Digest erhalten und dann die Verschlüsselung durchführen. Was unterscheidet die Ergebnisse?

EDIT:

Leonidas ließ mich überprüfen, ob das Signaturschema das tun soll, was ich denke. In RFC sind zwei Arten von Signaturen definiert:

Das erste von diesen (PKCS1) ist das, das ich oben beschreibe. Es verwendet eine Hash-Funktion, um einen Digest zu erstellen, und verschlüsselt dann das Ergebnis mit einem privaten Schlüssel.

Der zweite Algorithmus verwendet einen zufälligen Salt-Wert und ist sicherer, aber nicht deterministisch. Die aus dem obigen Code erzeugte Signatur ändert sich nicht, wenn derselbe Schlüssel wiederholt verwendet wird. Ich glaube nicht, dass es sich um PSS handeln kann.

EDIT:

Hier ist die bytes2string-Methode, die ich verwendet habe:

private static String bytes2String(byte[] bytes) {
    StringBuilder string = new StringBuilder();
    for (byte b : bytes) {
        String hexString = Integer.toHexString(0x00FF & b);
        string.append(hexString.length() == 1 ? "0" + hexString : hexString);
    }
    return string.toString();
}
65
Mike Houston

OK, ich habe herausgefunden, was los ist. Ich war dumm. Leonidas hat recht, nicht nur der Hash wird verschlüsselt, sondern die ID des Hash-Algorithmus, der mit dem Digest verkettet ist:

  DigestInfo ::= SEQUENCE {
      digestAlgorithm AlgorithmIdentifier,
      digest OCTET STRING
  }

Deshalb unterscheiden sie sich.

51
Mike Houston

Um die gleichen Ergebnisse zu erzielen:

MessageDigest sha1 = MessageDigest.getInstance("SHA1", BOUNCY_CASTLE_PROVIDER);
byte[] digest = sha1.digest(content);
DERObjectIdentifier sha1oid_ = new DERObjectIdentifier("1.3.14.3.2.26");

AlgorithmIdentifier sha1aid_ = new AlgorithmIdentifier(sha1oid_, null);
DigestInfo di = new DigestInfo(sha1aid_, digest);

byte[] plainSig = di.getDEREncoded();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", BOUNCY_CASTLE_PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] signature = cipher.doFinal(plainSig);
9
Romulo Pereira

Eine etwas effizientere Version der Methode bytes2String ist

private static final char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static String byteArray2Hex(byte[] bytes) {
    StringBuilder sb = new StringBuilder(bytes.length * 2);
    for (final byte b : bytes) {
        sb.append(hex[(b & 0xF0) >> 4]);
        sb.append(hex[b & 0x0F]);
    }
    return sb.toString();
}
4
magiconair

Ähm, nachdem Sie Ihre Frage verstanden haben: Sind Sie sicher, dass die Signaturmethode nur ein SHA1 erstellt und verschlüsselt? GPG et al bieten an, die Daten zu komprimieren/zu löschen. Möglicherweise erstellt dieses Java-Signaturalg auch eine entfernbare/anfügbare Signatur.

4
Leonidas

Wenn Sie die Antwort von @Mike Houston als Zeiger verwenden, finden Sie hier einen vollständigen Beispielcode, der Signatur, Hash und Verschlüsselung ausführt.

/**
 * @param args
 */
public static void main(String[] args)
{
    try
    {
        boolean useBouncyCastleProvider = false;

        Provider provider = null;
        if (useBouncyCastleProvider)
        {
            provider = new BouncyCastleProvider();
            Security.addProvider(provider);
        }

        String plainText = "This is a plain text!!";

        // KeyPair
        KeyPairGenerator keyPairGenerator = null;
        if (null != provider)
            keyPairGenerator = KeyPairGenerator.getInstance("RSA", provider);
        else
            keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);

        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // Signature
        Signature signatureProvider = null;
        if (null != provider)
            signatureProvider = Signature.getInstance("SHA256WithRSA", provider);
        else
            signatureProvider = Signature.getInstance("SHA256WithRSA");
        signatureProvider.initSign(keyPair.getPrivate());

        signatureProvider.update(plainText.getBytes());
        byte[] signature = signatureProvider.sign();

        System.out.println("Signature Output : ");
        System.out.println("\t" + new String(Base64.encode(signature)));

        // Message Digest
        String hashingAlgorithm = "SHA-256";
        MessageDigest messageDigestProvider = null;
        if (null != provider)
            messageDigestProvider = MessageDigest.getInstance(hashingAlgorithm, provider);
        else
            messageDigestProvider = MessageDigest.getInstance(hashingAlgorithm);
        messageDigestProvider.update(plainText.getBytes());

        byte[] hash = messageDigestProvider.digest();

        DigestAlgorithmIdentifierFinder hashAlgorithmFinder = new DefaultDigestAlgorithmIdentifierFinder();
        AlgorithmIdentifier hashingAlgorithmIdentifier = hashAlgorithmFinder.find(hashingAlgorithm);

        DigestInfo digestInfo = new DigestInfo(hashingAlgorithmIdentifier, hash);
        byte[] hashToEncrypt = digestInfo.getEncoded();

        // Crypto
        // You could also use "RSA/ECB/PKCS1Padding" for both the BC and Sun Providers.
        Cipher encCipher = null;
        if (null != provider)
            encCipher = Cipher.getInstance("RSA/NONE/PKCS1Padding", provider);
        else
            encCipher = Cipher.getInstance("RSA");
        encCipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());

        byte[] encrypted = encCipher.doFinal(hashToEncrypt);

        System.out.println("Hash and Encryption Output : ");
        System.out.println("\t" + new String(Base64.encode(encrypted)));
    }
    catch (Throwable e)
    {
        e.printStackTrace();
    }
}

Sie können BouncyCastle Provider oder Standard-Sun-Provider verwenden.

Ich habe ein ähnliches Problem, ich habe das Hinzufügen von Code getestet und einige interessante Ergebnisse gefunden ... Mit diesem Code, den ich hinzufüge, kann ich daraus ableiten, dass je nach "Provider" die Firma unterschiedlich sein kann. (weil die in der Verschlüsselung enthaltenen Daten nicht bei allen Anbietern gleich sind).

Ergebnisse meines Tests.

Schlussfolgerung.- Signaturentschlüsselung = ??? (Papierkorb) + DigestInfo (wenn wir den Wert von "Papierkorb" kennen, sind die digitalen Signaturen gleich)

IDE Eclipse OUTPUT ...

Eingabedaten: Dies ist die Nachricht, die signiert wird

Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067

DigestInfo: 3021300906052b0e03021a0500041462b0a9ef15461c82766fb5bdaae9edbe4ac2e067

Signature Decipher: 1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a0500041462b0a9ef15461c82766fb5bdaae9edbe4ac2e067

CODE

import Java.security.InvalidKeyException;
import Java.security.KeyPair;
import Java.security.KeyPairGenerator;
import Java.security.MessageDigest;
import Java.security.NoSuchAlgorithmException;
import Java.security.NoSuchProviderException;
import Java.security.PrivateKey;
import Java.security.PublicKey;
import Java.security.Signature;
import Java.security.SignatureException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.bouncycastle.asn1.x509.DigestInfo;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
public class prueba {
/**
* @param args
* @throws NoSuchProviderException 
* @throws NoSuchAlgorithmException 
* @throws InvalidKeyException 
* @throws SignatureException 
* @throws NoSuchPaddingException 
* @throws BadPaddingException 
* @throws IllegalBlockSizeException 
*///
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
// TODO Auto-generated method stub
KeyPair keyPair = KeyPairGenerator.getInstance("RSA","BC").generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey puKey = keyPair.getPublic();
String plaintext = "This is the message being signed";
// Hacer la firma
Signature instance = Signature.getInstance("SHA1withRSA","BC");
instance.initSign(privateKey);
instance.update((plaintext).getBytes());
byte[] signature = instance.sign();
// En dos partes primero hago un Hash
MessageDigest digest = MessageDigest.getInstance("SHA1", "BC");
byte[] hash = digest.digest((plaintext).getBytes());
// El digest es identico a  openssl dgst -sha1 texto.txt
//MessageDigest sha1 = MessageDigest.getInstance("SHA1","BC");
//byte[] digest = sha1.digest((plaintext).getBytes());
AlgorithmIdentifier digestAlgorithm = new AlgorithmIdentifier(new
DERObjectIdentifier("1.3.14.3.2.26"), null);
// create the digest info
DigestInfo di = new DigestInfo(digestAlgorithm, hash);
byte[] digestInfo = di.getDEREncoded();
//Luego cifro el hash
Cipher cipher = Cipher.getInstance("RSA","BC");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] cipherText = cipher.doFinal(digestInfo);
//byte[] cipherText = cipher.doFinal(digest2);
Cipher cipher2 = Cipher.getInstance("RSA","BC");
cipher2.init(Cipher.DECRYPT_MODE, puKey);
byte[] cipherText2 = cipher2.doFinal(signature);
System.out.println("Input data: " + plaintext);
System.out.println("Digest: " + bytes2String(hash));
System.out.println("Signature: " + bytes2String(signature));
System.out.println("Signature2: " + bytes2String(cipherText));
System.out.println("DigestInfo: " + bytes2String(digestInfo));
System.out.println("Signature Decipher: " + bytes2String(cipherText2));
}
0