how to specify signature length for java.security.Signature sign method - java

For my application, I'm trying to sign some byte contents using java.security.Signature class. The problem I'm having is that signature is never generated at a fixed length. For instance, sometimes it is generated at a length of 135, 136 or 137 bytes. Is there a way to specify the length or some padding at the end? Any other ideas or comments are appreciated.
private byte[] ecdsaSign(ECPrivateKey key, byte[] content) throws Exception {
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA", "SC");
ecdsaSign.initSign(key);
ecdsaSign.update(content);
byte[] signature = ecdsaSign.sign();
return signature;
}

For ECDSA Java crypto uses the ASN.1 DER encoding standardized by X9.62, SEC1 and rfc 3279 sec 2.2.3, which varies slightly in length. This is covered in more detail on another Stack: https://crypto.stackexchange.com/questions/1795/how-can-i-convert-a-der-ecdsa-signature-to-ASN.1 and https://crypto.stackexchange.com/questions/33095/shouldnt-a-signature-using-ecdsa-be-exactly-96-bytes-not-102-or-103 and https://crypto.stackexchange.com/questions/37528/why-do-openssl-elliptic-curve-digital-signatures-differ-by-one-byte
This is also true for DSA, but not RSA, where signatures (and cryptograms since RSA supports both signature and encryption) are fixed length for a given key, as defined by I2OS and OS2I in PKCS1.
If you want a different encoding, such as the fixed-length one used by PKCS11 (and your provider name "SC" suggests that possibility), you must convert it.
Added 2019-10: you no longer have to do it yourself in Java; BouncyCastle since 1.61 (2019-02) correctly supports this, as does SunEC in Java 9 up (2018-12). See later near-dupe Java ECDSAwithSHA256 signature with inconsistent length .

Related

RSA should I use X.509 or PKCS #1

Use case:
I have a use case wherein client generates private and public key , sends the base 64 encoded public key to the server.
On server side I will encrypt a message using this public key and send the encrypted message to client , which the client decrypts using its private key.The algorithm agreed upon is 'RSA'.
The problem is on server side I am seeing that certain keys are working using X509EncodedKeySpec as key spec
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
While some keys throw exception (Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence) using X509EncodedKeySpec but work using RSAPublicKeySpec:
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey.getInstance(publicBytes);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
So, what I came to understand is that client and server need to agree whether to use:
PKCS #1 or X.509 for encoding the key . My question is which one is better for my use case? Any guidelines when to use which format?
There's very little difference. The key format Java calls X.509, more exactly known as the ASN.1 structure SubjectPublicKeyInfo (or SPKI) defined in X.509 or equivalently and more conveniently in RFC5280 sec 4.1, is a quite simple way to handle a large and flexible set of algorithms: it consists of a substructure AlgorithmIdentifier which identifies the algorithm and its parameters if applicable, then an opaque BIT STRING which contains the actual key information (encoded) in a format depending on (the algorithm identified by) the AlgorithmIdentifier.
For RSA, the algorithm-dependent part is the ASN.1 structure RSAPublicKey defined in PKCS1 or more conveniently RFC8017 appendix A.1.1 and its earlier versions, and duplicated in RFC3279 sec 2.3.1. Thus for RSA the X.509 (SPKI) format contains the PKCS1 format, and since RSA doesn't have parameters (or at least key-related parameters), the only real difference is that the X.509 format explicitly specifies that the key is RSA -- which in your application you already know.
You have already discovered that vanilla (Oracle-was-Sun-now-OpenJDK) Java crypto, aka JCA Java Cryptographic Architecture, directly supports only the X.509 (SPKI) format, which is a minor advantage. However if you use BouncyCastle it is much easier to convert back and forth than the code in your Q; you simply use the org.bouncycastle.asn1.x509.SubjectPublicKeyInfo class to add or discard the AlgorithmIdentifier:
// test data source
KeyStore ks = KeyStore.getInstance("JKS"); ks.load (new FileInputStream (args[0]), args[1].toCharArray());
byte[] spkienc = ks.getCertificate(args[2]).getPublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spkienc));
// extract PKCS1 part of original SPKI
byte[] pkcs1enc = SubjectPublicKeyInfo.getInstance(spkienc).parsePublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(pkcs1enc));
// rebuild SPKI from the PKCS1
AlgorithmIdentifier algid = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
byte[] spki2enc = new SubjectPublicKeyInfo (algid, pkcs1enc).getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spki2enc));
See my answer to the similar golang x509.MarshalPKIXPublicKey vs x509.MarshalPKCS1PublicKey() and especially the links to:
Converting A public key in SubjectPublicKeyInfo format to RSAPublicKey format java
Generating RSA keys in PKCS#1 format in Java
Problem transmiting a RSA public key, javaME , bouncy castle
If you don't have BouncyCastle, it's a little harder; you need to write a partial ASN.1 parser or generator. Full ASN.1 processing is rather complicated, but for this case you need only a small subset that isn't too bad. (Yeah, that's faint praise.) I may add this later if I have more time.
A much bigger potential issue is that your key is not authenticated. The hard part of public key distribution, much harder than tiny format details, is making sure that only the legitimate key is distributed. If an attacker can substitute their publickey for the correct one, then the victim encrypts the supposedly secret data in a way the attacker can easily read, and all your fancy cryptography code is completely worthless.
This is why most actual systems don't distribute bare publickeys, but instead certificates that allow verifying the key is the correct key. There are a few certificate schemes, but the most widespread by far is X.509 and its Internet profile PKIX -- in fact the RFCs I referenced above, 5280 and 3279, are part of PKIX. SSL-now-TLS uses X.509. Code-signing uses X.509. S/MIME email uses X.509. (PGP/GPG uses a different kind of certificates, not X.509, but still certificates.) And (vanilla) Java directly supports X.509 certificates just as well or even better than it does "X.509" (SPKI) publickeys.

