#java #hash #signature #digital
Вопрос:
В образовательных целях я хотел бы иметь возможность сначала создать хэш для строки, а затем создать цифровую подпись RSA из этого хэша, чтобы результат был таким же, как при использовании SHA256withRSA за один раз. Таким образом, я хочу подтвердить, что я полностью понимаю все шаги, которые на самом деле выполняются автоматически для нас, когда мы вызываем SHA256withRSA.
У меня также есть один дополнительный вопрос. Выполняется ли цифровая подпись на хэше или на хэше, закодированном в Base64?
Ниже приведен код, который я использую в настоящее время, и вот результат кода, который показывает, что эти два подхода создают разные сигнатуры, что означает, что мне не хватает ручных шагов soma.
AUTOMATICALLY SIGN amp; VERIFY
SIGNATURE = Hj6a86sR2cJoQFolbxj0blk2I9CAdTdx6WOles5t/pyUyJwa9rp2/SRx2wyXWgc6GsvoZYGLUedeJ2Lklm5hYgT/TtNBATk5eChgfkJMz3NBIRPrsl7ZPG7Wvo4VmHsPpoZZ8PdRk8qY9RLou86OyIqRcX62isuV e/0deHJ yTZz4vqA3y PE4yRFp96A8sKw5VlDnByn7bsxM/QOS sQWTsETzU9s4YSRfKNq1Urn8/VDoel7n0ORjR918P 0kwE G77bAOI70yQZorvmbgrMLSBJeVzkKzM/YECLWyrJsqdjfp86FkA9MPGB1V6rO8q8m5GhNoJOmNhC7Ek95Bw==
MANUALLY SIGN amp; VERIFY
HASH = lDlahWCWx2b5TYUji52uPeReSW7vbro2wXuRsPKmOdA=
SIGNATURE = gsxw7UQpqni5HyPAw8wI2pvepbrDzizkOvO0hab1 7vi4EaYJi3n4lvnkBTOU5LXQKLZGzJcug0mL2pL/PVh8lrvzZ/F9CxULLxKpayrNkvL9yEWMvcfcku9Go5EGrxSzD7VYvkwOzHvGe4GgUGD1JOjvzXBAfJRT8h/wnZi9IPA9n31/tWI2eFw17Js/gymElycp7pjrpEhUNe/IVTP9HVfRQfAxEDAPW8GY/WFdxbD3Jk05LKvpTxua4jzCX9wJh/s8aiT9OvEXh3/zt06JSEpfgf CpkOFJupmRhsgqebPfVQEo24ctw1DnipKkL771mm30bFcm/FF1reXuOqqQ==
public class Main {
//====================================================================================
// MAIN
//====================================================================================
public static void main(String[] args) throws Exception {
//CREATE DATA
String dataString = "Data to be encrypted";
byte[] dataBytes = dataString.getBytes();
//CREATE KEYS
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
//AUTOMATICALLY SIGN amp; VERIFY
System.out.println("nAUTOMATICALLY SIGN amp; VERIFY");
byte[] automaticSignatureBytes = AutomaticHash.sign ("SHA256withRSA", privateKey, dataBytes);
//MANUALLY SIGN amp; VERIFY
System.out.println("nMANUALLY SIGN amp; VERIFY");
byte[] manualHashBytes = ManualHash.hash ("SHA-256", dataBytes);
byte[] manualSignatureBytes = ManualHash.sign ("NONEwithRSA", privateKey, manualHashBytes);
}
}
public class AutomaticHash {
//====================================================================================
// AUTOMATICALLY SIGN
//====================================================================================
public static byte[] sign(String algorithms, PrivateKey privateKey, byte[] dataBytes) throws Exception {
//CREATE SIGNATURE (use Hash first)
Signature signature = Signature.getInstance(algorithms);
signature.initSign(privateKey);
signature.update(dataBytes);
byte[] signatureBytes = signature.sign();
//ENCODE SIGNATURE
byte[] signatureEncodedBytes = Base64.getEncoder().encode(signatureBytes);
String signatureEncodedString = new String(signatureEncodedBytes);
//DISPLAY ENCODED SIGNATURE
System.out.println("SIGNATURE = " signatureEncodedString);
//RETURN SIGNATURE
return signatureBytes;
}
}
public class ManualHash {
//====================================================================================
// MANUALLY HASH
//====================================================================================
public static byte[] hash(String algorithm, byte[] dataBytes) throws Exception {
//CREATE HASH
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] hashBytes = digest.digest(dataBytes);
//ENCODE HASH
byte[] hashEncoded = Base64.getEncoder().encode(hashBytes);
String hashEncodedString = new String(hashEncoded);
//DISPLAY ENCODED HASH
System.out.println("HASH = " hashEncodedString);
//RETURN HASH
return hashBytes;
}
//====================================================================================
// MANUALLY SIGN
//====================================================================================
public static byte[] sign(String algorithm, PrivateKey privateKey, byte[] hashBytes) throws Exception {
//SIGN HASH
Signature signature = Signature.getInstance(algorithm);
signature.initSign(privateKey);
signature.update(hashBytes);
byte[] signatureBytes = signature.sign();
//ENCODE SIGNATURE
byte[] signatureEncodedBytes = Base64.getEncoder().encode(signatureBytes);
String signatureEncodedString = new String(signatureEncodedBytes);
//DISPLAY ENCODED HASH amp; SIGNATURE
System.out.println("SIGNATURE = " signatureEncodedString);
//RETURN SIGNATURE
return signatureBytes;
}
}
Комментарии:
1. Хэш данных подписан. Какой у вас вопрос относительно кода?
2. Значит, я должен подписывать хэш-байты, а не хэш-кодировать? Именно этим я сейчас и занимаюсь. Я обновил вопрос. Вывод кода показывает, что эти два подхода создают разные подписи, что означает, что мне не хватает ручных шагов soma.
3. Значит, я должен подписывать хэш-байты, а не хэш-кодировать? ДА. Что касается разных результатов, пожалуйста, смотрите Мой ответ.
Ответ №1:
SHA256withRSA
и NoneWithRSA
используйте дополнение PKCS # 1 версии v1.5, точнее RSASSA-PKCS1-v1_5. Это детерминированное заполнение, т. Е. Повторное подписание с одними и теми же данными приведет к созданию одной и той же подписи. Подробности можно найти в RFC8017, 8.2.
Это дополнение применяет кодировку DER для DigestInfo, которая приводит к SHA256, когда последовательность байтов (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 добавляется к хэшу.
Поэтому ваш ручной код должен быть изменен, например, следующим образом:
public static byte[] sign(String algorithm, PrivateKey privateKey, byte[] hashBytes) throws Exception {
byte[] id = new byte[] { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
byte[] derDigestInfo = new byte[id.length hashBytes.length];
System.arraycopy(id, 0, derDigestInfo, 0, id.length);
System.arraycopy(hashBytes, 0, derDigestInfo, id.length, hashBytes.length);
// SIGN HASH
Signature signature = Signature.getInstance(algorithm);
signature.initSign(privateKey);
signature.update(derDigestInfo);
byte[] signatureBytes = signature.sign();
...
С этим изменением оба sign()
метода возвращают один и тот же результат.
Кстати, java.util.Base64.Encoder.encodeToString()
напрямую генерирует строку в кодировке Base64.
Кроме того, при кодировании / декодировании всегда следует указывать кодировку, например dataString.getBytes(StandardCharsets.UTF_8)
, или new String(..., StandardCharsets.UTF_8)
. В противном случае используется кодировка платформы по умолчанию, которая может отличаться от предполагаемой.
Комментарии:
1. Можете ли вы также сделать это для SHA-1?
2. Для SHA-1 это аналогично, просто используйте соответствующие идентификаторы (
"SHA1withRSA"
и"SHA-1"
) и соответствующую последовательность байтов ( (0x) 30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 ), см. Для последнего и других дайджестов RFC8017, стр.47. Внимание: SHA-1 небезопасен!
Ответ №2:
Вот полный код, основанный на комментариях для SHA-1 и SHA-256
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
public class Main {
//====================================================================================
// MAIN
//====================================================================================
public static void main(String[] args) throws Exception {
//CREATE DATA
String dataString = "Data to be encrypted";
byte[] dataBytes = dataString.getBytes(StandardCharsets.UTF_8);
//CREATE KEYS
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
//SIGN
SHA1withRSA (privateKey, publicKey, dataBytes);
SHA256withRSA(privateKey, publicKey, dataBytes);
}
//====================================================================================
// SHA1 WITH RSA
//====================================================================================
private static void SHA1withRSA(PrivateKey privateKey, PublicKey publicKey, byte[] dataBytes) throws Exception {
//LOG
System.out.println("nSHA1 WITH RSA ==================================================================");
//AUTOMATICALLY SIGN amp; VERIFY
System.out.println("nAUTOMATICALLY SIGN amp; VERIFY");
byte[] automaticSignatureBytes = AutomaticHash.sign ("SHA1withRSA", privateKey, dataBytes);
Boolean automaticVerified = AutomaticHash.verify("SHA1withRSA", publicKey , dataBytes, automaticSignatureBytes);
//MANUALLY SIGN amp; VERIFY
System.out.println("nMANUALLY SIGN amp; VERIFY");
byte[] manualHashBytes = ManualHash.hash ("SHA-1", dataBytes);
byte[] manualPaddingHashBytes = ManualHash.padding("SHA-1", manualHashBytes);
byte[] manualSignatureBytes = ManualHash.sign ("NONEwithRSA", privateKey, manualPaddingHashBytes);
Boolean manualVerified = ManualHash.verify ("NONEwithRSA", publicKey , manualPaddingHashBytes, manualSignatureBytes);
}
//====================================================================================
// SHA256 WITH RSA
//====================================================================================
private static void SHA256withRSA(PrivateKey privateKey, PublicKey publicKey, byte[] dataBytes) throws Exception {
//LOG
System.out.println("nSHA256 WITH RSA ================================================================");
//AUTOMATICALLY SIGN amp; VERIFY
System.out.println("nAUTOMATICALLY SIGN amp; VERIFY");
byte[] automaticSignatureBytes = AutomaticHash.sign ("SHA256withRSA", privateKey, dataBytes);
Boolean automaticVerified = AutomaticHash.verify("SHA256withRSA", publicKey , dataBytes, automaticSignatureBytes);
//MANUALLY SIGN amp; VERIFY
System.out.println("nMANUALLY SIGN amp; VERIFY");
byte[] manualHashBytes = ManualHash.hash ("SHA-256", dataBytes);
byte[] manualPaddingHashBytes = ManualHash.padding("SHA-256", manualHashBytes);
byte[] manualSignatureBytes = ManualHash.sign ("NONEwithRSA", privateKey, manualPaddingHashBytes);
Boolean manualVerified = ManualHash.verify ("NONEwithRSA", publicKey , manualPaddingHashBytes, manualSignatureBytes);
}
}
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
public class AutomaticHash {
//====================================================================================
// AUTOMATICALLY SIGN
//====================================================================================
public static byte[] sign(String algorithms, PrivateKey privateKey, byte[] dataBytes) throws Exception {
//CREATE SIGNATURE (use Hash first)
Signature signature = Signature.getInstance(algorithms);
signature.initSign(privateKey);
signature.update(dataBytes);
byte[] signatureBytes = signature.sign();
//DISPLAY ENCODED SIGNATURE
System.out.println("SIGNATURE = " Base64.getEncoder().encodeToString(signatureBytes));
//RETURN SIGNATURE
return signatureBytes;
}
//====================================================================================
// AUTOMATICALLY VERIFY
//====================================================================================
public static Boolean verify(String algorithms, PublicKey publicKey, byte[] dataBytes, byte[] signatureBytes) throws Exception {
//INITIALIZE SIGNATURE
Signature signature = Signature.getInstance(algorithms);
signature.initVerify(publicKey);
signature.update(dataBytes);
//VERIFY SIGNATURE
boolean verified = signature.verify(signatureBytes);
//DISPLAY VERIFICATION
System.out.println("VERIFIED = " verified);
//RETURN SIGNATURE
return verified;
}
}
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
public class ManualHash {
//====================================================================================
// HASH
//====================================================================================
public static byte[] hash(String algorithm, byte[] dataBytes) throws Exception {
//CREATE HASH
MessageDigest digest = MessageDigest.getInstance(algorithm);
byte[] hashBytes = digest.digest(dataBytes);
//DISPLAY ENCODED HASH
System.out.println("HASH = " Base64.getEncoder().encodeToString(hashBytes));
//RETURN HASH
return hashBytes;
}
//====================================================================================
// PADDING
//====================================================================================
public static byte[] padding(String algorithm, byte[] hashBytes) throws Exception {
//PREPARE PADDING
byte[] padding = null;
if (algorithm.equals("SHA-1" )) { padding = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, (byte) 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; }
if (algorithm.equals("SHA-256")) { padding = new byte[] { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, (byte) 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 }; }
//ADD PADDING amp; HASH TO RESULTING ARRAY
byte[] paddingHash = new byte[padding.length hashBytes.length];
System.arraycopy(padding , 0, paddingHash, 0 , padding.length );
System.arraycopy(hashBytes, 0, paddingHash, padding.length, hashBytes.length);
//RETURN HASH
return paddingHash;
}
//====================================================================================
// SIGN
//====================================================================================
public static byte[] sign(String algorithm, PrivateKey privateKey, byte[] paddingHash) throws Exception {
//SIGN PADDED HASH
Signature signature = Signature.getInstance(algorithm);
signature.initSign(privateKey);
signature.update(paddingHash);
byte[] signatureBytes = signature.sign();
//DISPLAY ENCODED HASH amp; SIGNATURE
System.out.println("SIGNATURE = " Base64.getEncoder().encodeToString(signatureBytes));
//RETURN SIGNATURE
return signatureBytes;
}
//====================================================================================
// MANUALLY VERIFY
//====================================================================================
public static Boolean verify(String algorithm, PublicKey publicKey, byte[] hashBytes, byte[] signatureBytes) throws Exception {
//INITIALIZE SIGNATURE
Signature signature = Signature.getInstance(algorithm);
signature.initVerify(publicKey);
signature.update(hashBytes);
//VERIFY SIGNATURE
boolean verified = signature.verify(signatureBytes);
//DISPLAY VERIFICATION
System.out.println("VERIFIED = " verified);
//RETURN SIGNATURE
return verified;
}
}
Результат
SHA1 WITH RSA ==================================================================
AUTOMATICALLY SIGN amp; VERIFY
SIGNATURE = bGLX8elMdZeyDmT1YCh86Uz5kH9P69NpAqYYNn11ybyYp2cqLUSyqTEolhv4QgEAnSoyGQH9McjEMqhDL u8Kp68nDRmt2ScFtNN17i0F7 65XO2PHqQtpfOsb2tUoG4Q SmoBWSiXJliIQTRjYeblgLteY74SJreDlN7AAEYmFpi9oVbptwCj0OnZMlz5EdffX66Yy47qxYSF3O3Wuea1mCY/lG52qHwTFFJ //N X/5wFX4UDpzA BMoWynE7FKzzfkQOGl9LazhFCTH46i30UHXmNGlbrjpApRtZKL9LnZUNPbLJTVKtCDZFMEdocT/a9NBeclZPtu/LBj3l Iw==
VERIFIED = true
MANUALLY SIGN amp; VERIFY
HASH = AayjWUd2pEmeoC49PlJj2mMdG4=
SIGNATURE = bGLX8elMdZeyDmT1YCh86Uz5kH9P69NpAqYYNn11ybyYp2cqLUSyqTEolhv4QgEAnSoyGQH9McjEMqhDL u8Kp68nDRmt2ScFtNN17i0F7 65XO2PHqQtpfOsb2tUoG4Q SmoBWSiXJliIQTRjYeblgLteY74SJreDlN7AAEYmFpi9oVbptwCj0OnZMlz5EdffX66Yy47qxYSF3O3Wuea1mCY/lG52qHwTFFJ //N X/5wFX4UDpzA BMoWynE7FKzzfkQOGl9LazhFCTH46i30UHXmNGlbrjpApRtZKL9LnZUNPbLJTVKtCDZFMEdocT/a9NBeclZPtu/LBj3l Iw==
VERIFIED = true
SHA256 WITH RSA ================================================================
AUTOMATICALLY SIGN amp; VERIFY
SIGNATURE = M7bqVsCdlQnjCBBupuOmCJSzhuZ4C2K1VsNcJUXpdKBt17jy3TbFKcaYQ47Lsj0x/xqHFK3OTFxxZRjZyywqk25ovyzTfCkwHoEYWE2TiApEkf92pTcVn86YkFhDQCSJWboJu75NAEtDxkiJelUut3DmHp0Pu91U17cKC6JxUSIHlTQAxbNHhUfkx0YdE6sTvzK2lG/XJcZdA/Nx6qia9548EsBoi71vY1GlsSn7pEf5pg5yCJF5fAufD9hRSPI5OX2TKtj3hYXMr8UXQ6VaDBf4PiJjQ8MNg q4cXtr BpK1uHtxm0BuRHlcM4eBkTyuia93Xc88Ojwsma2tlCrwg==
VERIFIED = true
MANUALLY SIGN amp; VERIFY
HASH = lDlahWCWx2b5TYUji52uPeReSW7vbro2wXuRsPKmOdA=
SIGNATURE = M7bqVsCdlQnjCBBupuOmCJSzhuZ4C2K1VsNcJUXpdKBt17jy3TbFKcaYQ47Lsj0x/xqHFK3OTFxxZRjZyywqk25ovyzTfCkwHoEYWE2TiApEkf92pTcVn86YkFhDQCSJWboJu75NAEtDxkiJelUut3DmHp0Pu91U17cKC6JxUSIHlTQAxbNHhUfkx0YdE6sTvzK2lG/XJcZdA/Nx6qia9548EsBoi71vY1GlsSn7pEf5pg5yCJF5fAufD9hRSPI5OX2TKtj3hYXMr8UXQ6VaDBf4PiJjQ8MNg q4cXtr BpK1uHtxm0BuRHlcM4eBkTyuia93Xc88Ojwsma2tlCrwg==
VERIFIED = true