简体   繁体   中英

SHA1 with RSA encryption: bad length error

Probably I have several misunderstandings.

AFAIK signing a byte array with RSA-SHA1 generates a byte array (signature) of the same lenght as the RSA key used. Is that right?

From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?

Later on, in order to recover this hash (with or without padding schema on it) I would need to encrypt the signature with the public key. Is that right?

Something is broken in my logic because I'm not able to encrypt the signature with the public key.

Or my code is wrong. I'm using .net RSACryptoServiceProvider and it raises a bad data length error when trying to encrypt a signature... I assume encrypt means apply RSA using public key, right? When trying to decrypt it raises a Key Not found exception. As expected because I only have the public key.

EDIT: Given a byte array and RSACryptoServiceProvider I could Encrypt, Decrypt and SignData. I thought that SignData (without padding schema to simplify the question) is a shortcut of apply SHA, then Decrypt. For Encrypt I mean applying the RSA formula using public key as input, and for Decrypt I mean applying the RSA formula (the very same formula) using private key as input. Are this definitions ok?

EDIT2: For exemple have a look at the next signed xml: http://www.facturae.gob.es/formato/Versiones/factura_ejemplo2_32v1.xml

And the next powershell script:

$signb64="oYR1T06OSaryEDv8VF9/JgWmwf0KSyOXKpBWY4uAD0YoMh7hedEj8GyRnKpVpaFanqycIAwGGCgl vtCNm+qeLvZXuI0cfl2RF421F8Ay+Q0ani/OtzUUE49wuvwTCClPaNdhv2vfUadR8ExR7e/gI/IL 51uc3mEJX+bQ8dxAQ2w=";
$certB64="MIIDtDCCAx2gAwIBAgICAIcwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMCRVMxDzANBgNVBAgT Bk1hZHJpZDEPMA0GA1UEBxMGTWFkcmlkMQ4wDAYDVQQKEwVNSVR5QzEbMBkGA1UECxMSTUlUeUMg RE5JZSBQcnVlYmFzMRQwEgYDVQQDEwtDQSB1c3VhcmlvczAeFw0wOTEwMTUxNjA5MzRaFw0xMDEw MTUxNjA5MzRaMHExCzAJBgNVBAYTAkVTMQ8wDQYDVQQIEwZNYWRyaWQxDzANBgNVBAcTBk1hZHJp ZDEOMAwGA1UEChMFTUlUeUMxGzAZBgNVBAsTEk1JVHlDIEROSWUgUHJ1ZWJhczETMBEGA1UEAxMK VXN1YXJpbyA1NDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAroms65axKuQK18YDfD/x6DIn 0zKZ+6bv1K2hItJxel/JvU3JJ80/nY5o0Zbn+PrvlR2xF3poWYcPHLZpesgxhCMfnP7Jb5OUfceL g44m6T9P3PG1lSAZs3H6/TabyWGJy+cNRZMWs13KnB9fDAjJ5Jw0HVkwYNwmb1c7sHCuyxcCAwEA AaOCAVgwggFUMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMB0GA1UdDgQWBBTYhqU2tppJoHl+S1py BOH+dliYhzCBmAYDVR0jBIGQMIGNgBT1oWqod09bsQSMp35I8Q6fxXaPG6FypHAwbjEPMA0GA1UE CBMGTWFkcmlkMQ8wDQYDVQQHEwZNYWRyaWQxDjAMBgNVBAoTBU1JVHlDMRswGQYDVQQLExJNSVR5 QyBETkllIFBydWViYXMxEDAOBgNVBAMTB1Jvb3QgQ0ExCzAJBgNVBAYTAkVTggEDMAkGA1UdEQQC MAAwNgYDVR0SBC8wLYYraHR0cDovL21pbmlzdGVyLThqZ3h5OS5taXR5Yy5hZ2UvUEtJL0NBLmNy dDA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vbWluaXN0ZXItOGpneHk5Lm1pdHljLmFnZS9QS0kv Y3JsLmNybDANBgkqhkiG9w0BAQsFAAOBgQAhAN/KVouQrHOgd74gBJqGXyBXfVOeTVW+UTthhfCv DatXzTcrkYPQMfBAQMgGEa5KaQXcqKKhaoCUvrzFqE0HnAGX+ytX41oxZiM2fGNxRZcyUApLEX67 m8HOA/Cs2ZDlpU2W7wiOX5qr+ToTyfXsnRwPWvJ8VUmmXwyMEKcuzg==";

