it-swarm-tr.com

SHA1 ve RSA'yı Java.security.Signature vs. MessageDigest ve Cipher ile Kullanma

Java Java.security.Signature sınıfının ne yaptığını anlamaya çalışıyorum. Bir SHA1 ileti özeti hesaplar ve RSA kullanarak bu özeti şifrelersem, İmza sınıfından aynı şeyi imzalamasını istemek için farklı bir sonuç alırım:

// 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));

İçindeki sonuçlar (örneğin):

Giriş verileri: Bu, imzalanan mesajdır.
Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067
Şifre metni: 057dc0d2f7f54acc95d3cf5cba9f944619394711003bdd12 ...
İmza: 7177c74bbbb871cc0af92e30d2808ebae146f25d3fd8ba1622 ...

İmza 'ın ne yaptığını temel bir yanlış anlama olmalı - bunun üzerinden geçtim ve algoritma SHA1 olarak ayarlanmış bir MessageDigest nesnesinde güncelleme olarak görünüyor Beklerdim, sonra özeti alır, sonra şifrelemeyi yapardım. Sonuçları farklı kılan nedir?

DÜZENLE:

Leonidas, imza planının düşündüğüm şeyi yapması gerekip gerekmediğini kontrol etmemi sağladı. RFC 'de tanımlanan iki tür imza vardır:

Bunlardan ilki (PKCS1) yukarıda tanımladığım şeydir. Özet oluşturmak için bir karma işlevi kullanır ve sonucu özel bir anahtarla şifreler.

İkinci algoritma rasgele bir tuz değeri kullanır ve daha güvenlidir ancak deterministik değildir. Aynı kodun tekrar tekrar kullanılması durumunda yukarıdaki koddan üretilen imza değişmez, bu yüzden PSS olabileceğini sanmıyorum.

DÜZENLE:

İşte kullandığım bytes2string yöntemi:

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

Tamam, neler olduğunu araştırdım. Aptallık yapıyordum. Leonidas haklı, sadece şifreli olan karma değil, özümsemeyle birleştirilen karma algoritmasının kimliği:

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

Bu yüzden farklılar.

51
Mike Houston

Aynı sonuçları üretmek için:

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

Bytes2String yönteminin biraz daha verimli bir sürümü

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

Erm, sorunuzu anladıktan sonra: imza yönteminin yalnızca bir SHA1 oluşturup şifrelendiğinden emin misiniz? GPG ve diğerleri verileri sıkıştırmayı/silmeyi teklif ediyor. Belki bu Java imzası-alg ayrıca ayrılabilir/takılabilir bir imza oluşturur.

4
Leonidas

@Mike Houston'ın cevabını işaretçi olarak alan, işte İmza, Hash ve şifreleme yapan tam bir örnek kod.

/**
 * @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();
    }
}

BouncyCastle Provider'ı veya varsayılan Sun Provider'ı kullanabilirsiniz.

Benzer bir problemim var, kod ekleyerek test ettim ve bazı ilginç sonuçlar buldum .. __ Eklenen bu kodla, kullanacağım "sağlayıcıya" bağlı olarak firmanın farklı olabileceğini tahmin edebilirim? (çünkü şifrelemeye dahil edilen veriler her zaman tüm sağlayıcılarda eşit değildir).

Testimin sonuçları.

Sonuç.- İmza Deşifre = ??? (çöp) + DigestInfo ("çöp" değerini biliyorsak, dijital imzalar eşit olacaktır)

IDE Eclipse ÇIKIŞI ...

Giriş verileri: Bu, imzalanan mesajdır.

Digest: 62b0a9ef15461c82766fb5bdaae9edbe4ac2e067

DigestInfo: 3021300906052b0e03021a0500041462b0a9ef15461c82766fb5bdaae9edbe4ac2e067

İmza deşifre: 1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a0500041462b0a9ef15461c82766fb5bdaae9edbe4ac2e067

KOD

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