I am trying to create certificate request programmatically (that I would send to server) in iOS am OSX without using openSSL. I have managed to create RSA key pair but am failing at doing the rest. I have the code that does exactly what I need but it is written in Java and am wondering if there is someone to help me translate this to objective c.
Here is the Java code:
test.generateKeys(); // generate RSA key pair
PrivateKey privateKey = test.keys.getPrivate();
PublicKey publicKey = test.keys.getPublic();
SecureRandom sr = new SecureRandom();
String token = "123456"; // dummy token
String uuid = "4670ff33-d9f7-4026-957d-25c00e4dec54"; // dummy uuid
// with Bouncy Castle
ContentSigner signGen = new JcaContentSignerBuilder("SHA1withRSA").setSecureRandom(sr).build(privateKey);
X500Principal subject = new X500Principal("O=" + token + ", CN=" + uuid);
PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, publicKey);
PKCS10CertificationRequest request = builder.build(signGen);
String bc = Hex.encodeHexString(request.getEncoded());
System.out.println(PEMtoString(request));
I am not very good in cryptography and the documentation for the crypto layer apple is developing is pretty poor documented so I am a bit lost here. I have came across a lot of similar samples but there is always something missing.
Thx in advance.
In case someone stumbles on the same problem here is the solution using Apples common crypto layer (no openSSL).
https://github.com/ateska/ios-csr
No need for several weeks of coding just a simple include.
SCCSR *sccsr = [[SCCSR alloc] init];
sccsr.commonName = #"some name";
sccsr.organizationName = #"some organisation";
// // aditional data you can set
// sccsr.countryName = #"";
// sccsr.organizationalUnitName = #"";
// sccsr.subjectDER = nil;
// //
//
NSData *certificateRequest = [sccsr build:pPublicKey privateKey:privateKey];
NSString *str = [certificateRequest base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString *strCertificateRequest = #"-----BEGIN CERTIFICATE REQUEST-----\n";
strCertificateRequest = [strCertificateRequest stringByAppendingString:str];
strCertificateRequest = [strCertificateRequest stringByAppendingString:#"\n-----END CERTIFICATE REQUEST-----\n"];
SCCSR.h -> DOWNLOADED FROM PROVIDED LINK
Related
SO I have to write a Rest client for an integration we are working on with one of our client.
They gave us a private key and told us to create a JWT of the json payload.
Key:
"-----BEGIN RSA PRIVATE KEY-----\nYYYYYYYYYYYYYYYYYYYYYYYYYYYYY edited ..YYYYY==\n---
--END RSA PRIVATE KEY-----\n"
Question:
Is it correct to share private key, is there any java example I can use to create JWT using RSA?
Courtesy - https://wstutorial.com/misc/jwt-java-public-key-rsa.html
public String generateJwtToken(PrivateKey privateKey) {
String token = Jwts.builder().setSubject("adam")
.setExpiration(new Date(2018, 1, 1))
.setIssuer("info#wstutorial.com")
.claim("groups", new String[] { "user", "admin" })
// RS256 with privateKey
.signWith(SignatureAlgorithm.RS256, privateKey).compact();
return token;
}
No, it's not correct to share a private key. Never.
It's called private for a reason.
It is also not correct to create a token on client side.
The only correct thing here is, that you would need the private key to sign the token, but that's not the task of the client. It defeats the whole purpose of a JWT, because you could write into it (e.g. roles, expiration time), whatever you want. It seems the API owner trusts you, and probably it's an API that is not public, but anyway, I would recommend to do it in the right way. They should implement an endpoint to request a token.
Usage examples can usually be found on the websites of the jwt libraries.
RSA keys are public / private key pairs. The private key can be used for signing JWTs, and the public key can be used to verify the signature of those JWTs.
The private key should not be shared with anyone. Doing so would allow random people to access your client API.
I've had good experience using Nimbus to sign JWT with RSA. You can see some examples here: https://connect2id.com/products/nimbus-jose-jwt/examples/jwt-with-rsa-signature.
better late than never
It’s generally a bad idea to share private key. But let's assume you own the private key and you want to use it instead to generate a Keypair each time.
Here an example how to use an existing private key, which is stored in P12 file.
#Test
public void readP12() {
char[] keyStorePassword = "1234567890".toCharArray();
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyStoreData = new FileInputStream(LOCATION + "\\keystore.p12");
keyStore.load(keyStoreData, keyStorePassword);
KeyStore.ProtectionParameter entryPassword = new KeyStore.PasswordProtection(keyStorePassword);
KeyStore.Entry keyEntry = keyStore.getEntry("1", entryPassword);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)
keyStore.getEntry("1", entryPassword);
PrivateKey privateKey = privateKeyEntry.getPrivateKey();
PublicKey publicKey = privateKeyEntry.getCertificate().getPublicKey();
String token = generateJwtToken(privateKey);
System.out.println(token);
printStructure(token, publicKey);
} catch (Exception e) {
// tbd
}
}
You can see how to save private key in p12 file
please help me to solve the following:
I have a code on Java
java code bellow:
StringBuilder fullText;
KeyStore p12 = KeyStore.getInstance("PKCS12");
p12.load(new FileInputStream("FileName.p12"), "1234".toCharArray());
Key key = (Key) p12.getKey("1", "1234".toCharArray());
//signing
Signature signer = Signature.getInstance("SHA1withRSA");
signer.initSign((PrivateKey) key);
signer.update(fullText.toString().getBytes());
b`yte[] digitalSignature = signer.sign();
String base64sign = new String(Base64.getEncoder().encode(digitalSignature));
I tried to reproduce it on .Net platform.
I create a code on .NET 3.5 platform. The code on X++ below:
public static boolean Encrypt(str sXmlDoc)
{
boolean bSuccess = false;
System.Security.Cryptography.X509Certificates.X509Certificate2 p12;
System.Security.Cryptography.AsymmetricAlgorithm key;
str sBase64Cert;
str sBase64Xml;
str sBase64Sign;
str sTmp;
System.Byte[] byteArray;
System.Security.Cryptography.Xml.Signature signer;
System.Exception ex;
str sKeyPublic;
System.Byte[] keyPublic;
System.Int32 myInt32;
int myInt;
System.Byte[] byteTmp, byteTmp2;
System.Text.ASCIIEncoding txtEncoder;
System.Security.Cryptography.Xml.KeyInfo keyInfo;
System.Security.Cryptography.Xml.SignedXml signedXml;
System.Xml.XmlDocument xmlDocument;
System.Xml.XmlElement xmlElement;
System.Security.Cryptography.Xml.SignedInfo signedInfo;
System.Security.Cryptography.Xml.Reference reference;
System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform env;
System.Security.Cryptography.Xml.RSAKeyValue rsaKeyValue;
System.Security.Cryptography.RSA rsaKey;
try
{
p12 = new System.Security.Cryptography.X509Certificates.X509Certificate2("fileName.p12", "pass");
if (p12)
{
//Signature
//TEST
if (p12.get_HasPrivateKey())
{
key = p12.get_PrivateKey();
rsaKey = p12.get_PrivateKey();
xmlDocument = new System.Xml.XmlDocument();
xmlDocument.set_PreserveWhitespace(true); //Allow white spaces
xmlDocument.LoadXml(sXmlDoc);
signedXml = new System.Security.Cryptography.Xml.SignedXml(xmlDocument);
signedXml.set_SigningKey(key);
keyInfo = new System.Security.Cryptography.Xml.KeyInfo();
rsaKeyValue = new System.Security.Cryptography.Xml.RSAKeyValue(rsaKey);
keyInfo.AddClause(rsaKeyValue);
signedXml.set_KeyInfo(keyInfo);
// Create a reference to be signed.
//System.Security.Cryptography.Xml.Reference reference;
reference = new System.Security.Cryptography.Xml.Reference();
reference.set_Uri("");
// Add an enveloped transformation to the reference.
env = new System.Security.Cryptography.Xml.XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
signedXml.set_KeyInfo(keyInfo);
signedXml.ComputeSignature();
xmlElement = signedXml.GetXml();
signer = new System.Security.Cryptography.Xml.Signature();
signer = signedXml.get_Signature();
signedInfo = new System.Security.Cryptography.Xml.SignedInfo();
signedInfo = signer.get_SignedInfo();
byteTmp = signer.get_SignatureValue();
sTmp = System.Convert::ToBase64String(byteTmp);
sBase64Sign = "<signature>"+sTmp+"</signature>";
info(sBase64Sign);
}
}
}
catch (Exception::CLRError)
{
ex = ClrInterop::getLastException();
if (ex != null)
{
ex = ex.get_InnerException();
if (ex != null)
{
error(ex.ToString());
}
}
}
return bSuccess;
}
But the result differs than I got on java. I opened a p12 key. I signed an XML sting and got the signature for this XML, but got the wrong string. What did I do wrong?
The Java code doesn't say anything about XML, so I don't know if you learned anything about porting code, but if you do use XML in C# then it's going to fail.
In short, you need to use the RSA functions directly. Starting with RSA.Create() might make a lot of sense in most languages. However, .NET is rather certificate / key based (you perform operations on the key rather than using the key, for better or worse, and private keys are considered part of the certificate that they belong to). So using a constructor to read PKCS#12 is probably a better starting point.
That's all for this little tutorial. I don't believe for a second that you thought that your code would be a correct port, so start over. Happy programming.
EDIT: Oh, one last hint: SHA1withRSA is RSA using PKCS#1 v1.5 padding for signature generation, using SHA-1 as underlying hash function (which of course means it is SHATTERED and more).
The best way to sign an XML on .NET is by using a Bouncy Castle library. I hope anybody in one day will solve it on .NET framework, without using an external library, but the solution has found via using BounsyCastle.
This code on C# used BouncyCastle.
string item = string.Empty;
Pkcs12Store p12 = new Pkcs12Store();
p12.Load(_p12, _p12_psw.ToCharArray());
string alias = "";
foreach (string al in p12.Aliases)
{
if (p12.IsKeyEntry(al) && p12.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
}
//signature
var data = Encoding.UTF8.GetBytes(xmlDoc);
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
signer.Init(true,p12.GetKey(alias).Key);
signer.BlockUpdate(data, 0, data.Length);
var sign = signer.GenerateSignature();
string base64Sign = Convert.ToBase64String(sign);
item = "<signature>"+base64Sign+"</signature>", base64Sign);
So I am not the Crypto wizard by any means but here is some code I have that works in C# but does not return the same b64 string in Java.
c#
string _Cert = "long b64 string here";
string _Pass = "my password";
string lvreturn = "Test";
byte[] lvCertBytes = Convert.FromBase64String(_Cert);
X509Certificate2 lvCertFromBytes = new X509Certificate2(lvCertBytes, _Pass);
SHA1Managed lvSHA1 = new SHA1Managed();
byte[] lvData = Encoding.Unicode.GetBytes(lvReturn);
byte[] lvHash = lvSHA1.ComputeHash(lvData);
RSACryptoServiceProvider lvCryptoProvider = (RSACryptoServiceProvider)lvCertFromBytes.PrivateKey;
byte[] lvSignedBytes = lvCryptoProvider.SignHash(lvHash, CryptoConfig.MapNameToOID("SHA1"));
string lvToken = Convert.ToBase64String(lvSignedBytes);
Java
String certB64 = "long b64 string here";
char[] Pass = "text password".toCharArray();
String alias = "guid looking ID here";
String plaintext = "Test";
byte[] certbytes = Base64.getDecoder().decode(certB64);
InputStream in = new ByteArrayInputStream(certbytes);
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(in,Pass);
KeyStore.PrivateKeyEntry pvk = (KeyStore.PrivateKeyEntry)keystore.getEntry(alias, new KeyStore.PasswordProtection(Pass));
PrivateKey pkey = (PrivateKey)pvk.getPrivateKey();
Signature rsa = Signature.getInstance("SHA1withRSA");
rsa.initSign(pkey);
rsa.update(plaintext.getBytes());
System.out.println("Hash: " + Base64.getEncoder().encodeToString(rsa.sign()));
I have Cert.pfx file that I want to use to use the privatekey to encrypt a https auth segment. I am just ripping the file to a base64 string and stuffing it into the "_Cert" var in C#. I do the same in Java. I want to sign the plaintext message using the private key of the cert and SHA1. The C# code below works and the https server provides a response. Java however is not spitting out the same base64 encoded string. Thanks for some help!
Update: I found a link to another post that is the same as mine but with a couple small diffs, and I didn't want to necro post on it. I followed it exactly removing the messagedigest piece of my original code. I tried reading directly from the pfx file or using the b64 string directly in the code. I am still not getting the same between Java and C#. At this point it has to be something small I am missing with encoding in Java because the C# is basically identical to mine.
Java Digital Signature different to C#
I'm trying to implement digital signature in php as in java sample code below:
Signature rsaSig = Signature.getInstance("MD5withRSA");
RSAPrivateKey clientPrivateKey = readPrivateKeyFromFile(fileName);
rsaSig.initSign(clientPrivateKey);
String source = msg;
byte temp[] = source.getBytes();
rsaSig.update(temp);
byte sig[] = rsaSig.sign();
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(sig);
My php code :
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // in xml format
$plaintext = '...';
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext);
But looks like some thing is missing. We should get same signature as java code returns.Can anybody guide me in this?
By default phpseclib uses sha1 as the hash. You probably need to do $rsa->setHash('md5').
I am trying to send a Certificate Signing Request from an Android device to a server. The server is working properly with iOS devices and follows a SCEP procedure with OpenSSL.
So here is my problem :
I can send the signed enveloped CSR but the server can't read the enveloped CSR. I have the following error from the server :
pki.rb:26:in initialize: Could not parse the PKCS7: header too long (ArgumentError)
Related ruby server code :
#receive object and put it in object data
[...]
# Verify Input Data
p7sign = OpenSSL::PKCS7.new(data)
store = OpenSSL::X509::Store.new
p7sign.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY)
signers = p7sign.signers
# Encrypted data (LINE 26 :)
p7enc = OpenSSL::PKCS7.new(p7sign.data)
# Certificate Signing Request
csr = p7enc.decrypt(ssl.key, ssl.certificate)
# Signed Certificate
request = OpenSSL::X509::Request.new(csr)
Java Code (Android) :
I am using Bouncy Castle to generate the CSR and Volley (Google) to send it.
Main :
//Generate PEM formated CSR
byte[] pemCsr = getPemFromCsr(generateCSR());
//Envelop it in a PKCS#7 object
byte[] envelopedData = getDerFromCMSEnvelopedData(envelopData(pemCsr));
//Sign it in a PKCS#7 object
byte[] signedData = getDerFromCMSSignedData(signData(envelopedData));
sendCsrRequest(signedData);
CSR :
//Generate the CSR
private static PKCS10CertificationRequest genrateCertificationRequest(){
// Build the CN for the cert we
X500NameBuilder nameBld = new X500NameBuilder(BCStyle.INSTANCE);
nameBld.addRDN(BCStyle.CN, "cn");
nameBld.addRDN(BCStyle.O, "o");
nameBld.addRDN(BCStyle.NAME, "name");
X500Name principal = nameBld.build();
// Generate the certificate signing request (csr = PKCS10)
String sigAlg = "SHA1withRSA";
JcaContentSignerBuilder csb = new JcaContentSignerBuilder(sigAlg);
ContentSigner cs = csb.build(privateKey);
DERPrintableString password = new DERPrintableString("mychallenge");
PKCS10CertificationRequestBuilder crb = new JcaPKCS10CertificationRequestBuilder(principal, publicKey);
crb.addAttribute((ASN1ObjectIdentifier) PKCSObjectIdentifiers.pkcs_9_at_challengePassword, password);
PKCS10CertificationRequest csr = crb.build(cs);
return csr;
}
//Envelop the CSR
private static CMSEnvelopedData envelopData(byte[] pemCsr) {
CMSTypedData msg = new CMSProcessableByteArray(pemCsr);
CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(x509Certificate).setProvider("BC"));
CMSEnvelopedData ed = edGen.generate(msg,new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider("BC").build());
return ed;
}
//Sign the enveloped CSR
private static CMSSignedData signData(byte[] data){
ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(signer, (X509Certificate) x509Certificate));
CMSTypedData cmsdata = new CMSProcessableByteArray(data);
CMSSignedData signedData = generator.generate(cmsdata, true);
return signedData;
}
I have other code ready to paste (the Volley request, the utils converter) but maybe it is enough for the moment.
SCEP is already working with iOS devices, so the server is clean.
Ruby can create the signed PKCS#7 so I guess that my signing step is OK.
But if I send an empty signed PKCS#7, I surprisingly have the same error.
Thanks in advance for any help.
It seems that the ASN1 of the envelop isn't correct for OpenSSL.
In parallel, Google Volley automatically adds "\n" in the response which also raises problems.