$signb=[System.Convert]::FromBase64String($signB64);
$certb=[System.Convert]::FromBase64String($certB64);

$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @(,$certb)
$rsacsp = [System.Security.Cryptography.RSACryptoServiceProvider] $cert.PublicKey.Key;

$signb.Length*8;
$rsacsp;

$rsacsp.Encrypt($signb,0);

I tried:

$rsacsp.Encrypt($signb,[System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1);

instead of

$rsacsp.Encrypt($signb,0);

But I always get a bad length error:

Exception calling "Encrypt" with "2" argument(s): "Bad Length.

EDIT 3: After reading, I can see my main issue was "From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?".

RSA sign (with an bits key length) could be viewed as an operation that takes an arbitraty byte array and outputs n bits. In order to do that, it uses a hash function like SHA1 that takes an arbitrary byte array and produces a fixed output (160 bits for SHA1). Now in theory I could "encrypt" with the private key butthen the output would be 160 bits long tooit is not the way RSA is implemented. RSA Signing needs to apply padding function after the hash in order to produces an n bits text before "encrypting" it.

Another source of confusion is the meaning of the Encrypt method of .NET RSACryptoProvider. It turns out that this method has two parameters: a byte array and a flag indicating the padding function. It takes the byte array, applies the padding and then "encrypts" with the public key. It is of no use for a signature scenario. The operations decrypt and encrypt in RSACryptoProvider are not simmetrical. You can "decrypt" whatever has been "encrypt", but not the other way around.

At the end the confusion lies in that "atomic" functions used when encrypting/decrypting and the ones used when signin are the same, but they are used in incompatible ways.

AFAIK signing a byte array with RSA-SHA1 generates a byte array (signature) of the same lenght as the RSA key used. Is that right?

Usually yes, although the size will of course be encoded as octet stream (aka byte array) it is possible that the size of the signature is actually up to 7 bits larger. The key size is normally a multiple of 8 (bits) so this doesn't come up much.

From another side signing, roughly means generate a hash using SHA1 (so it is 160 bites long) and then with or without a padding scheme encrypt it with the private key.Is that right?

No, you should never perform modular exponentiation in RSA without padding; a padding scheme is required for security. Note that you should not talk about encryption here. Encryption is used to provide confidentiality. That RSA signature generation and encryption both uses modular exponentiation - although with different keys - doesn't mean one equates the other.

It is important to note that the padding scheme for PKCS#1 v1.5 encryption is different from the one used for signature generation . Furthermore there are also the newer OAEP padding scheme for encryption and the PSS padding scheme for signature generation which are rather distinct.

Later on, in order to recover this hash (with or without padding schema on it) I would need to encrypt the signature with the public key. Is that right?

Perform modular exponentiation and then verify the result, yes. But as the verification requires verifying the padding in a secure way you should really let an API handle this.

Something is broken in my logic because I'm not able to encrypt the signature with the public key.

Try something written for verification instead, like the method VerifyHash as seen in this example .


You can try and find a raw RSA implementation to find out what is within the RSA signature. You should only do this to analyze the signature.

So if you "encrypt" the data with the public key (ie just perform modular exponentiation) you would get:

0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a05000414a2304127e2fe3b8a8203b219feafdd9b58558310

as result. This is clearly PCKS#1 v1.5 padding for signature generation. It includes an encoded hash value:

SEQUENCE(2 elem)
  SEQUENCE(2 elem)
    OBJECT IDENTIFIER1.3.14.3.2.26
    NULL
  OCTET STRING(20 byte) A2304127E2FE3B8A8203B219FEAFDD9B58558310

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM