1 2 Previous Next 25 Replies Latest reply: Oct 29, 2012 11:55 AM by Jim.Marion-Oracle RSS

    Invoking a Java Method from Peoplecode

    Jeremy Leung
      Hello,

      We are tasked to do a Single Sign On from a third-party to PeopleSoft HRHD using 3DES encryption method based from the email of the user from the third party. We managed to get the java program that was used to encrypt the email and use this to process the decryption on the Peoplesoft side.

      First, are we correct in enabling the SignOn Peoplecode and then creating a Record Peoplecode that process the encrypted string that was passed via the URL?
      Supposing we are correct in this first step, how do we call this java method? We have placed the java class in the PS_HOME/appserv/classes directory and we are somehow stuck on this part of the Peoplecode.

      <Peoplecode>
      &oDecrypt = CreateJavaObject("SSOCrpyto");
      &strDecEmail=&oDecrypt.decryptString(&strEncEmail);

      The decryptString part of the Java class is constructed like this.
      public String decryptString(byte[] message) throws Exception {
      ...code here...
      }


      We are not familiar on how the parameter is passed when the datatype in java is an array of byte[] (If I understood it correctly).

      Thank you.
      Jeremy Leung

      Edited by: 934351 on May 15, 2012 12:36 AM
        • 1. Re: Invoking a Java Method from Peoplecode
          HakanBiroglu
          Hi,

          You need to create a JavaArray.
          Here is a piece of code I used to where a Java Array is passed as return value using Reflection.
          Hope it helps.


          Local JavaObject &JavaClassProgram , &oReflectArray, &oResultArray;
          Local string&stringparam1, &stringparam2, &stringparam3, &stringparam4;
          Local string &JavaArrayClass;

          /* Java result array */
          &JavaArrayClass = "java.lang.reflect.Array";

          /* Class name */
          &class = "com.yourclass";

          /* Create response objects */
          &oReflectArray = GetJavaClass(&JavaArrayClass);
          &oResultArray = GetJavaClass(&JavaArrayClass);

          /* Create Splitter object */
          &JavaClassProgram = CreateJavaObject(&class, &stringparam1, &stringparam2, &stringparam3, &stringparam4);

          /* Execute Splitter object */
          &oResultArray = &JavaClassProgram .execute();

          /* Get result array length */
          &aLen = &oResultArray.length;

          /* Read return value */
          For &i = 1 To &aLen
          /* Java Arrays start at index 0 where PeopleCode Arrays start at index 1 */
          &svalue = &oReflectArray.get(&oResultArray, &i - 1).toString();
          End-For;

          /* Garbagecolletor */
          &JavaClassProgram = Null;
          &oReflectArray = Null;
          &oResultArray = Null;
          • 2. Re: Invoking a Java Method from Peoplecode
            Jim.Marion-Oracle
            Yes, your approach is fine (question #1).

            If I understood the method's Java call spec correctly, the method wants a string converted to a binary array. I have a Base64 encoding blog post, with an example, but there is only 1 line that is relevant:

            Local JavaObject &bytes = CreateJavaObject("java.lang.String", "Hello World").getBytes();
            In my case I was converting "Hello World" to a binary array. In your case, you will want to replace "Hello World" with your encrypted string.


            http://jjmpsj.blogspot.com/2009/05/base64-encoding-for-peoplesoft.html
            • 3. Re: Invoking a Java Method from Peoplecode
              Jeremy Leung
              Hello Hakan and Jim. Thank you for your replies. I, however, was wondering where do the method decryptString come in, in the Peoplecode? Pardon me if I post the entire Java class here. I am not very well-versed with the Java language, but upon reading most Java-related programs regarding Encryption/Decryption they are identical with the Java class pasted below.

              import java.security.MessageDigest;
              import javax.crypto.Cipher;
              import javax.crypto.SecretKey;
              import javax.crypto.spec.IvParameterSpec;
              import javax.crypto.spec.SecretKeySpec;

              public class SAPSSOCrypto {


                   public byte[] encrypt(String message) throws Exception {
                        final MessageDigest md = MessageDigest.getInstance("md5");
                        final byte[] digestOfPassword =
                             md.digest("password".getBytes("utf-8"));
                        
                        final byte[] keyBytes = new byte[24];//Arrays.copyOf(digestOfPassword, 24);
                        
                        System.arraycopy(digestOfPassword,0,keyBytes,0,digestOfPassword.length);
                        
                        for (int j = 0, k = 16; j < 8;) {
                             keyBytes[k++] = keyBytes[j++];
                        }
                        
                   
                        final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
                        final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
                        final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
                        cipher.init(Cipher.ENCRYPT_MODE, key, iv);
                        final byte[] plainTextBytes = message.getBytes("utf-8");
                        final byte[] cipherText = cipher.doFinal(plainTextBytes);
                        // final String encodedCipherText = new sun.misc.BASE64Encoder()
                        // .encode(cipherText);
                        return cipherText;
                   }
                   
                   public String decryptString(byte[] message) throws Exception {
                        final MessageDigest md = MessageDigest.getInstance("md5");
                        final byte[] digestOfPassword =
                             md.digest("password".getBytes("utf-8"));
                        final byte[] keyBytes = new byte[24]; //Arrays.copyOf(digestOfPassword, 24);
                        System.arraycopy(digestOfPassword,0,keyBytes,0,digestOfPassword.length);
                        for (int j = 0, k = 16; j < 8;) {
                             keyBytes[k++] = keyBytes[j++];
                        }
                        final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
                        final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
                        final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
                        decipher.init(Cipher.DECRYPT_MODE, key, iv);
                        // final byte[] encData = new // sun.misc.BASE64Decoder().decodeBuffer(message);
                        final byte[] plainText = decipher.doFinal(message);
                        return new String(plainText, "UTF-8");
                   }
                   
              }
              • 4. Re: Invoking a Java Method from Peoplecode
                HakanBiroglu
                Hi,

                Your initial code is correct

                /* Create JavaClass Object */
                &oDecrypt = CreateJavaObject("SSOCrpyto");

                /* Create input array of bytes object, from example of JIM*/
                Local JavaObject &strEncEmail= CreateJavaObject("java.lang.String", "your string here"").getBytes();

                /* Execute method decryptString */
                &strDecEmail=&oDecrypt.decryptString(&strEncEmail);
                • 5. Re: Invoking a Java Method from Peoplecode
                  Jeremy Leung
                  On the line Local JavaObject &strEncEmail= CreateJavaObject("java.lang.String", "your string here"").getBytes();
                  "your string here" - would be the string that was derived from the url, right? Debugging my code is very tricky, when I issue a SetAuthenticationResult after the call to the Java method, I am still able to login despite setting the flag to False.

                  supposing this is the url being passed to the peoplesoft page from the external site: http://website.peoplesoft.com:port#/psp/CDVL/?DynamicParameter=uid%3diTPGZuBFRXazLeFJodR1HASRYC4SMn9nzJvLmt7A4f4%3d and the string after uid would be the string which represents the encrypted email of the user:
                  I made the following code.

                  Function SSO_TO_PS();
                  Local string &strURLParam = %Request.GetParameter("DynamicParameter");
                  Local string &HTML, &TargetURL, &strDecrypted, &strEmail, &strDecEmail, &strEncEmail, &strUserId;
                  Local number &numAcctLock, &iLen, &i;
                  Local JavaObject &oDecrypt, &oStringArray;

                  /* Truncate Parameter starting from uid* */
                  &iLen = Len(&strURLParam);

                  If Left(&strURLParam, 3) = "uid" Then
                  For &i = 1 To &iLen
                  If &i <= 4 Then
                  &strEncEmail = Substring(&strURLParam, &i, &iLen);
                  Else
                  Break;
                  End-If;
                  End-For
                  End-If;

                  /* Call java class SSOCrypto */
                  &oDecrypt = CreateJavaObject("SSOCrypto");

                  Local JavaObject &oByteStr = CreateJavaObject("java.lang.String", &strEncEmail).getBytes();

                  &strDecEmail = &oDecrypt.decryptString(&oByteStr);

                  /* Display Decrypted String (Testing Purposes Only) */
                  SetAuthenticationResult( False, &USERID, &strDecEmail, False);

                  /* Check if email is associated to any user */
                  SQLExec("SELECT oprid,acctlock,emailid FROM psoprdefn where emailid = :1", &strDecrypted, &strUserId, &numAcctLock, &strDecEmail);

                  /* Login user when ACCTLOCK=0 and EMAILID is present */
                  If All(&strEmail) And
                  &numAcctLock = 0 Then
                  SetAuthenticationResult( True, &strUserId, "", False);
                  End-If;

                  End-Function;

                  Again, many thanks.
                  • 6. Re: Invoking a Java Method from Peoplecode
                    HakanBiroglu
                    >
                    On the line Local JavaObject &strEncEmail= CreateJavaObject("java.lang.String", "your string here"").getBytes();
                    "your string here" - would be the string that was derived from the url, right
                    {quote}
                    Right, the "your string here" will be the string/byte you want to decrypt, in your case the email of a user.

                    Your code look fine.

                    You might be reusing the PeopleSoft cookie, which is still "alive" in your browser.
                    Try closing all browsers and clearing cookies and after this try setting the parameter for SetAuthenticationResult to false, you should not be able to log in.

                    If you want to trace your code, you have a few possibilities:
                    - add SetTracePC(2060); and the start of your code and SetTracePC(0); and the end of your code, this will create a tracefile on the applicationserver.
                    - write log messages to a file
                    - write log messages to a table
                    • 7. Re: Invoking a Java Method from Peoplecode
                      Jeremy Leung
                      I think the problem could be more of Java-related. I have followed the PC trace and I was able to get this error:

                      Caught Exception: Java method decryptString not found for class SSOCrypto. (2,760)

                      I have placed the Java Class SSO Crypto under PS_HOME\class and PS_HOME\appserv\classes. Did I miss anything?
                      Could different versions of Java be the cause of this, for example, the Java used to compile the class (because originally this Java Class is for the third party website) is different from the Java version found on Peoplesoft?

                      Your inputs are highly appreciated.
                      • 8. Re: Invoking a Java Method from Peoplecode
                        HakanBiroglu
                        The path for the class is correct, every time you add or change a java class, you need to bounce the application server, did you do this?

                        Also in your example it says the class name is SAPSSOCrypto whereas you create a java object in PeopleCode SSOCrypto, these names need to match.
                        And if the class resides in a jar file you need to supply the full path in PeopleCode eg com.sun.encryption.SAPSSOCrypto.

                        And keep in mind java is case sensitive, make sure you use the exact same naming convention of the class method in PeopleCode.
                        • 9. Re: Invoking a Java Method from Peoplecode
                          Jeremy Leung
                          Hi Hakan,

                          I was wrong when I pasted the class names here. I tried to omit the SAP part in pasting here but I forgot to do so on some other parts.
                          Yes, the class name in the peoplecode and in java are both the same on our end. We have restarted the app server.
                          Will try to check more today if there is any progress.

                          Thank you for your continous inputs.
                          • 10. Re: Invoking a Java Method from Peoplecode
                            Jeremy Leung
                            You were right. We did another app server bounce and we got rid of that error.
                            However, we are faced with this error:

                            javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher

                            Is this error have something to do with the length of the encrypted string {iTPGZuBFRXazLeFJodR1HASRYC4SMn9nzJvLmt7A4f4=} whose length is 44, which is not a multiple of 8 or with this statement:
                            Local JavaObject &oByteStr = CreateJavaObject("java.lang.String", &strEncEmail).getBytes();
                            • 11. Re: Invoking a Java Method from Peoplecode
                              HakanBiroglu
                              I do not think that I am in a position to provide advice on java errors, since my java knowlegde is at a level of read/ (basic) write. Sorry about that.
                              Looking at around on the net you see this error a lot and indeed it something to do with the input being a multiple of 8 and that during encoding padding is used to "fill up" the 8 byte.
                              • 12. Re: Invoking a Java Method from Peoplecode
                                HakanBiroglu
                                Just want to add, that you maybe can ask the provider of the java class for an example on which methods an in with order to call the methods.
                                • 13. Re: Invoking a Java Method from Peoplecode
                                  Jeremy Leung
                                  Thanks Hakan. You have been very helpful with this.

                                  We are coordinating with the point person who provided the Java class.
                                  • 14. Re: Invoking a Java Method from Peoplecode
                                    Jeremy Leung
                                    Sorry but I still can't get the decryption to work on the Peoplesoft side.
                                    The java class is ok when executed by itself, it encrypts and decrypts properly.
                                    I went as far as hardcoding the encrypting string to the peoplecode.

                                    /* Call java class SSOCrypto */
                                    &oDecrypt = CreateJavaObject("SSOCrypto");

                                    Local JavaObject &oByteStr = CreateJavaObject("java.lang.String", &strEncEmail).getBytes();

                                    try
                                    &strDecEmail = &oDecrypt.decryptString("+encryptedstringhere+");
                                    catch Exception &e1
                                    SetAuthenticationResult( False, &USERID, "Test: " | &e1.ToString(), False);
                                    end-try;

                                    And I get this error: Test: Calling Java SSOCrypto.decryptString: no overload matches. (2,743)
                                    Will placing the java class on subfolders so we could have the syntax CreateJavaObject("com.peoplesoft.util.SSOCrypto"); make a difference?

                                    There is a thread from another forum that also experienced this but the steps on how it was resolved was never mentioned.

                                    Thanks,
                                    Jeremy
                                    1 2 Previous Next