Introduction
In this article we shall see how Java can access an MS certificate in Personal Certificates.
Background
Back to my Java project, I want to use a MS certificate to validate clients before sending a request to my web server to get back
a runtime jar file.
I try to open "Window-My" to read a Personal Certificate in Java but no luck at all. After searching on Google a while I come to a conclusion to use JNI in
Java to call a VC++ function.
In turn the VC++ function will call a C# module to access the Personal Certificate.
Using the code
The attached code is built using C# 2005, VC++ 2005, and Java 6.0. It has:
- Java Application
- C# class (code module)
- VC++ library (library.dll)
Let's break it down
First we create a Java class named LoadCert.java and compile to LoadCert.class.
public class LoadCert {
public native String encryptedData();
static {
System.loadLibrary("LoadCert");
}
public static void main (String[] args) {
String test = new LoadCert().encryptedData();
System.out.print(test);
}
}
Using javah -jni LoadCert to generate LoadCert.h for VC++ library uses later on.
#include <jni.h>
#ifndef _Included_LoadCert
#define _Included_LoadCert
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jstring JNICALL Java_LoadCert_encryptedData
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Next we create a C# module to access the MS certificate for the VC++ library call.
Let's create a C# class called CSharpCert.cs.
using System;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
public class CSharpCert
{
public CSharpCert() { }
public string encryptedData()
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection cert =
store.Certificates.Find(X509FindType.FindBySubjectName, System.Net.Dns.GetHostName(), false);
if (cert.Count == 0) return string.Empty;
DateTime expired = Convert.ToDateTime(
cert[0].GetExpirationDateString(), new System.Globalization.CultureInfo("en-GB"));
if (expired.CompareTo(DateTime.Now) < 1) return string.Empty;
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert[0].PublicKey.Key;
byte[] bytes = Encoding.UTF8.GetBytes(System.Net.Dns.GetHostName());
return ToStringByte(rsa.Encrypt(bytes, false));
}
private string ToStringByte(byte[] bytes)
{
string ret = "";
for (int i = 0; i < bytes.Length; i++)
ret += bytes[i].ToString("X2");
return ret;
}
}
Then create a C# module named CSharpCert.netmodule.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc /target:module
/reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll CSharpCert.cs
Last part we create VC++ 2005 project
Create two folders named Java and MCPP under the LoadCert project name. Add
LoadCert.h into the Java folder. Create a header for this project and add it into
the MCPP folder.
#include <string.h>
#using <mscorlib.dll>
#using "CSharpCert.netmodule"
using namespace System;
using namespace System::Runtime::InteropServices;
public __gc class LoadCertC
{
public:
CSharpCert __gc *t;
LoadCertC() {
t = new CSharpCert(); }
char* callEncryptedData() {
System::String * str = t->encryptedData();
char* str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(str);
char *copy = new char[strlen(str2)+1];
strcpy(copy,str2);
Marshal::FreeHGlobal(str2);
return copy;
}
};
Add LoadCert.cpp into the source files.
#include <jni.h>
#include "Java\LoadCert.h"
#include "MCPP\LoadCert.h"
JNIEXPORT jstring JNICALL Java_LoadCert_encryptedData(JNIEnv *jn, jobject jobj) {
LoadCertC* t = new LoadCertC();
char* str2 = t->callEncryptedData();
return (jstring) jn->NewStringUTF(str2);
}
The last thing is set up VC++ to point to jni.h and CSharpCert.netmodule. Right click on Project --> Properties, expand C/C++, click on
Additional Include Directories to JDK\Include and JDK\Include\Win32 to point to
jni.h.
Click on resolve #using References to CSharpCert.netmodule.
Click on Command line and add /clr:oldSyntax, expand linker, click on Command line and add /NOENTRY /NODEFAULTLIB:nochkclr.obj /NODEFAULTLIB:MSVCRTD /NODEFAULTLIB:LIBCMTD.
Compile to LoadCert.dll.
Copy CSharpCert.netmodule and the libray DLL to the folder that contains LoadCert.class then run
java LoadCert.
Points of Interest
What we want to know in this article is understand why JNI is necessary and useful.
There are various examples on the internet about JNI. But here we have only touched the very basics of JNI in this article and why we need JNI.
History
- 19 March 2013: First version.