使用ColdFusion为单点登录签署数据

时间:2016-11-22 03:13:21

标签: encryption coldfusion cryptography single-sign-on private-key

我提前为这篇文章的篇幅道歉。我对这个问题不太了解,无法正确识别具体问题究竟是什么!但无论如何,我们已经使用@Leigh here提供的步骤和建议,对我们的会员API进行调用,以查询有关我们会员的信息(加入日期,会员类型等)。工作得很好!再次感谢Leigh,我们的成员非常高兴能够做到这一点!

现在我想为我们的会员设置单点登录,允许他们登录我们的页面,然后将其转到他们已登录该网站的会员资料中。根据{{​​3}},我需要做的一件事是:

"使用您的签名证书签署登录用户的门户网站用户名。"

我完全坚持这个。我已经以

的形式提供了一个XML私钥(由他们的.NET应用程序生成)
<RSAKeyValue><Modulus>{stuff}</Modulus><Exponent>{stuff}</Exponent><P>... etc etc

我认为我无法直接使用此格式,必须将其转换为PEM格式或类似格式。使用OpenSSL,我想我已经完成了这个,现在有一个格式为&#34; ----- BEGIN PRIVATE KEY ----- {stuff} ----- END PRIVATE KEY ---- - &#34;

使用Leigh的解决方案确实给了我一个签名,但它与API文档中提供的示例不符。我认为这是因为它使用HmacSHA1,而他们注意到#34;标头中的签名使用HMAC SHA1,而创建安全令牌的签名使用公钥/私钥对和RSA-SHA1。同样的方法不能用于生成两者。&#34;我试过改变

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

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

并得到&#34;算法RSA-SHA1不可用。&#34;

我已经尝试过复制和粘贴其他一些建议的解决方案,但它们都不起作用。一个例子(来自API documentation):

<!--- 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#">

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

我尝试过的另一件事是:

<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#">

这给了我&#34; java.security.InvalidKeyException:无效的密钥格式。&#34;顺便说一句,如果我将rsaPrivateKey设置为&#34; key&#34;我得到了一个不同的错误,&#34; java.security.InvalidKeyException:IOException:DerInputStream.getLength():lengthTag = 127,太大了。&#34;我很高兴得到不同的错误信息;至少有些事情正在发生! : - )

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

我欢迎任何有助于我的见解或建议!如果有人需要更多信息,我很乐意提供。非常感谢大家!

1 个答案:

答案 0 :(得分:4)

  

我认为我无法直接使用此格式,必须将其转换为PEM格式或类似的

这样做没有错,但技术上并不是必需。密钥信息可以从PEM文件或directly from the XML加载。

选项1:从XML加载密钥:

将示例XML字符串解析为对象。然后提取模数和私有指数(即<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");
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);

选项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):

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

NB: 无论您选择哪种方法,正确保护私钥都非常重要。一旦您有样本工作,请务必阅读如何最好地存储和保护私钥。