使用ColdFusion对数据进行单点登录 [英] Using ColdFusion to sign data for single sign-on

查看:89
本文介绍了使用ColdFusion对数据进行单点登录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对此帖子的长度提前致歉。我对这个问题的了解不足,无法正确确定具体的问题可能是什么!但是无论如何,我们一直在使用我们的成员资格API进行调用,以使用@Leigh 在api中生成有效的签名,,他们一直在努力!再次感谢Leigh,我们的会员非常高兴能够做到这一点!



现在,我想为我们的会员设置单点登录,使他们能够登录进入我们的页面,然后转到其成员个人资料,该个人资料已在该站点上登录。根据 API文档,我需要做的一件事是:



使用您的签名证书对要登录的用户的门户网站用户名进行签名。



我对此一无所知。我已经获得了XML私钥(由他们的.NET应用程序生成),格式为

 < RSAKeyValue>< ; Modulus> {stuff}< / Modulus>< Exponent> {stuff}< / Exponent>< P> ...等等等

我得知我无法直接使用此格式,因此必须将其转换为PEM格式或类似格式。我想我使用OpenSSL做到了,现在有一个格式为 ----- BEGIN PRIVATE KEY ----- {stuff} ----- END PRIVATE KEY -----的文件。



使用Leigh的解决方案确实可以给我签名,但是与API文档中提供的示例不匹配。我认为这是因为它使用了HmacSHA1,而他们注意到标头中的签名使用HMAC SHA1,而用于创建安全令牌的签名使用了公共/专用密钥对和RSA-SHA1。不能使用相同的方法来生成两者。 我尝试更改

 < cfset key = key.init(jKey, HmacSHA1)/> 

 < cfset key = key.init(jKey, RSA-SHA1)/> 

并获得 Algorithm RSA-SHA1不可用。



我尝试过复制和粘贴其他建议的解决方案,但是它们都不起作用。一个示例(摘自 12Robots.com ):

 <!-创建Java密码对象并获得模式---> 
< cfset cipher = createObject('java','javax.crypto.Cipher')。getInstance( RSA)/>

<!---模式告诉密码将是加密还是解密--->
< cfset encMode = cipher.ENCRYPT_MODE />

< cfset encryptionValue = /> <!-返回变量--->

<!---用模式和键--->初始化密码。
< cfset cipher.init(encMode,key)/>

<!---将字符串转换为字节--->
< cfset stringBytes = stringToSign.getBytes( UTF8)/>

<!---执行加密--->
< cfset加密值= cipher.doFinal(stringBytes,0,len(inputString))/>

< cfdump var =#encryptedValue#>

在这种情况下,键是我前面提到的PEM文本,而 stringToSign是用户名。我得到的错误是要么没有指定方法名称和参数类型的方法,要么init方法过载了ColdFusion无法可靠解密的参数类型。ColdFusion找到了0个与提供的参数匹配的方法。如果这是一个Java对象并且您已验证该方法存在,请使用javacast函数来减少歧义。



我尝试过的另一件事是:

 < cfset rsaPrivateKey = toBase64(key, utf-8)> 

< cfset jKey = JavaCast( string,rsaPrivateKey)>
< cfset jMsg = JavaCast( string,stringToSign).getBytes( ASCII)>

< cfset键= createObject( java, java.security.PrivateKey)>
< cfset keySpec = createObject( java, java.security.spec.PKCS8EncodedKeySpec)>

< cfset keyFactory = createObject( java, java.security.KeyFactory)>
< cfset b64dec = createObject( java, sun.misc.BASE64Decoder)>
< cfset sig = createObject( java, java.security.Signature)>

< cfset byteClass = createObject( java, java.lang.Class)>
< cfset byteArray = createObject( java, java.lang.reflect.Array)>

< cfset byteClass = byteClass.forName(JavaCast( string, java.lang.Byte))>
< cfset keyBytes = byteArray.newInstance(byteClass,JavaCast( int, 1024))>
< cfset keyBytes = b64dec.decodeBuffer(jKey)>

