some questions of itext signature and verification - pdf

I have some questions about itext signature and verification source code .
first, i want to know what is "digest"
if (externalDigest != null) {
digest = externalDigest;
if (RSAdata != null)
RSAdata = externalRSAdata;
}
In my opinion, "digest" is the encoding of "AuthenticatedAttributeBytes" using private key, "AuthenticatedAttributeBytes" includes messageDigest of PDF origin text, so maybe we can reduce the "messageDigest" using "digest" and public key?
second, i want to know the function of "verifySigAttributes(sigAttr)"
boolean absentEncContDigestCompare = Arrays.equals(msgDigestBytes, digestAttr);
boolean concludingDigestCompare = absentEncContDigestCompare || encContDigestCompare;
boolean sigVerify = verifySigAttributes(sigAttr) || verifySigAttributes(sigAttrDer);
verifyResult = concludingDigestCompare && sigVerify && verifyRSAdata;
In my opinion, sigAttr is also the encode of "AuthenticatedAttributeBytes", and then compare "sigAttr" with "digest", i just want to know the meaning of this behavior.
third, In my opinion, there is a "messageDigest" attribute in "AuthenticatedAttributeBytes", and "digest" is the encode of "AuthenticatedAttributeBytes", I think they are contradictory. For example, I assume that I have a pdf with attached signature, now i need to verify the integrity of PDF, i can get messageDigest from the "updateByteRange" method, and then i need to get the messageDigest from signature, I think there are many options for me to choose, I can get "RsaData" or get messageDigest from "AuthenticatedAttributeBytes" in signerInfos, and i even think I can get messageDigest from "digest" which is the last parts in /Contents. What should I to choose?

Related

"ERROR unspecified algorithm" when signing a PDF with itextsharp 5.5.13.2 and SHA-256

Good morning, I am trying to sign a PDF document using ItextSharp 5.5.13.2 and SHA-256 for the signature process but at the moment of signing I get the error "Specified Algorithm is Invalid". This error does not happen when I use SHA-1 with .NetFramework 4.7.2 to sign the PDF, the next method is the one I use to sign the PDF.
public void SignPDF(string PathSourceDoc, string PathTargetDoc, X509Certificate2 certificate, string pathLogo)
{
using (PdfReader reader = new PdfReader(PathSourceDoc))
using (var writer = new FileStream(PathTargetDoc, FileMode.Create, FileAccess.Write))
using (var stamper = PdfStamper.CreateSignature(reader, writer, '\0', null, true))
{
var signature = stamper.SignatureAppearance;
signature.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
signature.Reason = "My Reason";
signature.Location = "My Location";
signature.SignDate = DateTime.Now;
signature.Acro6Layers = true;
PdfSignature objSignature = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
objSignature.Date = new PdfDate(signature.SignDate);
signature.CryptoDictionary = objSignature;
var bcCert = DotNetUtilities.FromX509Certificate(certificate);
string name = CertificateInfo.GetSubjectFields(bcCert).GetField("CN");
string industry = CertificateInfo.GetSubjectFields(bcCert).GetField("O");
string position = CertificateInfo.GetSubjectFields(bcCert).GetField("T");
DateTime date = DateTime.Now;
signature.Layer2Text = "Digital Signed by: " + name + "\n" +
"Reason: " + "My Reason" + "\n" +
"Date: " + date;
signature.Layer2Font = new Font(iTextSharp.text.Font.FontFamily.TIMES_ROMAN, 8);
Image img = Image.GetInstance(pathLogo);
signature.SignatureGraphic = img;
signature.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
Rectangle rect = new Rectangle(50, 50, 300, 110);
signature.SetVisibleSignature(rect, 1, null);
var standard = CryptoStandard.CADES;
/*I tried this way but I get an error of type Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.'*/
X509Certificate cert = certificate;
X509Certificate2 signatureCert = new X509Certificate2(cert);
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private;// the error is generated in this line (Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.').
IExternalSignature es = new PrivateKeySignature(pk, "SHA-256");
/****************************************************************************************************************************************************************/
X509Certificate2Signature externalSignature = new X509Certificate2Signature(certificate, DigestAlgorithms.SHA1); /*throws an exception on this line of type "System.ArgumentException: 'Unknown encryption algorithm System.Security.Cryptography.RSACng'"
* when using .NET Core 3.1. this always happens, it doesn't matter if I use sha1 or use any other algorithm, it always generates error*/
MakeSignature.SignDetached(signature, externalSignature, new[] { bcCert }, null, null, null, 0, standard);
}
}
The method receives as input parameters the path of the PDF that I need to sign, the path of the pdf that will be created when signing, the certificate and the path of the logo for displaying the image in the signature. Actually I don't know what I'm doing wrong since I've been researching related questions and it should work with SHA-1 and SHA-256 and .NetFramework.
Then I migrated the project to .NetCore 3.1 in order to try to fix the problem but instead I got a new error (It is commented in the code). My goal is to use .NetCore and to allow me to sign a pdf using sha256. I have no problem modifying the SignPdf method in order to make the program work.
Any contribution or information link is appreciated. Thanks for your help.
Psdt: This is the stacktrace is as follows...
this is the stacktrace image of the error when the project was migrated to .Net Core 3.1
Apparently the Microsoft Crypto APIs used by X509Certificate2Signature do not support SHA-256.
You use a X509Certificate2Signature instance as IExternalSignature argument of MakeSignature.SignDetached.
If the PrivateKey of your X509Certificate2 is a RSACryptoServiceProvider (which appears to be the case for your certificate), X509Certificate2Signature.Sign calls
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
return rsa.SignData(message, hashAlgorithm);
RSACryptoServiceProvider.SignData at first apparently does support "SHA256" because before forwarding execution to SignHash it must have calculated the hash value to sign.
RSACryptoServiceProvider.SignHash, though, does not support SHA-256, according to the Microsoft docs on that method:
Remarks
This method creates a digital signature that is verified using the VerifyHash method.
The valid hash algorithms are SHA1 and MD5. The algorithm identifier can be derived from the hash name by using the MapNameToOID method.
Due to collision problems with SHA1 and MD5, Microsoft recommends a security model based on SHA256 or better.
(Viewed 2021-06-24, 11:07)
Thus, you get the "Specified Algorithm is Invalid" error.
Essentially, therefore, one should not base one's IExternalSignature implementation for RSA signing on RSACryptoServiceProvider anymore.
Maybe this is why X509Certificate2Signature was not ported to iText version 7...
An alternative approach might be to check whether the PrivateKey is a RSACng first and in that case use it as a RSACng. This Cryptography Next Generation (CNG) implementation of the RSA algorithm should support better hashing algorithms...

