Java中RSACryptoServiceProvider signHash方法的等价物

Java中RSACryptoServiceProvider signHash方法的等价物,java,c#,rsa,signature,Java,C#,Rsa,Signature,我试图得到以下C#方法的等价物: 在Java语言中。事实上,我想到了这一点: private static String SignHash(final byte[] btHash, String SerialNumber) throws Exception { KeyStore ks = null; ks = KeyStore.getInstance("Windows-MY"); ks.load(null, null); Boolean noValidCerti

我试图得到以下C#方法的等价物:

在Java语言中。事实上,我想到了这一点:

private static String SignHash(final byte[] btHash, String SerialNumber) throws Exception
{
    KeyStore ks = null;
    ks = KeyStore.getInstance("Windows-MY");
    ks.load(null, null);

    Boolean noValidCertificate = true;

    Enumeration<String> en = ks.aliases();

    ArrayList<String> lstAlias = Collections.list(en);

    lstErreurs.add(lstAlias.size() + " certificate(s) found");

    for (String aliasKey : lstAlias)
    {
        X509Certificate cert = (X509Certificate) ks.getCertificate(aliasKey);

        Certificat = Base64Coder.encodeBase64String(cert.getEncoded());

        Boolean blnCertificateFound = false;

        if (SerialNumber != null && !SerialNumber.equals(""))
        {
            String SerialNumberCert = cert.getSerialNumber().toString(16);

            if (SerialNumber.toLowerCase().contains(SerialNumberCert.toLowerCase())
                    || SerialNumberCert.toLowerCase().contains(SerialNumber.toLowerCase()))
            {
                blnCertificateFound = true;
            }
        }

        if (blnCertificateFound == false)
        {
            continue;
        }

        Provider p = ks.getProvider();

        boolean isHashToSign = false;
        for (String strToSign : input.split(";")) {
            if(strToSign.length() == 44 && General.isBase64(strToSign)) {
                isHashToSign = true;
                break;
            }
        }

        String algorithm = "";
        if(isHashToSign)
        {
            algorithm = "RSA";
        } else {
            algorithm = "SHA256withRSA";
        }
        Signature sig = Signature.getInstance(algorithm, p);

        PrivateKey key = (PrivateKey) ks.getKey(aliasKey, "1234".toCharArray());

        if (key != null)
        {
            noValidCertificate = false;

            sig.initSign(key);

            String[] TabToSign = input.split(";");
            String strResultSignature = "";
            String separator = "";
            for (String strToSign : TabToSign)
            {
                byte[] btToSign = null;
                if(isHashToSign) {
                    btToSign = General.Base64_Decode_To_ByteArray(strToSign.getBytes(Charset.forName("UTF-8")));
                } else {
                    btToSign = strToSign.getBytes(Charset.forName("UTF-8"));
                }

                sig.update(btToSign);

                byte[] res = sig.sign();

                String resB64 = Base64Coder.encodeBase64String(res);

                strResultSignature += separator + resB64;
                separator = ";";
            }

            return strResultSignature;
        }
    }

    return null;
}
private静态字符串SignHash(最终字节[]btHash,字符串SerialNumber)引发异常
{
密钥库ks=null;
ks=KeyStore.getInstance(“Windows MY”);
ks.load(null,null);
布尔值noValidCertificate=true;
枚举en=ks.alias();
ArrayList lstalas=Collections.list(en);
添加(lstalas.size()+“找到证书”);
用于(字符串别名键:lstalas)
{
X509Certificate cert=(X509Certificate)ks.getCertificate(别名密钥);
certificate=Base64Coder.encodeBase64String(cert.getEncoded());
布尔blnCertificateFound=false;
if(SerialNumber!=null&!SerialNumber.equals(“”)
{
字符串SerialNumberCert=cert.getSerialNumber().toString(16);
if(SerialNumber.toLowerCase().contains(SerialNumberCert.toLowerCase())
||SerialNumberCert.toLowerCase()包含(SerialNumber.toLowerCase())
{
bNcertificatefound=真;
}
}
如果(blnCertificateFound==false)
{
继续;
}
Provider p=ks.getProvider();
布尔值isHashToSign=false;
for(字符串strosign:input.split(“;”)){
if(strosign.length()==44&&General.isBase64(strosign)){
isHashToSign=true;
打破
}
}
字符串算法=”;
如果(isHashToSign)
{
算法=“RSA”;
}否则{
算法=“SHA256withRSA”;
}
Signature sig=Signature.getInstance(算法,p);
PrivateKey=(PrivateKey)ks.getKey(别名key,“1234.tocharray());
if(key!=null)
{
noValidCertificate=假;
sig.initSign(key);
String[]TabToSign=input.split(“;”);
字符串strResultSignature=“”;
字符串分隔符=”;
for(字符串符号:禁忌符号)
{
字节[]btToSign=null;
如果(isHashToSign){
btToSign=General.Base64_Decode_To_ByteArray(strToSign.getBytes(Charset.forName(“UTF-8”));
}否则{
btToSign=strToSign.getBytes(Charset.forName(“UTF-8”);
}
信号更新(btToSign);
字节[]res=sig.sign();
String resB64=base64编码器。encodebase64字符串(res);
strResultSignature+=分隔符+resB64;
分隔符“;”;
}
返回strResultSignature;
}
}
返回null;
}
但是得到“RSA”算法对签名不起作用。我终于在Java中对哈希的哈希进行了签名。我希望对SHA256字节数组哈希进行签名,而不必再次对其进行哈希。我怎样才能得到这个结果?(有关信息,我正在使用Windows证书存储,因此我必须与Sun MSCAPI提供程序合作)

编辑1:

我尝试了“NONEwithRSA”算法,但签名结果与.NET中使用SignHash方法的签名不同

编辑2:

下面的线程:解释了实际上可以对哈希进行签名,但该方法需要BouncyCastle

我无法使用BouncyCastle操作,因为我需要使用sun MSCAPI提供程序(Windows证书存储)。我必须找到BouncyCastle的替代品(除非BC允许我们使用Sun MSCAPI提供程序)。

(答案完全重写了。一些不太有趣的想法和片段可以在中找到)

调用SignHash(btHash,CryptoConfig.mapnametoid(“SHA256”))会生成PKCS#1 v1.5签名(RSASSA-PKCS1-v1_5),例如:

签名:

0A5003E549C4E4310F720076A5A4D785B165C4FE352110F6CA9877EB9F364D0C40B0197110304D6F92E8BD40DFD38DB91F356601CDD2CD34129BC54492C2D7F371D431150288A95C21E47533F01A9FA4977439FF9594C703380BEDF49A47A7B060ECAC26AEB53B8732D93E18FAD3B2D5889B3311C1B0D4F9F6B318169BDEB143D771DEFB56BAFE49B2B59F172757D4273EF369AFCB32490EC954E17599BD66D4E3BDB345B860748DB0C3B5A272ECFA546E65F2D4C87870CC62D91680AB71DB52DE618C006356258A941E8F36A5DCC7A06BA6DACAC3DC35F482B168107B4D7DA6C19A56FEDC247232DD7210CA9DB7273AA9AE6A90A8A4DFEB64BA0FBC830AB922
CE26A9F84A85037856D8F910141CE7F68D6CAAB416E5C2D48ACD9677BBACCB46B41500452A79018A22AB1CA866DD878A76B040A343C1BABCDB683AFA8CE1A6CCCA48E3120521E8A7E4F8B62B453565E6A6DC08273084C0748C337724A84929630DC79E2EB1F45F5EEBA2148EC0CA5178F2A232A2AE8A5D22BB06C508659051CD1F5A36951B60F6322C5AEB5D4461FABE4D84E28766501A1583EC2A5D8553C163EC8DB9E80EF972233516BEC50AAC38E54C8B5E3B3DEAE90F37A1D230CD976ABEEE97A4601461E842F0F548DA7A74645AAB42586044B084E6A9F3EFE409EE12D98EB0B16CDC2C0AB75BF5DC9B52EBB7E3303C53A2B1BDF14000B99112B1871A6A
其中包含一个PKCS#1 v1.5填充和散列(使用公钥解密时):


由于您只有要签名的散列(而不是数据),因此您需要使用java中的(应该在不进行任何散列的情况下对输入数据执行PKCS#1 v1.5填充签名),并使用散列OID手动生成正确的输入摘要信息。像那样(在……的帮助下):

它提供与C代码相同的签名(使用默认的
SunJCE
/
SunRsaSign
提供程序)


带有限制的
sunmsapi
提供程序。引用
sun.security.mscapi.RSASignature
javadoc:

注意:NONEwithRSA必须提供预先计算的消息摘要。 仅支持以下摘要算法:MD5、SHA-1、, SHA-256、SHA-384、SHA-512和一个专用摘要算法 是SHA-1和MD5摘要的串联

乍一看,这可能适用于这种情况。不幸的是:

Signature mscapiSignatureNoneWithRSA = Signature.getInstance("NONEwithRSA", "SunMSCAPI");
mscapiSignatureNoneWithRSA.initSign(mscapiPrivKey);
mscapiSignatureNoneWithRSA.update(btHash);
byte[] mscapiSignatureNoneWithRSA_btHash = mscapiSignatureNoneWithRSA.sign();
提供不同的签名:

0A5003E549C4E4310F720076A5A4D785B165C4FE352110F6CA9877EB9F364D0C40B0197110304D6F92E8BD40DFD38DB91F356601CDD2CD34129BC54492C2D7F371D431150288A95C21E47533F01A9FA4977439FF9594C703380BEDF49A47A7B060ECAC26AEB53B8732D93E18FAD3B2D5889B3311C1B0D4F9F6B318169BDEB143D771DEFB56BAFE49B2B59F172757D4273EF369AFCB32490EC954E17599BD66D4E3BDB345B860748DB0C3B5A272ECFA546E65F2D4C87870CC62D91680AB71DB52DE618C006356258A941E8F36A5DCC7A06BA6DACAC3DC35F482B168107B4D7DA6C19A56FEDC247232DD7210CA9DB7273AA9AE6A90A8A4DFEB64BA0FBC830AB922
CE26A9F84A85037856D8F910141CE7F68D6CAAB416E5C2D48ACD9677BBACCB46B41500452A79018A22AB1CA866DD878A76B040A343C1BABCDB683AFA8CE1A6CCCA48E3120521E8A7E4F8B62B453565E6A6DC08273084C0748C337724A84929630DC79E2EB1F45F5EEBA2148EC0CA5178F2A232A2AE8A5D22BB06C508659051CD1F5A36951B60F6322C5AEB5D4461FABE4D84E28766501A1583EC2A5D8553C163EC8DB9E80EF972233516BEC50AAC38E54C8B5E3B3DEAE90F37A1D230CD976ABEEE97A4601461E842F0F548DA7A74645AAB42586044B084E6A9F3EFE409EE12D98EB0B16CDC2C0AB75BF5DC9B52EBB7E3303C53A2B1BDF14000B99112B1871A6A
其中只包含哈希的PKCS#1 v1.5填充值(没有ASN.1 DigestInfo序列/在本例中是错误的/):

尝试从
SunJCE
示例对摘要信息进行签名会出现异常:

java.security.SignatureException:消息摘要太长

(这是造成这种行为的原因。)


使用RSA私钥加密生成签名的另一种方法,该方法通过
SunJCE
提供程序提供与C#code相同的签名(使用与上述相同的
asn
变量):

不适用于
sunmsapi
提供程序:

cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunMSCAPI");
cipher.init(Cipher.ENCRYPT_MODE, mscapiPrivKey);
byte[] ret = cipher.doFinal(asn1);
正如它所给出的:

4A540DFAD44EBDAE89BF5DD52121DA831E7C394E0586DC9EAEF949C466E649979E19DF81A44801EBD80B8946605B4EFCED53011A844A3A4E023136F0CDEAA57EAAB1EA1FA75400B3B2D5FAB3955BEB13A178AC03DED6AACA0571412B74BCE30E772082A368B58E94D8E20D8F2A116BA5B3881824A014281E9F04BD687C087ACF7164CAF7C74274BA356A671ADA2BB4142504DB2883AFEDA563C6E590BC962725D6697402AB24391409F50D7D16B8BF64A1C0224C379EF9C7B8E493BE889A70674C3AEEC524366DBF9DE36FEE01F186FC00DE2F06096C46CC873D37E194CB217FBFCCF450C1F96C804022E25E1589DF67247927AAD59C66294B027DD5EE991D46
用公开密钥解密的密码是胡说八道的:

3A9E0F985D1CF2CFDB45F201A68EF0F241ADBAED2D945FD36451CB4BE77D9B30D977004F95E6EDC208805E62870CD19D87C5E7F4E4C1AC2546F7F9C410299C9D203C47C2B547BAA55DA05C44DACB7E07C3F0DB99AE291E48A67EE089F8DA34EB1AABE352A7F94B082CFB167C0FE90761B79FCE238A0F3D0D917CA51220EEA9A43877703FC06CDC1F13A77CA9904E3660F7AD84DE0C34C877B737C20B1A117E60D69E6766054A30B95E6E88CF2C11AEE5CE30F2DD780C0334BE085302E73E0E5BB096893B7155E7A48CA16DD5EA9FC6F63090F7223E7FBAAA133BDFDA7251F412E395F4D8A462569BC5DCC34C2DF7D996BB3278C3F6B0E1EE9E729925937C8BAE
但(更有趣的是)在使用私钥解密时包含有效的PKCS#1 v1.5填充加密明文:

000211F7946FAD6BDB18830F8DD1238FD7EFCCFF041D55B88FBABDAAA6B06A5E9FD7556EB33678D9954D26E07B2FCE6D7304386DBDFC352C9932E2BA1794A3A0E0F6D78AA656DEB36CC483171A77ABF34408F4BF60661ECA79852B8E39C1A710976208FFBF6BE0DFB566149E6C5838762316F394B70BDF6D494F8C43C42CB6E527292DEF9204712CB24AC82C572BBC0E70A298D5FB050A27B54AFFA1332EEF37A14E65D379968BCE717BEC37C67A180DE943AAF2FE83560D33BC588E11B85D1C3391CCB13E4A80F57166BAC9003031300D060960864801650304020105000420579116B63E065883248C0716DA6A034D23370B321CA080081F4203818E543AC6
这意味着,尽管为加密操作提供了私钥,但SunMSCAPI使用了公钥部分(我没有深入研究实现细节以找到重新加密的方法)
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "SunMSCAPI");
cipher.init(Cipher.ENCRYPT_MODE, mscapiPrivKey);
byte[] ret = cipher.doFinal(asn1);
4A540DFAD44EBDAE89BF5DD52121DA831E7C394E0586DC9EAEF949C466E649979E19DF81A44801EBD80B8946605B4EFCED53011A844A3A4E023136F0CDEAA57EAAB1EA1FA75400B3B2D5FAB3955BEB13A178AC03DED6AACA0571412B74BCE30E772082A368B58E94D8E20D8F2A116BA5B3881824A014281E9F04BD687C087ACF7164CAF7C74274BA356A671ADA2BB4142504DB2883AFEDA563C6E590BC962725D6697402AB24391409F50D7D16B8BF64A1C0224C379EF9C7B8E493BE889A70674C3AEEC524366DBF9DE36FEE01F186FC00DE2F06096C46CC873D37E194CB217FBFCCF450C1F96C804022E25E1589DF67247927AAD59C66294B027DD5EE991D46
3A9E0F985D1CF2CFDB45F201A68EF0F241ADBAED2D945FD36451CB4BE77D9B30D977004F95E6EDC208805E62870CD19D87C5E7F4E4C1AC2546F7F9C410299C9D203C47C2B547BAA55DA05C44DACB7E07C3F0DB99AE291E48A67EE089F8DA34EB1AABE352A7F94B082CFB167C0FE90761B79FCE238A0F3D0D917CA51220EEA9A43877703FC06CDC1F13A77CA9904E3660F7AD84DE0C34C877B737C20B1A117E60D69E6766054A30B95E6E88CF2C11AEE5CE30F2DD780C0334BE085302E73E0E5BB096893B7155E7A48CA16DD5EA9FC6F63090F7223E7FBAAA133BDFDA7251F412E395F4D8A462569BC5DCC34C2DF7D996BB3278C3F6B0E1EE9E729925937C8BAE
000211F7946FAD6BDB18830F8DD1238FD7EFCCFF041D55B88FBABDAAA6B06A5E9FD7556EB33678D9954D26E07B2FCE6D7304386DBDFC352C9932E2BA1794A3A0E0F6D78AA656DEB36CC483171A77ABF34408F4BF60661ECA79852B8E39C1A710976208FFBF6BE0DFB566149E6C5838762316F394B70BDF6D494F8C43C42CB6E527292DEF9204712CB24AC82C572BBC0E70A298D5FB050A27B54AFFA1332EEF37A14E65D379968BCE717BEC37C67A180DE943AAF2FE83560D33BC588E11B85D1C3391CCB13E4A80F57166BAC9003031300D060960864801650304020105000420579116B63E065883248C0716DA6A034D23370B321CA080081F4203818E543AC6
// Obtain the handles
long hCryptKey = (Long)MethodUtils.invokeMethod(mscapiPrivKey, "getHCryptKey");
long hCryptProvider = (Long)MethodUtils.invokeMethod(mscapiPrivKey, "getHCryptProvider");
// Call the internal native method
Class<?> internalClass = Class.forName("sun.security.mscapi.RSASignature");
Method internalSignHashMethod = internalClass.getDeclaredMethod("signHash", boolean.class, byte[].class, int.class, String.class, long.class, long.class);
internalSignHashMethod.setAccessible(true);
byte[] res = (byte[])internalSignHashMethod.invoke(internalClass, false, btHash, btHash.length, "SHA-256", hCryptProvider, hCryptKey);
ArrayUtils.reverse(res); // Make it big endian
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDoZFvkEpdzXwSw
9g6cDxg9n/khCjLIO7E8VQFzu80C0iR0C6K05SHvTFEdssmzZmdCQi092ReSJRPH
yAOQUnlcMuCpi0m62ufZ4yNkZX5sH3fjHkP1FMv5CPtJOIArGFCMS4CufXu2XkXh
dbJuCLPJsUuiRsaoRg0Q6a8QVqWAR1oyVojTNFqzZWTLD46lQQIvINOrIeYvKklU
FUNcmq8PyArwEvxaDeiop4gVyizx7n7v213FjAXMfEG920O4DlnKjObdi1+PhejT
1RUxRUipTmAI2d3JmACpYH6+Il8Ck61wmKQ9IjoTstNeRfKGEkxH9RKP2P4ko5w9
8YfToVDXAgMBAAECggEAI5vNIMNghYMXuu3ZCzyc4ER07gUcBuZun+n+kPdD0JzW
jRmDUuiRLJOrEjvlACI+zD5LpGBxZilcQI57TU/13JTHK/N11rXYNODC+Y07s+GW
gyyOCS2om34u0udfbDsLjJO9If+ER0tmtcdNEeMveUY7aqAhrIMfWWoVMxGzxlXd
0kHWl4blnisjc01xCG4WcXVItyA6F8WZ3kL+BTeR5/3IwM72r9k7fcBkJZPLJZff
oZ2W+whGa9UXAkt6DQ6PlWMvt+AVcu84f8k/4FRRUNbY1OslO7zHbEc1K5qibpjb
6Tlfg2G+bux/1oCJ59bdyRP7FQMmgjLx49H17mwcgQKBgQD1j4HRtwSPnS+INPS4
nVpfEtb+wXGeDLCMAgdesgXYfr5JWJKJCKW65/j2TkJ/xoN8L1z8TeE6o6Q3ZHJ9
QtcM1ifCpNCnjjalbkm9BG4bCPy+5MUIuS5oRtJjwb7mPTxzpq4DIj3G2ooY5F2i
9Nyqde3bEvWn910yqQgI6CjOtwKBgQDyRYkU46gKLhp98gJ0zdm3KNZ/TBE5zsO6
rDnwFPLGxanVNMzkIQX/LXAQOaNK1WD8fmKG+iixDVLyJYtVtuvTQLOHkOOTXw44
QY4BGh+wbS0HrfKd7Qcpt/3HTCKq9eW33+jggyBc+fa+LDIGpdbO16SBCP3Cb7k6
9gtBN5du4QKBgQCKriVO3uGAifESJ3Yd3R/wmZ85+N3FuLzsFSk8XaXXgpzMp2z6
XxvZ1rBPyhrcNqyDMex9wS32A/z2G5BdFaaF5VxHHPWJ61MJUqPqT9ovAoBa/rAY
IR0IXxbqp7y8ItFFL1kPBAOHjlx3emE3arpEup0+IBMEbTsBJV0YSqThOQKBgFRf
syX7QwKIm+FQ71oOdsw7BLjAnR8syy2v3V2nbgWbwVHnWZP5jEUaZfTAngXp2iUV
PusTJCjFIyYBvUzUr7yaw+tqolcou6ML8ZCgsHiZDR2njt9BNUVqNo+6DDjN+nrX
GBtYj2TSCQSiD6oRB4Zxw3DM2NNmZXQLTFAiNDMBAoGBAJOu4+nVB8iUE3JBsrM5
WkwBivqTyo9pusxwEs+GHnkVFsFubFKHda04220JMRVevOuf48DPgvlW6UCTojyr
85dFon9tV0qyi9Ehc0OdXqOjmx0y+wdR6ZqG+x+e6JGiYeReIa4XBrq0cLHlzrNY
8UwL0QLJpuaQZEbqhyOGMNKE
-----END PRIVATE KEY-----