< cfset sig = sig.getInstance( SHA1withRSA, SunJSSE)>
< cfset sig.initSign(keyFactory.getInstance( RSA)。generatePrivate(keySpec.init(keyBytes)))>
< cfset sig.update(jMsg)>
< cfset signBytes = sig.sign()>

< cfset finalSig = ToBase64(signBytes)>

< cfdump var =#finalSig#>

这给了我 java.security.InvalidKeyException:无效的密钥格式。顺便说一句,如果我将rsaPrivateKey设置为 key,则会收到另一个错误, java.security.InvalidKeyException:IOException:DerInputStream.getLength():lengthTag = 127,太大。我很高兴收到不同的错误消息;至少发生了什么事! :-)



同样,我不知道这些Java函数在做什么。而且我肯定不明白为什么看似简单的事情变得如此复杂!但是我的怀疑是,我或者错误地存储了私钥PEM,或者错误地从数据库中读取了(或者两者都读取了),这就是导致这些各种解决方案失败的原因。但是我还不足以确定是否是这种情况。



我欢迎任何有帮助的见解或建议!如果有人需要更多信息,我很乐意提供。

解决方案


我非常抱歉,我无法使用此功能直接格式化,并且必须将其转换为PEM格式或类似格式


这样做没什么问题,但从技术上讲不是必需的 。可以从PEM文件或直接从中加载密钥信息。 XML



选项1:从XML加载密钥:



解析示例XML字符串变成一个对象。然后提取模数和 private 指数(即< D> 元素)。使用模数和指数创建 RSAPrivateKeySpec 并加载RSA私钥:

  xmlKeyString =< RSAKeyValue>< Modulus> ... ...< / D>< / RSAKeyValue>; 
xmlDoc = xmlParse(xmlKeyString);
modBytes = binaryDecode(xmlDoc.RSAKeyValue.Modulus.xmlText, base64);
dBytes = binaryDecode(xmlDoc.RSAKeyValue.D.xmlText, base64);
模数= createObject( java, java.math.BigInteger)。init(1,modBytes);
exponent = createObject( java, java.math.BigInteger)。init(1,dBytes);
keySpec = createObject( java, java.security.spec.RSAPrivateKeySpec)。init(modulus,exponent);
keyFactory = createObject( java, java.security.KeyFactory)。getInstance( RSA);
privateKey = keyFactory.generatePrivate(keySpec);

选项2:从PEM文件中加载密钥:



将PEM文件读入变量。删除标题/尾部,即 --- BEGIN / END RSA PRIVATE KEY -----。然后解码base64内容,并使用 KeyFactory

  rawKey = replace(pemContent, ---- BEGIN RSA PRIVATE KEY ---- -,); 
rawKey = replace(rawKey, ----- END RSA PRIVATE KEY -----,);
keyBytes = rawKey.trim()。binaryDecode( base64);
keySpec = createObject( java, java.security.spec.PKCS8EncodedKeySpec);
keyFactory = createObject( java, java.security.KeyFactory)。getInstance( RSA);
privateKey = keyFactory.generatePrivate(keySpec.init(keyBytes));

加载私钥后,可以使用 Signature 对象执行SHA1哈希并使用RSA密钥生成签名:

  stringToSign = test@membersuite.com; 
signer = createObject( java, java.security.Signature)。getInstance( SHA1withRSA);;
signer.initSign(privateKey);
signer.update(stringToSign.getBytes( us-ASCII));
signedBytes = binaryEncode(signer.sign(), base64);

writeDump(signedBytes);

结果(使用示例XML):



'pre> jTDKoH + INi19kGWn7WRk / PZegLv / 9fPUOluaM57x8y1tkuwxOiyX86gxsZ7gU / OsStIT9Q5SVSG5NoaL3B + AxjuLY8b7XBMfTXHv2vidrDkkTTBW0D2LsrkZ3xzmvvPqqfA3tF2HXUYF + zoiTsr3bQdA32CJ + lDNkf + QjV3ZEoc =