How to read RSA public key from file/string in java/kotlin using BouncyCastle

I am trying to parse a public key in the RSA format from kotlin. I have the key in a string whose contents are like this:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAwAzOKC8d0o0dcv1KqILLehASGgOWyjlAc+adazix6ThhX7QeD3Qw
HzxPpbwsJrVPIEMEIN383awIqnCfIL+AbCQPL13XaUCCS74wC5a84X1r6hcI5XO1
9CPAn+jBKmTr4hPaHWKxuhfO3PcXxGfQdXyqNT96bCYnAYaeSECohFjqDbe+RFcL
1lIns2GtQPMh1/uDyhPA+8HSguREWn+Ac3I2c0wtrzZa6R4nruPgIi6XbWRqAskr
tzbO2Xy6O1DcERH9tg+es/pbrWHRHrwEmLXorj3iGqkJJBUmLeW6B5EjmIgiukdJ
dw7bLTNcwf2n0BLJy/bgnhcw4TMOzUadSQIDAQAB
-----END RSA PUBLIC KEY-----
I found a lot of code examples to do this that involve trimming out the BEGIN and END, using String.replace() but that seemed hacky to me. The BouncyCastle code seems to handle this already including the ability to create a parsers for the different types of "files" it encounters. I'm trying this:
try {
val parser = PEMParser(StringReader(publicKeyString))
val pemObject = parser.readPemObject()
val pemContent = pemObject.content
val key = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(pemContent)
serviceLogger.info("Key object: {}", key)
} catch (e: Exception) {
serviceLogger.error("Could not generate key from keyspec", e)
}
I get as far as a pemContent (an array of bytes) without a problem, but when I try to actually parse that into an RSAPublicKey I get this:
java.lang.IllegalArgumentException: failed to construct sequence from byte[]: DEF length 3 object truncated by 2
What I can't figure out is if I'm calling RSAPublicKey.getInstance() correctly - with the content of the entire PemObject - or is this exception telling me that there's something wrong with my key.
The examples I have been able to find on this are pretty old, and the APIs seem to have evolved to the point that I shouldn't have to be chopping up strings manually.
I doubt this really helps the matter, but I'm generating this file in go from an rsa keypair:
func PublicKeyToPemBytes(prvkey *rsa.PrivateKey) ([]byte, error) {
var pubkey *rsa.PublicKey
pubkey = &prvkey.PublicKey
pubkey_bytes := x509.MarshalPKCS1PublicKey(pubkey)
if pubkey_bytes == nil {
return nil, errors.New("Public key could not be serialized")
}
pubkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubkey_bytes,
},
)
return pubkey_pem, nil
}
The go rsa.PublicKey object contains an N and an E. It gives me a file like listed above, and the base64 decoding results in the exact same length, 270 bytes.
dave_thompson_085 was right. This code actually does work. He asked me if I altered the data, which made me look more closely at it and I realized I was doing a .toUpperCase() on the base64. The result was still perfectly valid base64 with some bits flipped on here and there. I shouldn't have been doing that .toUpperCase() at all, I just didn't see it until he said that. Bouncy does work.