Authenticating signature created on c# by public key on java

I am new to verification and certificates etc ..
I am facing an issue , that I need to sign a message on c# then verify the signature on java , the issue I ma facing that I am unable to load the public key on java on a (PublicKey) object using the Base64 string generated on c# , I used the following code to generate the private and public key on c# side
CspParameters cspParams = new CspParameters { ProviderType = 1 };
cspParams.KeyContainerName = "MyKeyContainer";
RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(1024);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
System.Diagnostics.Debug.WriteLine("pub:" + publicKey);
System.Diagnostics.Debug.WriteLine("pri:" + privateKey);
Console.WriteLine("Key added to container: \n {0}", rsaProvider.ToXmlString(true));
then I used the following code to create a public key on Java side :
X509EncodedKeySpec specc = new X509EncodedKeySpec(org.apache.commons.codec.binary.Base64.decodeBase64("BgIAAACkAABSU0ExAAQAAA......"));
KeyFactory xx = KeyFactory .getInstance("RSA");
PublicKey ssx= xx.generatePublic(specc);
note that I copied the base64 public key string from the c# console .
When I try to run the code on java side the I get the following exception :
java.security.spec.InvalidKeySpecException: Inappropriate key specification: invalid key format
at sun.security.provider.DSAKeyFactory.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(Unknown Source)
I need to find a way to generate private and public key on c# (and generate a .cer file for the public key) to load it on java side , or find a way to load the base64 public key string into a (Publickey) object on java side . please help !
Option 1: Same data, different format.
The easiest way to transmit a public RSA key from .NET is to check that the public exponent value is { 01 00 01 } and then send the modulus value. On the receiver side you accept the modulus and assert the public exponent.
RSAParameters keyParams = rsa.ExportParameters(false);
if (!keyParams.Exponent.SequenceEqual(new byte[] { 0x01, 0x00, 0x01 }))
throw new InvalidOperationException();
Send(keyParams.Modulus);
Then Creating RSA keys from known parameters in Java says you can straightforwardly recover it on the Java side.
Option 2: Same format, different parser.
The next option you have is to keep using the CSP blob, but writing a parser in Java. The data is the result of calling CryptExportKey with PUBLICKEYBLOB, making your data layout as described at https://msdn.microsoft.com/en-us/library/ee442238.aspx and https://msdn.microsoft.com/en-us/library/windows/desktop/aa375601(v=vs.85).aspx#pub_BLOB.
In summary:
A header (which you could decide to skip, or just test it for equal to the fixed value(s) that you expect):
A byte, value 0x06 (PUBLICKEYBLOB)
A byte, value 0x02 (blob v2)
A short, value 0x0000 (reserved)
An integer (stored as little-endian) identifying the key as RSA (0x0000A400 or 0x00002400)
An integer (stored as little-endian) identifying the next segment as an RSA public key (a bit redundant, but technically a different structure now): 0x31415352
After all that comes the relevant data:
The bit-length of the modulus stored as a little-endian unsigned integer. For your 1024-bit example this will be 1024, aka 0x00000400, aka { 00 04 00 00 }(LE).
The public exponent, stored as a little-endian unsigned integer. This is almost always 0x00010001 (aka { 01 00 01 00 }), but since it's there you should respect it.
The next bitLen/8 bytes represent the modulus value. Since this is the public key that should be "the rest of the bytes in this array".
Option 3: Build a certificate
.NET Framework doesn't have this capability built-in (as of the current version, 4.7). You can P/Invoke to CertCreateSelfSignCertificate, but that would involve quite a lot of change (since RSACryptoServiceProvider won't let you get at the key handle, so you'll have to P/Invoke all of that, too).
You could "bit bang" out the DER-encoded certificate yourself. While fun, this is hard to get right, and probably not a viable path.
If you can move to .NET Core, the ability to create certificates has been added to .NET Core 2.0 via the CertificateRequest class. For a very simple certificate from your key:
var certReq = new CertificateRequest(
"CN=SubjectCN",
rsaProvider,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
// add any extensions you want. I'm not adding any because I said "simple".
DateTimeOffset now = DateTimeOffset.UtcNow;
X509Certificate2 cert = certReq.CreateSelfSigned(now, now.AddMinutes(90));
byte[] xfer = cert.RawData;

Generate CMSSignedData with no private key in JAVA

I'm currently trying to generate a CMSSignedData using only a certificate data and a signed data which is generated from an external software (so I don't have access to the private key and the signature generation is transparent for me). What i need is the encoded data to add it to a SMIME signature content.
Searching on web I find out a solution with BouncyCastle that uses the private key and the data to be signed to get a CMSSignedData. But it don't fix to my problem because I don't have access to private key and the signature is already generated.
Is there any way to create a CMS object using only the certificate data, the public key and the previously generated signature?
Any approach or library i can use or try?
CMS (Cryptographic Message Syntax) is specified in RFC 5652. The SignedData type is an ASN.1 structure, which basically consists of the signed data (encapContentInfo), the signature certificate (and chain) and the signature itself (part of signerInfos):
SignedData ::= SEQUENCE {
version CMSVersion,
digestAlgorithms DigestAlgorithmIdentifiers,
encapContentInfo EncapsulatedContentInfo,
certificates [0] IMPLICIT CertificateSet OPTIONAL,
crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
signerInfos SignerInfos }
SignerInfo ::= SEQUENCE {
version CMSVersion,
sid SignerIdentifier,
digestAlgorithm DigestAlgorithmIdentifier,
signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
signatureAlgorithm SignatureAlgorithmIdentifier,
signature SignatureValue,
unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
So, what you have to do now is to create this ASN.1 structure out of the data you have. If you don't already know the algorithms that were used for the signature, you can certainly obtain that information from the operator of the external signing software.
Bouncy Castle is generally a good choice for ASN.1 encoding and in addition you can use the source code of Bouncy Castle that normally creates a CMSSignedData object (CMSSignedDataGenerator) as a template.

Hashing using md5 and sign using private certificate

I have a requirement wherein I have to generate a URL where one of the parameter is signature and signature has to be generated using below requirement in a Java Application:
The other 4 URL parameter values should be hashed (in the order specified below) using MD5 and sign using the private certificate. (The signature will be DER-encoded PKCS #1 block as defined in RSA Laboratory's Public Key Cryptography Standards Note #1.) The resulting digest should be converted to ASCII character set using base64 and then encoded to comply with HTTP URL character set limitations.
Order Parameter
1 [queryparameter1]
2.. [queryparameter …] *
3 Expiration
The final url should look something like
https://<ServerName>:<Port>/imageRet/pod?ID=123456789&build=XHB&date=201102151326&expiration=20110218155523&signature=H767dhghjKJ#23mxi
I have never worked on Cryptography before and hence don't know how to start.
Can somebody help how can this be achived.
This will be the signature code
Signature sig = Signature.getInstance("MD5withRSA");
sig.initSign(privateKey);
sig.update(canonicalize(params));
byte signature[] = sig.sign();
String signatureB64UrlEncoded = Base64.getUrlEncoder().encodeToString(signature);
Where canonicalize(params) means converting the String parameters of the url to byte[] the way your service specified. You have not given details. This step is not trivial at all because equivalent urls may generate different signatures.
For example
q=hello%20world --> Qazz_tVB-guYai5oW0Eef6BbVP ...
q=hello world --> JJWDEPMQDmffcsjR0dP3vnrkFT ...
An example implementation, but surely not valid...
//Convert params[] to byte[] converting each String to byte with default charset and concatenating results
public byte[] canonicalize(String params[] ) throws IOException{
final ByteArrayOutputStream out = new ByteArrayOutputStream();
for (String param: params){
out.write(param.getBytes());
}
return out.toByteArray();
}
Take a look at Amazon AWS to see an example of how canonicalize a URL
If you finally decide to use a more secure algorithm, simply replace MD5withRSA with for example SHA256withRSA

Creating PKCS7 signature from etoken

I am trying to generate a digital signature using an etoken.
I am using BouncyCastle library and the signature is being generated.
The problem is that I want it in pkcs7 format which also contains tags like '-----BEGIN PKCS7-----' and ends with '-----END PKCS7-----'.But the signature that I generate doesn't contain these tags.
I am not sure where I'm going wrong.
Here is my code..
CMSProcessable content = new CMSProcessableByteArray(contentbytes);
CMSSignedData signedData = signGen.generate(content, securityProviderName);
byte[] signeddata = signedData.getEncoded();
BASE64Encoder encoder = new BASE64Encoder();
digitalSignature = encoder.encode(signeddata);
Now I need the data to be in pkcs7 format.Is there anything wrong with the code? Or do I need to add anything to this? Please help.. Thank you.
PKCS#7, usually called CMS (Cryptographic Message Syntax) is a container format that is specified using ASN.1 and encoded using BER/DER (Basic/Distinguished Encoding Rules). BER/DER are methods of performing binary encoding.
What you are talking about is PEM format, which was specified for Privacy Enhanced Mail. It is sometimes also called an ASCII armor as it makes it possible for binary data to be send over text interfaces. It consists of a header and footer that also identify the data, as well as the encoding to base 64 of the DER encoded blob.
What you can do is to implement the header and footer generation yourself, or you can take a look at the PemWriter class within the Bouncy Castle for Java functionality. The specifications of the format can be found here.

Categories

Resources