NB: 无论选择哪种方法,正确保护私钥都非常重要。完成示例工作后,一定要仔细阅读如何最好地存储和保护私钥。


I apologize in advance for the length of this post. I don't really know enough about this issue to properly identify what the specific problem may actually be! But at any rate, we've been making calls against our membership API to query info about our members (join dates, membership types, etc.) using steps and advice provided by @Leigh here and they have been working great! Thanks again, Leigh, our members are very happy to be able to do this!

Now I want to set up single sign-on for our members, allowing them to log in at our page and then be whisked over to their member profile, already logged in on that site. According to the API documentation, one thing I need to do is:

"Use your Signing Certificate to sign the Portal username of the person to log in."

I am totally stuck on this. I've been provided with an XML private key (generated by their .NET application) in the form

<RSAKeyValue><Modulus>{stuff}</Modulus><Exponent>{stuff}</Exponent><P>... etc etc

I gather that I am unable to work with this format directly and must convert it to PEM format or similar. Using OpenSSL, I think I have done this and now have a file in the format of "-----BEGIN PRIVATE KEY-----{stuff}-----END PRIVATE KEY-----."

Using Leigh's solution does give me a signature, but it does not match the example provided in the API docs. I think this is because it uses HmacSHA1, whereas they note that "the signature in the header uses HMAC SHA1 whereas the signature for creating security tokens uses a public/private keypair and RSA-SHA1. The same method cannot be used to generate both." I tried changing

<cfset key = key.init(jKey,"HmacSHA1") />

to

<cfset key = key.init(jKey,"RSA-SHA1") />

and got "Algorithm RSA-SHA1 not available."

I have tried copying and pasting some other suggested solutions, but none of them work. One example (gotten from 12Robots.com):

<!--- Create a Java Cipher object and get a mode --->
<cfset cipher = createObject('java', 'javax.crypto.Cipher').getInstance("RSA") />

<!--- The mode tells the Cipher whether is will be encrypting or decrypting --->
<cfset encMode = cipher.ENCRYPT_MODE />

<cfset encryptedValue = "" /> <!--- Return variable --->

<!--- Initialize the Cipher with the mode and the key --->
<cfset cipher.init(encMode, key) />

<!--- Convert the string to bytes --->
<cfset stringBytes = stringToSign.getBytes("UTF8") />

<!--- Perform encryption --->
<cfset encryptedValue = cipher.doFinal(stringBytes, 0, len(inputString)) />

<cfdump var="#encryptedValue#">

"Key" in this instance is the PEM text I mentioned earlier and "stringToSign" is the username. The error I get is "Either there are no methods with the specified method name and argument types or the init method is overloaded with argument types that ColdFusion cannot decipher reliably. ColdFusion found 0 methods that match the provided arguments. If this is a Java object and you verified that the method exists, use the javacast function to reduce ambiguity."

Another thing I have tried is:

<cfset rsaPrivateKey = toBase64(key, "utf-8")>

<cfset jKey = JavaCast("string", rsaPrivateKey)>
<cfset jMsg = JavaCast("string", stringToSign).getBytes("ASCII")>

<cfset key = createObject("java", "java.security.PrivateKey")>
<cfset keySpec = createObject("java", "java.security.spec.PKCS8EncodedKeySpec")>

<cfset keyFactory = createObject("java", "java.security.KeyFactory")>
<cfset b64dec = createObject("java", "sun.misc.BASE64Decoder")>
<cfset sig = createObject("java", "java.security.Signature")>

<cfset byteClass = createObject("java", "java.lang.Class")>
<cfset byteArray = createObject("java", "java.lang.reflect.Array")>

<cfset byteClass = byteClass.forName(JavaCast("string", "java.lang.Byte"))>
<cfset keyBytes = byteArray.newInstance(byteClass, JavaCast("int", "1024"))>
<cfset keyBytes = b64dec.decodeBuffer(jKey)>