iText 7 PDF signing with GlobalSign DSS

I'm digitally signing a PDF using iText 7 in an asp.net app. I'm using vb but I could figure out a c# answer, too. We're using GlobalSign DSS in order to have the signature not come up as UNKNOWN (AATL).
I created a pfx file from our company SSL certificate and I am able to sign the PDF files using that file but I don't understand how to use the things I get from GlobalSign to accomplish the same thing. This service requires to send a hex encoded SHA256 digest and in return I receive a hex encoded signatureValue. Besides that I also receive a PEM encoded X509 signing certificate, intermediate certificate (PEM encoded X509 certificate that represents the issuer of the signing certificate), Base64 encoded DER representation of the OCSP response for signing_certificate, a Base64 encoded DER representation of timestamp token. It includes the TSA signing certificate in SignedData.CertificatesSet.
I created the code below as best as I could with limited examples from the internet for itext 7 and my even more limited knowledge of PDF signing and certificates in general. I know now that there are some things I make that I don't even use properly such as the tsaClient and the crlList. I didn't quite understand what those were until I had to switch to the DSS. I actually still don't know what the crlList would be although I think it has something to do with a chain, although I don't quite know what a chain is in this context.
Dim outputFileStream As New FileStream(Server.MapPath(strDest), IO.FileMode.Create, IO.FileAccess.ReadWrite)
Dim signer As New PdfSigner(New PdfReader(Server.MapPath(strTemp)), outputFileStream, True)
signer.SetCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS)
Dim appearance As PdfSignatureAppearance = signer.GetSignatureAppearance
Dim rect As New iText.Kernel.Geom.Rectangle(50, 425, 500, 200)
appearance.SetContact("Contact Name - Phone")
appearance.SetSignatureCreator("Company - New Program")
appearance.SetReason(vbCrLf + "eDoc Consent Agreement: " + CType(Session("eDocConsentDateTime"), Date).ToString + vbCrLf _
+ "Terms and Conditions Agreement: " + objInstall.AgreementSigned.ToString + vbCrLf _
+ "Identifier: " + Session("Ident"))
appearance.SetLocation(vbCrLf + strInstallationAddress)
appearance.SetReuseAppearance(False)
appearance.SetPageRect(rect)
appearance.SetPageNumber(2) 'Signature will appear on Page 2
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION)
signer.SetFieldName(signer.GetNewSigFieldName())
Dim cert As New X509Certificate(Server.MapPath("CompSSL.pfx"), "password")
Dim tsaUrl As String = CertificateUtil.GetTSAURL(DotNetUtilities.FromX509Certificate(cert))
tsaClient = New TSAClientBouncyCastle(tsaUrl)
Dim crlList As IList(Of ICrlClient) = New List(Of ICrlClient)()
Dim fsCertFile As New FileStream(Server.MapPath("CompSSL.pfx"), FileMode.Open, FileAccess.Read)
Dim pk12 As New Pkcs12Store(fsCertFile, "password")
fsCertFile.Dispose()
'then Iterate throught certificate entries to find the private key entry
Dim [alias] As String = Nothing
For Each tAlias As String In pk12.Aliases
If pk12.IsKeyEntry(tAlias) Then
[alias] = tAlias
Exit For
End If
Next
Dim pk = pk12.GetKey([alias]).Key
Dim myExternalSignature As IExternalSignature = New PrivateKeySignature(pk, DigestAlgorithms.SHA1)
signer.SignDetached(myExternalSignature, New Org.BouncyCastle.X509.X509Certificate() {pk12.GetCertificate([alias]).Certificate}, Nothing, Nothing, Nothing, 0, PdfSigner.CryptoStandard.CMS)
So I'd like to know how I can use the GlobalSign pieces in order to sign the PDF. It almost seems like they are giving me everything I need to plug into the SignDetached method at the very end but I specifically don't know these things:
I believe that I'm supposed to do the hash to create the digest AFTER I've added the appearance stuff but I don't know what I would pass those bytes to something like Dim hash() As Byte = s.ComputeHash(fileBytes) at that point in my code (right after signer.SetFieldName(signer.GetNewSigFieldName()) I assume)
I don't know how or if I use the two certificates I get from the DSS. I even saw an example that suggests I may put them together to get something.
I don't know if the Signature Value I get is the equivalent of the IExternalSignature. If so, I don't know how to convert it to type "Org.BouncyCastle.Crypto.ICipherParameters" or even if I'm supposed to be trying to.
I plug the timestamp token string I get back from the DSS into the line tsaClient = New TSAClientBouncyCastle(tsaUrl) in the place of "tsaUrl" and there is no error but I don't know if that's right.
I don't know what to do with the OCSP response that I get from the DSS to put it into an IOcspClient object.
If I don't even try to put in the timestamp or the OCSP response in, as apparently I don't in the .pfx version of my code, what will that mean?
What are the data type conversions I'll have to do, if any, to make the DSS responses work?
I've tried to follow the iTextsharp examples, even this one (itextsharp signing pdf with signed hash) which is exactly what I'm trying to do but it doesn't seem to give me the info I need to finish it off. Thanks for your help.
EDIT:
Thanks for the help, dsandoval. I went ahead and hash the unchanged file and that's what I send to GlobalSign:
Dim s As New SHA256Managed
Dim fileBytes() As Byte = IO.File.ReadAllBytes(Server.MapPath(strSrc))
Dim hash() As Byte = s.ComputeHash(fileBytes)
Dim calculatedHash As String = ByteArrayToString(hash)
With this too:
Private Function ByteArrayToString(ByVal arrInput() As Byte) As String
Dim sb As New System.Text.StringBuilder(arrInput.Length * 2)
For i As Integer = 0 To arrInput.Length - 1
sb.Append(arrInput(i).ToString("X2"))
Next
Return sb.ToString().ToUpper
End Function
Then I wrote the two certificates I get from GS to files and then use BouncyCastle to read them into certificates and put them into an array (signing cert first). I pick up where I am writing the second cert file from their 'certificate_path' string here:
Using sw As New StreamWriter(File.Open(strFile2, FileMode.OpenOrCreate))
sw.WriteLine(strTempCert2)
End Using
Dim fileStream2 = System.IO.File.OpenText(strFile2)
Dim pemReader2 = New Org.BouncyCastle.OpenSsl.PemReader(fileStream2)
Dim myCertificateChain() As Org.BouncyCastle.X509.X509Certificate = New Org.BouncyCastle.X509.X509Certificate() {pemReader.ReadObject, pemReader2.ReadObject}
I did the same thing you did with IExternalSignature using this to convert the string from GS:
Private Function StringToByteArray(s As String) As Byte()
' remove any spaces from, e.g. "A0 20 34 34"
s = s.Replace(" "c, "")
' make sure we have an even number of digits
If (s.Length And 1) = 1 Then
Throw New FormatException("Odd string length when even string length is required.")
End If
' calculate the length of the byte array and dim an array to that
Dim nBytes = s.Length \ 2
Dim a(nBytes - 1) As Byte
' pick out every two bytes and convert them from hex representation
For i = 0 To nBytes - 1
a(i) = Convert.ToByte(s.Substring(i * 2, 2), 16)
Next
Return a
End Function
For now I'm just including the certificate chain and the signature and not the timestamp or the OCSP to see if I can just get it signed. With the certificate chain and the implementation of IExternalSignature, I'm able to get SignDetached to run:
signer.SignDetached(myExternalSignature, myCertificateChain, Nothing, Nothing, Nothing, 0, PdfSigner.CryptoStandard.CMS)
The PDF is signed but the signature panel says "Certification by is invalid", "Certified by %s". The details say "There are errors in the formatting or information contained in this signature". When clicking on "Certificate details", nothing happens. I found another person online with this error (Error signing pdf with PHP's openssl_pkcs7_sign) and it was due to the signature container being too big. The zero in my SignDetached parameters is supposed to be "estimate the size" so I changed it to 8192 which was a magic number of 8k I found in some other examples online. That didn't help. Any suggestions at this point?
I'm using c# for my implementation. I'm not too familiar with VB but hopefully this helps. Most of the examples I've seen are using Java and there were some differences in how they implement this, so I had to work around them. The only things I had to implement for this work was the IExternalSignature, IOcspClient, and ITsaClient.
The digest that is sent to GlobalSign are the contents of the document prior to applying to the signature, so the appearance can be left out if applied with signature. The document is not modified until you apply the signature using the PdfSigner.SignDetached, I believe. To compute the digest, you need to use the SHA256 algorithm to hash the message and hexencode it. This is the digest that will be sent to GS.
You need to put both the certificates from GlobalSign to create the certificate chain. First add the signing certificate then the certificate you can retrieve from their 'certificate_path' endpoint.
I'm not sure about the conversion to that type, but with the c# library, the Sign method of IExternalSignature returns a byte[]. With my own implementation of the interface, I simply converted the SignatureValue we received from GS to a byte[].
For the timestamp, we also implemented our own ITsaClient that calls GS for the timestamp. The digest has to be calculated in the GetTimeStampToken method and sent to GS. The response is a hexencoded string that we convert to a byte[].
For the OCSP, we also implemented our own IOcspClient. Since we get the ocsp_response when we fetch the identity, this client simply uses that, rather than calling GS again. I had issues enabling LTV when returning the response by only converting it from a Base64 string to a byte[]. BouncyCastle had trouble reading the response in this format. I had to convert it to the following BouncyCastle objects for this to work properly.
public byte[] GetEncoded( X509Certificate checkCert, X509Certificate issuerCert, string url )
{
var decoded = Base64.Decode(_ocspResponse); //_ocspResponse is same one that we received when fecthing the identity
var response = new OcspResp(decoded);
var basicResponse = (BasicOcspResp)response.GetResponseObject();
return basicResponse.GetEncoded();
}
If no timestamp is attached, the signature will not be timestamped. It will display the time as something along the lines of "the time is time on the signer's computer's system clock" when you open the document in Adobe. If no ocsp response is attatched, the person validating will not have a way to validate that the certificate has/hasn't been revoked at the time of the signature.
I believe the only conversions I had to make were the one mentioned above.
Let me know if you need any more code examples or any other questions.
EDIT
Here is the IExternalSignature implementation. The hash and encryption algorithm has to match what GS will use. They are using SHA256 and RSA.
public class ExternalSignature : IExternalSignature
{
private readonly SigningIdentity _identity;
private readonly GlobalSignClient _client;
public ExternalSignature( GlobalSignClient client, SigningIdentity identity )
{
_client = client;
_identity = identity;
}
public string GetEncryptionAlgorithm() => "RSA";
public string GetHashAlgorithm() => DigestAlgorithms.SHA256;
public byte[] Sign( byte[] message )
{
// Using the System.Security.Cryptography implementation of SHA256
using (var sha = SHA256.Create())
{
// Hash the message, containing the PDF
var bytes = sha.ComputeHash(message);
// HexEconde the hashed message to send to GlobalSign
var digest = Helpers.ByteArrayToString(bytes);
// Send the digest to GlobalSign
var signature = _client.GetSignatureAsync(_identity.Id, digest).Result;
// Return the returned signature as a byte[]
return Helpers.StringToByteArray(signature);
}
}
}
To add the signature, just create an instance and pass it to SignDetached method.
var signature = new ExternalSignature(_client, signingIdentity);
...
...
//After creating the PDF signer, adding appearance, creating the OCSP and TSA clients.
signer.SignDetached(signature, certChain, null, ocsp, tsa, 0, PdfSigner.CryptoStandard.CMS);
I did the same with the OCSP and TSA clients.

Walmart Marketplace API Integration and Authentication

I am working on integrating my application Walmart Marketplace API using Ruby on Rails.
  1. if i try to generate Auth signature for multiple parameters, it does not generate it and returns exceptions. I am using a Jar file to generate Auth signature
    For e.g. -: https://marketplace.walmartapis.com/v3/orders?createdStartDate=2016-09-13&createdEndDate=2016-09-23 
Does anyone generate Auth Signature & timestamp for multiple parameter for Walmart Marketplace API
  2. Does Auth Signature & timestamp need to be generated for each API call for e.g . Pagination call Also?
Does Authentication need to do for each call?
Additional Comments
I know it is a month later and you already have your program figured out but in case you need some help with these parts or anyone else does, I thought I would include the following information I have on the Walmart API.
1.You might want to consider building a method in ruby since it'll be more interactive with the rest of your ruby program, it was kind of difficult but when I was doing it the most difficult part was wrapping the string in the with the SHA256 digest of string to sign. So I threw together a few methods and it works:
pem = make_pem('PRIVATE KEY', encodedKeyBytes)
digest = OpenSSL::Digest::SHA256.new
pkey = OpenSSL::PKey::RSA.new(pem)
signature = pkey.sign(digest, stringToSign)
def make_pem(tag, der)
box tag, Base64.strict_encode64(der).scan(/.{1,64}/)
end
def box(tag, lines)
lines.unshift "-----BEGIN #{tag}-----"
lines.push "-----END #{tag}-----"
lines.join("\n")
end
It's not perfect but ruby doesn't really have the functionality built in so you have to change it around to get it to work. If this still doesn't work feel free to contact me, but I started out using the jar they provide and I promise it is necessary when you are making thousands of different calls a day with different parameters and urls to be able to find the point of failure and if it isn't in ruby its going to be a lot harder to work with and fix.
2/3. You already answered that these need to be included in every call to the API and I don't really have anything else to add here except to not try to find a way around this, like submitting the same time stamp for a batch of calls. Even though it might work if the calls are made within a certain time window, Walmart uses the time stamp to determine which call came in last which is especially important for things like their price API. Again feel free to email me with any questions, I'll try to respond here too but I don't this website that often.
The variable names I am using these variable names just to reference the code provided in the walmart developer guide. I am just going to translate the java code there to ruby to show how I got the values for stringToSign and encodedKeyBytes.
# This is provided to you by walmart
consumerId = "b68d2a72...."
# Also provided by walmart
privateEncodedStr = "MIICeAIBADANBgkqhkiG9w0BAQEFAA......"
# Full path
baseUrl = "https://marketplace.walmartapis.com/v2/feeds"
# HTTP Method Verb
httpMethod = "GET"
timestamp = (Time.now.to_f * 1000).to_i.to_s
stringToSign = consumerId + "\n" + baseUrl + "\n" + httpMethod + "\n" + timestamp + "\n"
encodedKeyBytes = Base64.decode64(privateEncodedStr)
From there you just run it through the original code and then base64 encode the signature and remove white spaces and then you're good to make a request.
In Order to generate multiple parameter pass string as by escaping sting.
Auth Signature & timestamp need to be generated for each API call for e.g . Pagination call Also
if i try to generate Auth signature for multiple parameters, it does not generate it and returns exceptions. I am using a Jar file to generate Auth signature.
USE SHA class instead of jar file =>
It will generate signature for multiple parameters also.
import org.apache.commons.codec.binary.Base64;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
public class SHA256WithRSAAlgo {
private static String consumerId = "b68d2a72...."; // Trimmed for security reason
private static String baseUrl = "https://marketplace.walmartapis.com/v2/feeds";
private static String privateEncodedStr = "MIICeAIBADANBgkqhkiG9w0BAQEFAA......"; //Trimmed for security reasons
public static void main(String[] args) {
String httpMethod = "GET";
String timestamp = String.valueOf(System.currentTimeMillis());
String stringToSign = consumerId + "\n" +
baseUrl + "\n" +
httpMethod + "\n" +
timestamp + "\n";
String signedString = SHA256WithRSAAlgo.signData(stringToSign, privateEncodedStr);
System.out.println("Signed String: " + signedString);
}
public static String signData(String stringToBeSigned, String encodedPrivateKey) {
String signatureString = null;
try {
byte[] encodedKeyBytes = Base64.decodeBase64(encodedPrivateKey);
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(encodedKeyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey myPrivateKey = kf.generatePrivate(privSpec);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(myPrivateKey);
byte[] data = stringToBeSigned.getBytes("UTF-8");
signature.update(data);
byte[] signedBytes = signature.sign();
signatureString = Base64.encodeBase64String(signedBytes);
} catch (Exception e) {
e.printStackTrace();
}
return signatureString;
}
}
Does Auth Signature & timestamps need to be generated for each API call for e.g . Pagination call Also?
YES, for each and every call including pagination , you need to generate new Signature and Timestamps.
Does Authentication need to do for each call?
YES, Authentication need to do for each call.

Basic Sample (Java preferred) for Desire2Learn API

I have visited and read all the Valence, and specifically the REST API, pages. I have one approved key already and a second key that has not yet been approved by D2L, and it's not clear how I request that approval.
The documentation contains a lot of information, but it is difficult to put all the pieces together. For example, in order to make any REST API call, I have to add several parameters to the end of the call. The parameters are documented in one place, but it isn't clear in some cases how to construct them (for example, one of the keys is to contain the url, timestamp, and the type of call being made, but how are they to be concatenated?). Then they have to be signed, and the documentation that tells how to sign the keys is in a completely different page that is not even referenced from the page that tells you that you have to sign the parameters. On top of that, the documentation is not extremely clear about how to do the signing, and offers no further explanation or examples. So to get anywhere, we have to jump around a lot through the documentation, and go through a whole lot of trial and error. It appears that the documentation assumes that the reader has expertise in several areas, which may or may not be true.
Code examples would make a huge difference.
There aren’t a lot of samples yet; we are working to add more, and to make the ones that are present more obvious. As one example, there is a Java Android app that has all the authentication stuff and some basic calls (including the call “whoami” which is a great test call).
The specific auth related files are available as well. From the D2LSigner class, you can see the signing algorithm we use:
Mac hmacSha256 = Mac.getInstance("hmacSHA256");
byte[] keyBytes = key.getBytes("UTF-8");
Key k = new SecretKeySpec(keyBytes, "hmacSHA256");
hmacSha256.init(k);
byte[] dataBytes = data.getBytes("UTF-8");
byte[] sig = hmacSha256.doFinal(dataBytes)
String sigString = base64Url( sig );
From D2LOperationSecurityImpl, you can see how the query string fits together:
//uppercase METHOD, lowercase PATH, timestamp as string
private static /*final*/ String BASE_STRING_TEMPLATE = "{0}&{1}&{2}";
private static /*final*/ String APP_ID_QUERY_NAME = "x_a";
private static /*final*/ String APP_SIG_QUERY_NAME = "x_c";
private static /*final*/ String USER_ID_QUERY_NAME = "x_b";
private static /*final*/ String USER_SIG_QUERY_NAME = "x_d";
private static /*final*/ String TIMESTAMP_QUERY_NAME = "x_t";
...
#Override
public Uri createAuthenticatedUri(String path, String httpMethod) {
long timestamp = System.currentTimeMillis() +
mServerSkewCorrectionMillis.longValue();
Long timestampObjectSeconds = new Long(timestamp/1000);
Object[]formatParms = {httpMethod.toUpperCase(),
path.toLowerCase(),
timestampObjectSeconds.toString()};
String signatureBaseString = MessageFormat.format(BASE_STRING_TEMPLATE,
formatParms);
String appSig = D2LSigner.base64URLSig(mAppKey, signatureBaseString);
String userSig = D2LSigner.base64URLSig(mUserKey, signatureBaseString);
if ((appSig == null) || (userSig == null)) {
return null;
}
String scheme = mEncryptOperations?ENCRYPED_SCHEME:PLAIN_SCHEME;
Uri.Builder b = new Uri.Builder();
b.scheme(scheme);
b.authority(mHostName);
b.path(path);
b.appendQueryParameter(APP_ID_QUERY_NAME, mAppID);
b.appendQueryParameter(APP_SIG_QUERY_NAME, appSig);
b.appendQueryParameter(USER_ID_QUERY_NAME, mUserID);
b.appendQueryParameter(USER_SIG_QUERY_NAME, userSig);
b.appendQueryParameter(TIMESTAMP_QUERY_NAME, timestampObjectSeconds.toString());
Uri securedURI = b.build();
return securedURI;
}
Also, you need to sign the first URL you use for logging in, but only with the application key (because you haven't yet established a user context). It uses a different base string (to protect the URL that is used during auth):
String signature = D2LSigner.base64URLSig(mAppKey, resultURLString);
BasicNameValuePair appID = new BasicNameValuePair(APP_ID_NAME, mAppID);
BasicNameValuePair appSig = new BasicNameValuePair(APP_SIG_NAME, signature);
BasicNameValuePair callbackURL = new BasicNameValuePair(CALLBACK_NAME, resultURLString);