<cfset sig = sig.getInstance("SHA1withRSA", "SunJSSE")>
<cfset sig.initSign(keyFactory.getInstance("RSA").generatePrivate(keySpec.init(keyBytes)))>
<cfset sig.update(jMsg)>
<cfset signBytes = sig.sign()>

<cfset finalSig = ToBase64(signBytes)>

<cfdump var="#finalSig#">

Which gives me "java.security.InvalidKeyException: invalid key format." BTW if I set rsaPrivateKey to just "key" I get a different error, "java.security.InvalidKeyException: IOException : DerInputStream.getLength(): lengthTag=127, too big." I am pleased to be getting different error messages; at least something is happening! :-)

Again, I do not know what these Java functions are doing. And I surely am not getting why something seemingly straightforward has ended up being so complicated! But my suspicion is, I have either stored the private key PEM incorrectly, or am reading out of the database incorrectly (or both), and that is what is contributing to causing these various solutions to fail. But I don't know enough to say for sure if that is the case.

I would welcome any insight or suggestions that might help me out! If anybody needs more info, I am happy to provide that. Thank you all very much in advance!

解决方案

I gather that I am unable to work with this format directly and must convert it to PEM format or similar

Nothing wrong with doing that, but it is not technically required. The key information can be loaded either from a PEM file OR directly from the XML.

Option 1: Load Key from XML:

Parse the sample XML string into an object. Then extract the modulus and private exponent (ie the <D> element). Use the modulus and exponent to create a RSAPrivateKeySpec and load the RSA private key:

xmlKeyString = "<RSAKeyValue><Modulus>........</D></RSAKeyValue>";
xmlDoc = xmlParse(xmlKeyString);
modBytes = binaryDecode(xmlDoc.RSAKeyValue.Modulus.xmlText, "base64");
dBytes = binaryDecode(xmlDoc.RSAKeyValue.D.xmlText, "base64");
modulus = createObject("java","java.math.BigInteger").init(1, modBytes);
exponent = createObject("java","java.math.BigInteger").init(1, dBytes);
keySpec = createObject("java", "java.security.spec.RSAPrivateKeySpec").init(modulus, exponent);
keyFactory = createObject("java", "java.security.KeyFactory").getInstance("RSA");
privateKey = keyFactory.generatePrivate(keySpec);

Option 2: Load Key from PEM file:

Read the PEM file into a variable. Remove the header/trailer ie, "---BEGIN/END RSA PRIVATE KEY-----". Then decode the base64 contents and load the private key using a KeyFactory:

rawKey = replace( pemContent, "-----BEGIN RSA PRIVATE KEY-----", "" );
rawKey = replace( rawKey, "-----END RSA PRIVATE KEY-----", "" );
keyBytes = rawKey.trim().binaryDecode("base64");
keySpec = createObject("java", "java.security.spec.PKCS8EncodedKeySpec");
keyFactory = createObject("java", "java.security.KeyFactory").getInstance("RSA");
privateKey = keyFactory.generatePrivate(keySpec.init(keyBytes));

After you have loaded the private key, you can use a Signature object to perform the SHA1 hash and generate the signature with the RSA key:

stringToSign = "test@membersuite.com";
signer = createObject("java", "java.security.Signature").getInstance("SHA1withRSA");;
signer.initSign(privateKey);
signer.update( stringToSign.getBytes("us-ASCII"));
signedBytes = binaryEncode(signer.sign(), "base64");

writeDump(signedBytes);

Result (using sample XML):

jTDKoH+INi19kGWn7WRk/PZegLv/9fPUOluaM57x8y1tkuwxOiyX86gxsZ7gU/OsStIT9Q5SVSG5NoaL3B+AxjuLY8b7XBMfTXHv2vidrDkkTTBW0D2LsrkZ3xzmvvPqqfA3tF2HXUYF+zoiTsr3bQdA32CJ+lDNkf+QjV3ZEoc= 

NB: Whichever method you choose, properly securing private keys is VERY important. Once you have the sample working, definitely read up on how to best store and secure private keys.

这篇关于使用ColdFusion对数据进行单点登录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